我想定义一个函数,将输入文本拆分为其组成字母,这样我就可以单独处理每个字母(这个问题的背景是我想将每个字母放入单独的框中,并且想避免一遍又一遍地手动调用我的装箱函数)。
那么我该如何将文本拆分成字母,以便每个字母都可以调用适当的子函数?
答案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
不是必需的。
作为第四个参数传递的字符串
\egreg_string_process:nnnn
被拆分成各个组件;第三个参数是组件的分隔符,也可以为空;清除辅助“输出”序列以供预处理或后处理宏后续使用;序列的每个元素都被传递给“预处理宏”,它应该是一个参数函数;
应用“后处理”宏。
在示例中,预处理宏存储\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
答案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
这可能是更好的方法。