使用连字符或空格拆分输入

使用连字符或空格拆分输入

我需要根据输入是否包含空格或连字符来区别对待。也就是说,以下

\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}

应该成为

\fooA{foobar}
\fooB{foo}\fooC{bar baz}
\fooB{foo}\fooC{bar-baz}
\fooD{foo}\fooE{bar baz}
\fooD{foo}\fooE{bar-baz}

我怎样才能实现这一点,即如何定义\foo

答案1

这里是介绍listofitems解析输入包的好地方。我最近介绍了一个包getargs,几乎立刻就收到了 Christian Tellechea 的电子邮件(http://ctan.org/author/tellechea) 指出了这个软件包的很多缺陷。但更重要的是,我的缺陷软件包启发了他编写一个更好的软件包:listofitems,这个软件包最近才发布(http://ctan.org/pkg/listofitems

这是一个非常强大且编写良好的包,可以执行输入列表的嵌套解析。在此示例中,我们不使用嵌套,而是使用同时解析两个不同字符的巧妙功能:连字符 ( -) 或空格 ( )。

\documentclass{article}
\usepackage{listofitems}
\newcommand\foo[1]{%
  \setsepchar{-|| }%
  \readlist\foobar{#1}%
  \ifnum\foobarlen=1\fooA{#1}\else%
    \ifnum\foobarlen=3%
      \if\foobarsep[1]-%
        \fooD{\foobar[1]}\fooE{\foobar[2]\foobarsep[2]\foobar[3]}%
      \else
        \fooB{\foobar[1]}\fooC{\foobar[2]\foobarsep[2]\foobar[3]}%
      \fi
    \else%
      Invalid argument!
    \fi%
  \fi%
}
\def\fooA#1{\textbackslash fooA\{#1\}\par}
\def\fooB#1{\textbackslash fooB\{#1\}}
\def\fooC#1{\textbackslash fooC\{#1\}\par}
\def\fooD#1{\textbackslash fooD\{#1\}}
\def\fooE#1{\textbackslash fooE\{#1\}\par}
\begin{document}
\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
\end{document}

在此处输入图片描述

listofitems包甚至设置为以纯文本形式工作:

\input listofitems.tex
\def\foo#1{%
  \setsepchar{-|| }%
  \readlist\foobar{#1}%
  \ifnum\foobarlen=1\relax\fooA{#1}\else%
    \ifnum\foobarlen=3%
      \if\foobarsep[1]-%
        \fooD{\foobar[1]}\fooE{\foobar[2]\foobarsep[2]\foobar[3]}%
      \else
        \fooB{\foobar[1]}\fooC{\foobar[2]\foobarsep[2]\foobar[3]}%
      \fi
    \else%
      Invalid argument!
    \fi%
  \fi%
}

\def\fooA#1{FooA[#1]\par}
\def\fooB#1{FooB[#1]}
\def\fooC#1{FooC[#1]\par}
\def\fooD#1{FooD[#1]}
\def\fooE#1{FooE[#1]\par}

\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
\bye

这是一个有趣的示例(使用 LaTeX),展示了嵌套解析。宏*的形式\readlist会从每个列表项中删除前导/尾随空格。虽然这里不是这种情况,但可以调用\ignoreemptyitems\reademptyitems(默认)来决定如何处理列表中的空项。

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{listofitems}
\usepackage{amssymb}
\begin{document}
% LEVEL 1 PARSE CHAR: \\
% LEVEL 2 PARSE CHAR: &
% LEVEL 3 PARSE CHARS: + OR -
\setsepchar{\\/&/+||-}
\readlist*\foo{
y &= mx + b\\
E &= mc^2\\
y &= Ax^2 + \mathbb{B}x - \mathbb{C }
}

\showitems*\foo\par
\showitems*\foo[1]\par
\showitems*\foo[2]\par
\showitems*\foo[3]... $\foo[3,2]$\par
1st term: $\foo[3,2,1]$\par
Following separator: $\foosep[3,2,1]$\par
2nd term: $\foo[3,2,2]$\par
Following separator: $\foosep[3,2,2]$\par
3rd term: $\foo[3,2,3]$
\end{document}

在此处输入图片描述

答案2

中的实现expl3;如果有连字符,我们会检查连字符前的文本是否包含空格,否则我们遵循 BC 路线;如果没有连字符,文本将在空格处拆分并遵循 DE 路线。没有空格和连字符将遵循 A 路线。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\foo}{m}
 {% call an internal function
  \hagen_foo:n { #1 }
 }

\cs_new_protected:Nn \hagen_foo:n
 {% check whether there is a hyphen first
  \seq_set_split:Nnn \l_hagen_foo_seq { - } { #1 }
  \int_compare:nTF { \seq_count:N \l_hagen_foo_seq = 1 }
   {% no hyphen
    \hagen_foo_spaces:n { #1 }
   }
   {% at least a hyphen
    \seq_pop_left:NN \l_hagen_foo_seq \l_hagen_foo_tl
    \tl_if_in:NnTF \l_hagen_foo_tl { ~ }
     {% the text before the hyphen has a space
      \hagen_foo_spaces:n { #1 }
     }
     {% no space
      \hagen_fooD:V \l_hagen_foo_tl
      \hagen_fooE:f { \seq_use:Nn \l_hagen_foo_seq { - } }
     }
   }
 }
\cs_new_protected:Nn \hagen_foo_spaces:n
 {% check whether there is at least a space
  \seq_set_split:Nnn \l_hagen_foo_seq { ~ } { #1 }
  \seq_pop_left:NN \l_hagen_foo_seq \l_hagen_foo_tl
  \seq_if_empty:NTF \l_hagen_foo_seq
   {% no space
    \hagen_fooA:V \l_hagen_foo_tl
   }
   {% at least a space
    \hagen_fooB:V \l_hagen_foo_tl
    \hagen_fooC:f { \seq_use:Nn \l_hagen_foo_seq { ~ } }
   }
 }

% here the auxiliary macros should be defined
\cs_new_protected:Nn \hagen_fooA:n {\texttt{|fooA: #1|}}
\cs_new_protected:Nn \hagen_fooB:n {\texttt{|fooB: #1|}}
\cs_new_protected:Nn \hagen_fooC:n {\texttt{|fooC: #1|}}
\cs_new_protected:Nn \hagen_fooD:n {\texttt{|fooD: #1|}}
\cs_new_protected:Nn \hagen_fooE:n {\texttt{|fooE: #1|}}
% and here the required variants
\cs_generate_variant:Nn \hagen_fooA:n { V }
\cs_generate_variant:Nn \hagen_fooB:n { V }
\cs_generate_variant:Nn \hagen_fooC:n { f }
\cs_generate_variant:Nn \hagen_fooD:n { V }
\cs_generate_variant:Nn \hagen_fooE:n { f }
\ExplSyntaxOff

\begin{document}

\foo{foobar}\par
\foo{foo bar baz}\par
\foo{foo bar-baz}\par
\foo{foo-bar baz}\par
\foo{foo-bar-baz}\par

\end{document}

在此处输入图片描述

答案3

这是一个基于 LuaLaTeX 的解决方案。它设置了一个名为 的 LaTeX 宏\foo,该宏又调用一个名为 的 Lua 函数,该HvE函数检查 的参数\foo并执行字符串的拆分以及将子字符串分配给\fooA\fooB等。

在此处输入图片描述

\documentclass{article}

\usepackage{luacode}
\begin{luacode*}
function HvE ( s )
   if     string.find ( s , "^%a-%s" ) then
      s = string.gsub ( s , "^(%a-)%s(.*)" , "\\fooB{%1}\\fooC{%2}" ) 
   elseif string.find ( s , "^%a-%-" ) then
      s = string.gsub ( s , "^(%a-)%-(.*)" , "\\fooD{%1}\\fooE{%2}" ) 
   else
      s = "\\fooA{"..s.."}" 
   end
   tex.sprint ( s )
end
\end{luacode*}
\newcommand\foo[1]{\directlua{HvE(\luastring{#1})}}

% set up test versions of \fooA, \fooB, etc
\usepackage{xcolor}
\newcommand\fooA[1]{\textit{\textbf{#1}}}
\newcommand\fooB[1]{\textit{#1}}
\newcommand\fooC[1]{\textcolor{red}{\textbf{#1}}}
\newcommand\fooD[1]{\textcolor{purple}{\textsc{#1}}}
\newcommand\fooE[1]{\textsf{#1}}

\begin{document}
\obeylines % just for this example    
\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
\end{document}

相关内容