如何将文本拆分为字符

如何将文本拆分为字符

我想定义一个函数,将输入文本拆分为其组成字母,这样我就可以单独处理每个字母(这个问题的背景是我想将每个字母放入单独的框中,并且想避免一遍又一遍地手动调用我的装箱函数)。

那么我该如何将文本拆分成字母,以便每个字母都可以调用适当的子函数?

答案1

答案很大程度上取决于你所说的“字符”是什么意思,以及你的输入是什么样子的(例如命令)。一种可能性是使用包含大量代码的 soul 来分析文本。例如,你可以通过滥用 \so 命令来获取你的框:

\documentclass{article}
\usepackage{soul}

\makeatletter
\def\SOUL@soeverytoken{%
 \fbox{\the\SOUL@token}}
\makeatother
\begin{document}
\so{abcADBC kdkkk dkdk kdkdk }
\end{document}

答案2

这似乎是 的工作expl3。假设我们想将一串字符拆分成其组成部分,以便稍后处理。因此,我们定义一个宏,它接受两个参数:字符串和用于处理的宏

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\stringprocess}{ m m }
 {
  \egreg_string_process:nn { #1 } { #2 }
 }
\cs_new_protected:Npn \egreg_string_process:nn #1 #2
 {
  \text_map_inline:nn { #2 } { #1 { ##1 } }
 }
\ExplSyntaxOff

\newcommand{\boxchar}[1]{\fbox{\strut#1} } % leave a space after the box

\begin{document}

\stringprocess{\boxchar}{abcdef}

\stringprocess{\boxchar}{ábcdefß}

\end{document}

在此处输入图片描述

不同的玩具问题,但这里“复杂”字符更成问题,所以我坚持使用 ASCII。我们希望输入一个字符串,并得到一个仅包含字符串中的数字(以逗号分隔)的标记列表。我们假设输入是受控的,因此它仅包含字母数字字符。

我们只需要定义一个合适的辅助函数,而不是\boxchar之前使用的简单函数。但是,最好使用序列而不是标记列表,所以我将从头开始重新设计解决方案。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \l__egreg_input_string_seq
\seq_new:N \l__egreg_output_string_seq

\cs_new_protected:Npn \egreg_string_process:nnn #1 #2 #3
% #1 = preprocess macro
% #2 = postprocess macro
% #3 = string
 {
  \seq_clear:N \l__egreg_output_string_seq
  \seq_set_split:Nnn \l__egreg_input_string_seq { } { #3 }
  \seq_map_inline:Nn \l__egreg_input_string_seq
   { #1 { ##1 } }
  #2
 }

\NewDocumentCommand{\boxchars}{ m }
 {
  \egreg_boxchars:n { #1 }
 }

\cs_new_protected:Npn \egreg_boxchars:n #1
 {
  \egreg_string_process:nnn
   { \egreg_fbox_strut:n }
   { \seq_use:Nnnn \l__egreg_output_string_seq { ~ } { ~ } { ~ } }
   { #1 }
 }
\cs_new_protected:Npn \egreg_fbox_strut:n #1
 {
  \seq_put_right:Nn \l__egreg_output_string_seq { \fbox { \strut #1 } }
 }
\ExplSyntaxOff

\begin{document}
\boxchars{abcdef}
\end{document}

这会产生与以前相同的结果,但\unskip不是必需的。

  1. 作为第四个参数传递的字符串\egreg_string_process:nnnn被拆分成各个组件;第三个参数是组件的分隔符,也可以为空;清除辅助“输出”序列以供预处理或后处理宏后续使用;

  2. 序列的每个元素都被传递给“预处理宏”,它应该是一个参数函数;

  3. 应用“后处理”宏。

在示例中,预处理宏存储\fbox,后处理宏仅生成输出序列中的项目,以空格分隔。

那么玩具问题呢?预处理宏应该测试该项目是否为数字,在本例中,将其添加到输出序列中。让我们在之前添加此代码\ExplSyntaxOff

\cs_new_protected:Npn \egreg_store_digit:n #1
 {
  \bool_if:nT
   {
    \int_compare_p:n { `#1 >= `0 } && \int_compare_p:n { `#1 <= `9 }
   }
   {
    \seq_put_right:Nn \l__egreg_output_string_seq { #1 }
   }
 }
\cs_new:Npn \egreg_print_list_commas:n #1
 {
  \seq_use:Nnnn \l__egreg_output_string_seq { , } { , } { , }
 }

\NewDocumentCommand{\extractdigits}{ m }
 {
  \egreg_string_process:nnnn
   { \egreg_store_digit:n }
   { \egreg_print_list_commas:n }
   { }
   { #2 }
 }

并尝试

\begin{document}
\extractdigits{a1b2c3}
\end{document}

要得到

1,2,3

玩具问题的完整代码:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \l__egreg_input_string_seq
\seq_new:N \l__egreg_output_string_seq

\cs_new_protected:Npn \egreg_string_process:nnnn #1 #2 #3 #4
% #1 = preprocess macro
% #2 = postprocess macro
% #3 = separator
% #4 = string
 {
  \seq_clear:N \l__egreg_output_string_seq
  \seq_set_split:Nnn \l__egreg_input_string_seq { #3 } { #4 }
  \seq_map_inline:Nn \l__egreg_input_string_seq
   { #1 { ##1 } }
  #2
 }

\cs_new_protected:Npn \egreg_store_digit:n #1
 {
  \bool_if:nT
   {
    \int_compare_p:n { `#1 >= `0 } && \int_compare_p:n { `#1 <= `9 }
   }
   {
    \seq_put_right:Nn \l__egreg_output_string_seq { #1 }
   }
 }
\cs_new:Npn \egreg_print_list_commas:n #1
 {
  \seq_use:Nnnn \l__egreg_output_string_seq { , } { , } { , }
 }

\NewDocumentCommand{\extractdigits}{ O{} m }
 {
  \egreg_string_process:nnnn
   { \egreg_store_digit:n }
   { \egreg_print_list_commas:n }
   { #1 }
   { #2 }
 }

\ExplSyntaxOff

\begin{document}
\extractdigits{a1b2c3d}
\end{document}

答案3

xstring包提供了用于拆分字符串、提取字符串字符和替换字符串的宏。这可以与forloopifthen如果需要的话。

答案4

至少对于简单情况来说,编写这样的宏相当容易。这是我刚刚编写的一个宏。

\newcommand*\foreachletter[2]{%
        \begingroup
        \let\templettercommand#1%
        \let\tempspacecommand#2%
        \catcode`\ 12
        \foreachlettergo
}
\def\foreachlettergo#1{%
        \testletter#1\relax
        \endgroup
}
\def\testletter#1#2\relax{%
        \if#1\otherspace
                \tempspacecommand
        \else
                \templettercommand{#1}%
        \fi
        \ifx\relax#2\relax
                \let\next\relax
        \else
                \let\next\testletter
        \fi
        \next#2\relax
}
\catcode`\ 12
\def\otherspace{ }%
\catcode`\ 10

\foreachletter\fbox\textvisiblespace{Here is some text!}

\foreachletter宏有三个参数,第一个参数是只接受单个参数(如\fbox)的宏,它将针对第三个参数中的每个非空格标记进行扩展。第二个参数是将针对每个空格标记进行扩展的宏。第三个参数应仅包含字母、“其他”(例如标点符号)和空格。

这并不在所有情况下都有效,但我认为它比使用更具可读性,\futurelet这可能是更好的方法。

相关内容