我正在使用一些生成的 LaTeX 代码,其中包含如下代码:
\command{a, b}
我提供了 的定义\command
。它的内部结构与问题实际上并不相关,但最重要的是它将参数传递给另一个命令\backend
(我无法更改):
\newcommand{\command}[1]{\backend{#1}}
现在我的问题是,由于无法处理逗号后的空格,\backend
因此参数失败。a, b
如何删除传递给宏的参数中每个逗号后面的(单个)空格?是否可以使用另一个 TeX 宏来实现这一点?
答案1
这只是另一个有效的解决方案(除了 campa 链接的解决方案):
\documentclass{article}
\usepackage{expl3,xparse}
\NewDocumentCommand{\backend}{m}{#1}
\ExplSyntaxOn
\NewDocumentCommand{\command}{m}{
\tl_set:Nn \l_tmpa_tl {#1}
\tl_replace_all:Nnn \l_tmpa_tl { ~ } { }
\backend{\tl_use:N\l_tmpa_tl}
}
\ExplSyntaxOff
\begin{document}
\command{My space is useless!}
\end{document}
更新:正如评论中所建议的,现在有一个扩展版本(并更正了拼写错误 [忘记的逗号])。
\documentclass{article}
\usepackage{expl3,xparse}
\NewDocumentCommand{\backend}{m}{#1}
\ExplSyntaxOn
\NewDocumentCommand{\command}{m}{
\tl_set:Nn \l_tmpa_tl {#1}
\tl_replace_all:Nnn \l_tmpa_tl { ,~ } { , }
\exp_args:No \backend { \l_tmpa_tl }
}
\ExplSyntaxOff
\def\foo{quack}
\begin{document}
\command{My space, is useless!, \foo}
\end{document}
答案2
这是一个可以处理参数中的宏的版本。为了演示目的,我让其参数\backend
执行\detokenize
,以便我们能够准确地看到它接收到了哪些标记。请注意,在显示的输出中,后面的空格\mymacro
是宏的函数\detokenize
,而不是宏的函数\command
。
至于逻辑,\readlist
宏将逗号分隔的列表读入数组\myarg
。*
调用\readlist*
会丢弃列表中每个项目周围的空白。然后,循环\foreachitem
按顺序遍历每个项目,我使用\g@addto@maco
将项目的标记附加到\tmp
,并在适当的位置添加逗号。一次扩展\tmp
最终传递给\backend
。
这钥匙我要提到的一点是,使用此方法,原始标记直接传递给\backend
。无需进一步扩展参数即可\backend
获得所需的标记。就好像直接将无空格参数列表输入到 一样\backend
。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{listofitems}
\makeatletter
\newcommand\command[1]{%
\setsepchar{,}%
\readlist*\myarg{#1}%
\def\tmp{}%
\foreachitem\x\in\myarg{%
\ifnum\xcnt=1\relax\else\g@addto@macro\tmp{,}\fi%
\expandafter\g@addto@macro\expandafter\tmp\expandafter{\x}%
}%
\expandafter\backend\expandafter{\tmp}%
}
\makeatother
\newcommand\backend[1]{[\detokenize{#1}]}
\begin{document}
\command{a, b}
\command{a, b,c, \mymacro, dfdg}
\end{document}
请注意,使用上述代码,只有逗号周围的空格会被丢弃……而空格之内一个参数被保留,因此\command{a a , bb b, c}
被处理为\backend{a a,bb b,c}
。如果所有参数本质上都不包含空格,那么这对 OP 来说可能不是什么问题。或者它甚至可能是一个理想的功能。
另一方面,如果希望删除所有空格(包括参数内的空格),则以下代码就足够了。在这里,它使用空格而不是逗号作为消化列表的项目分隔符。然后,当它逐项重新整理列表时,分隔符(空格)不是重新整理的列表项的一部分。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{listofitems}
\makeatletter
\newcommand\command[1]{%
\setsepchar{ }%
\readlist\myarg{#1}%
\def\tmp{}%
\foreachitem\x\in\myarg{%
\expandafter\g@addto@macro\expandafter\tmp\expandafter{\x}%
}%
\expandafter\backend\expandafter{\tmp}%
}
\makeatother
\newcommand\backend[1]{[\detokenize{#1}]}
\begin{document}
\command{a, b}
\command{a, b,c, \mymacro, df dg}
\end{document}
答案3
这将删除逗号周围的所有空格:
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\command}{m}
{
\seq_set_split:Nnn \l_jour_command_input_seq {,} { #1 }
\exp_args:Nf \backend { \seq_use:Nn \l_jour_command_input_seq {,} }
}
\seq_new:N \l_jour_command_input_seq
\ExplSyntaxOff
测试文档
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\command}{m}
{
\seq_set_split:Nnn \l_jour_command_input_seq {,} { #1 }
\exp_args:Nf \backend { \seq_use:Nn \l_jour_command_input_seq {,} }
}
\seq_new:N \l_jour_command_input_seq
\ExplSyntaxOff
\newcommand{\backend}[1]{#1} % just for testing
\begin{document}
\command{a, b}
\command{a , b}
\command{a, ,b}
\end{document}
如果您还想删除空项目,请使用 clist:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\command}{m}
{
\clist_set:Nn \l_jour_command_input_clist { #1 }
\exp_args:Nf \backend { \clist_use:Nn \l_jour_command_input_clist {,} }
}
\clist_new:N \l_jour_command_input_clist
\ExplSyntaxOff
\newcommand{\backend}[1]{#1} % just for testing
\begin{document}
\command{a, b}
\command{a , b}
\command{a, ,b}
\end{document}
解释
我们传递\backend
序列或 clist 中的项,并,
在它们之间使用 。两者都会\seq_set_split:Nnn
从项中删除前导和尾随空格,以“优化形式”存储列表。使用\seq_use:Nn
或\clist_use:Nn
,在 f 扩展时,项将使用指定的分隔符“一次性”传递。因此使用\exp_args:Nf
,它会将其后的第一个标记(\backend
在本例中为 )放在一边,并对其后的括号组的内容执行 f 扩展(括号保留在原位)。
\seq_set_split:Nnn
和之间的主要区别\clist_set:Nn
在于前者尊重两个分隔符之间的空项(即零个或一个空格),而后者则删除它们。
答案4
你可以这样做
\documentclass{article}
\usepackage{xinttools}
\newcommand{\command}[1]{%
\expandafter\backend\expandafter
{\romannumeral0\xintlistwithsep,{\xintCSVtoListNoExpand{#1}}}%
}
% for demonstration:
\newcommand\backend[1]{\detokenize{#1}}
\begin{document}
\ttfamily
+++\command{ \foo BAR1 BAR2 , \bar FOO1 FOO2 }+++
\end{document}
这会删除逗号周围、第一个参数开头和最后一个参数结尾的空格。中间的空格保持不变。即使是连续的空格标记,也需要特殊输入,因为 TeX 会将连续的空格合并为一个。
注意:detokenize 总是在每个 CS 后打印一个空格,因此我添加了 BAR1 和 BAR2 以进行额外的演示。
如果您想过滤掉空白项,可以使用此功能。但是,如果\backend
逗号后的空间真的太挤了,那么它可能不喜欢输入中不同数量的项目,这将在消除空或空白项目时产生。
\documentclass{article}
\usepackage{xinttools}
\catcode`_ 11
\newcommand\EmptinessFilter[1]{%
% originally blank items were converted into empty brace pairs
% by \xintCSVtoList, hence will get treated here as the true empty ones
\if\relax\detokenize{#1}\relax\expandafter\xint_gobble_thenstop\fi{#1}%
}
\catcode`_ 8
\newcommand{\command}[1]{%
\expandafter\backend\expandafter
{\romannumeral0\xintlistwithsep{,}
{\xintApplyUnbraced\EmptinessFilter{\xintCSVtoListNoExpand{#1}}}}%
}%
% for demonstration:
\newcommand\backend[1]{\detokenize{#1}}
\begin{document}
\ttfamily
+++\command{ \foo BAR1 BAR2 , \bar FOO1 FOO2 , , \third item (previous was
empty or blank) , \fourth item, ,, , fifth (previous three were empty or
blank) ,
, seventh because empty line gives \par token (the space
after \par or after \backend at the end of this sentence is put
in its output by \detokenize there is no actual space token in
the argument list passed to \backend }+++
\end{document}
这需要额外\EmptinessFilter
的新工具没有可以\xintCSVtoList
消除头部前部空白物品的变体,否则需要专用的变体。