我遇到过需要分别提取宏参数的第一个和第二个字符的情况。输入始终是两个字符长,并且始终是非特殊文本。我知道有 Lua 或 Latex3 或其他任何解决方案,但如果可能的话,我希望得到一个使用纯 LaTeX2 的答案,不使用或尽量少使用软件包。
答案1
您可以使用宏参数,这些参数可以用括号组以外的其他东西来分隔。因此,您可以使用一个特殊的标记来停止扫描。然后,您可以拾取所需的参数部分并丢弃其余部分。为了避免输入太短时出现错误,我们在末尾添加了一些虚拟内容。(在这里,我过去{}{}{}
只是在末尾添加一些空的内容,但您也可以使用未定义的宏,\zzzextractor#1\invalid\invalid\invalid\stophere
如果给定的文本不够长,它会抛出错误。)
\documentclass{article}
\newcommand*{\zzz}[1]{\zzzextractor#1{}{}{}\stophere}
\newcommand*{\zzzextractor}{} % just to make sure we don't overwrite an existing macro
% the real definition comes next
\def\zzzextractor#1#2#3\stophere{%
first: #1,
second: #2}
\begin{document}
\zzz{lo}
\zzz{ipsum}
\zzz{}
\end{document}
这种方法有相当多的局限性。
- 对于 pdfLaTeX,这不适用于非 ASCII 字符。
- 如果您提供宏,
\zzz
您可能会得到意想不到的结果。
如果你的输入保证只包含两个(ASCII)字符,那么下面的方法也可以工作
\documentclass{article}
\makeatletter
\newcommand*{\zzz}[1]{%
first: \@firstoftwo#1,
second: \@secondoftwo#1}
\makeatother
\begin{document}
\zzz{lo}
\zzz{al}
\end{document}
因为\@firstoftwo
被定义为接受两个参数并扩展为第一个参数,并且\@secondoftwo
被定义为接受两个参数并扩展为第二个参数。如果你没有用括号括住传递#1
给这两个宏的参数,它们将只抓取前两个标记作为它们的参数。如果参数#1
只包含两个标记,那就这样吧。
答案2
LaTeX 内核已经具有所需的命令。
\documentclass{article}
\makeatletter
\newcommand{\firstof}[1]{\@car#1\@nil}
\newcommand{\secondof}[1]{\expandafter\@car\@cdr#1\@nil\@nil}
\newcommand{\restof}[1]{\expandafter\@cdr\@cdr#1\@nil\@nil}
\makeatother
\begin{document}
\firstof{ab}
\secondof{ab}
\firstof{abcde}
\secondof{abcde}
X\restof{ab}X
X\restof{abcde}X
\end{document}
宏的定义非常简单:
% latex.ltx, line 846:
\def\@car#1#2\@nil{#1}
\def\@cdr#1#2\@nil{#2}
但是,如果输入的长度少于两个标记,则会产生奇怪的错误。以下是一种更安全的方法expl3
。
\documentclass{article}
\usepackage{expl3} % not needed with LaTeX April 2020 or later
\ExplSyntaxOn
\cs_new:Npn \firstof #1
{
\tl_range:nnn { #1 } { 1 } { 1 } % tokens from 1 to 1
}
\cs_new:Npn \secondof #1
{
\tl_range:nnn { #1 } { 2 } { 2 } % tokens from 2 to 2
}
\cs_new:Npn \restof #1
{
\tl_range:nnn { #1 } { 3 } { -1 } % tokens from 3 to the end
}
\ExplSyntaxOff
\begin{document}
X\firstof{a}X
X\secondof{a}X
X\restof{a}X
X\firstof{ab}X
X\secondof{ab}X
X\restof{ab}X
X\firstof{abcde}X
X\secondof{abcde}X
X\restof{abcde}X
\end{document}
泛化,您可以指定要选择的项目,或者(可选)指定起点和终点。如上面的代码所示,负数表示“从末尾开始计数”。该宏完全可扩展,因此可以放在 中\edef
。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\extract}{O{#2}mm}
{
\tl_range:nnn { #3 } { #1 } { #2 }
}
\ExplSyntaxOff
\begin{document}
X\extract{1}{a}X
X\extract{2}{a}X
X\extract[3]{-1}{a}X
X\extract{1}{ab}X
X\extract{2}{ab}X
X\extract[3]{-1}{ab}X
X\extract{1}{abcde}X
X\extract{2}{abcde}X
X\extract[3]{-1}{abcde}X
X\extract[2]{4}{abcde}X
X\extract{-1}{abcde}X % the last item
\end{document}
答案3
我认为此类事物有数百种变体。
\documentclass{article}
\newcommand\mytest[1]{%
\def\pft##1##2;{\def\pftfirstchar{##1}\def\pftsecondchar{##2}}%
\expandafter\pft#1;%
The first character is \textit{\pftfirstchar} end the second character
\textit{\pftsecondchar}.\par}
\begin{document}
\mytest{si} \mytest{no} \mytest{ja} \mytest{na}
\end{document}
当然还存在更多的故障安全变体和包等等。
答案4
我遇到过需要分别提取宏参数的第一个和第二个字符的情况。输入始终(至少)为两个字符长,并且始终为非特殊文本。
为了完整起见,这里有一个基于 LuaLaTeX 的解决方案。它提供了名为 、 和 的 LaTeX 宏\firstchar
。\secondchar
它们\finalchar
的参数可以是任何 utf8 编码的文本字符串 - 甚至可以是一个或多个 LaTeX 宏,这些宏将在返回结果文本字符串的第一个、第二个或最后一个字符之前进行扩展。
\documentclass{article}
\newcommand\firstchar[1]{\directlua{ tex.sprint( unicode.utf8.sub ("#1",1,1))}}
\newcommand\secondchar[1]{\directlua{tex.sprint( unicode.utf8.sub ("#1",2,2))}}
\newcommand\finalchar[1]{\directlua{ tex.sprint( unicode.utf8.sub ("#1",-1))}}
\begin{document}
\obeylines % just to keep this MWE's code compact
Consider the instruction \verb+\foo{Note}+.
The argument's first character is ``\firstchar{Note}''.
The argument's second character is ``\secondchar{Note}''.
The argument's final character is ``\finalchar{Note}''.
\medskip
Consider the instruction \verb+\foo{öÄüß}+.
The argument's first character is ``\firstchar{öÄüß}''.
The argument's second character is ``\secondchar{öÄüß}''.
The argument's final character is ``\finalchar{öÄüß}''.
\medskip
\def\a{B} \def\b{az}
Consider the instruction \verb+\bar{\a\b}+, where \verb+\def\a{B}+ and \verb+\def\b{az}+.
The expanded argument's first character is ``\firstchar{\a\b}''.
The expanded's argument's second character is ``\secondchar{\a\b}''.
The expanded's argument's final character is ``\finalchar{\a\b}''.
\end{document}