我喜欢可以(滥用)使用来非常有效地解析字符串的方式\def
(另一种选择是循环遍历 LaTeX3 样式的所有字符或使用 LaTeX3 正则表达式,但它更慢,而且通常更难编写)。
如果我们考虑这个 MWE:
\documentclass[]{article}
\begin{document}
\def\firstTry#1MYSEP#2ENDOFLINE{
The first try succeeded, I want to extract ``#2''.
}
\def\secondTry#1ENDOFLINE{
The second try succeeded, I want to extract ``#1''.
}
\firstTry foo MYSEPHere is the line I want to extractENDOFLINE
%\firstTry This line has no separator, yet I want to run secondTry on itENDOFLINE
\end{document}
您可以看到第一行将被正确解析\firstTry
。是的,第二行注释没有分隔符,因此会产生错误。但是,我还想能够对其进行一些操作(\secondTry
在其上运行),而不输出错误消息。在 TeX 中是否可以“尝试”解析,如果解析失败,则使用另一种机制?
答案1
答案2
怎么样:
\documentclass{article}
\newcommand\UDfirstoftwo[2]{#1}
\newcommand\UDsecondoftwo[2]{#2}
\csname @ifdefinable\endcsname{\firstTry}{%
\long\def\firstTry#1EOL{\firstTryA#1SEPSEPEOL{#1}}%
}%
\csname @ifdefinable\endcsname{\firstTryA}{%
\long\def\firstTryA#1SEP#2SEP#3EOL#4{%
% #3 either is empty or is the delimiter "SEP".
\ifx\relax#3\relax\expandafter\UDfirstoftwo\else\expandafter\UDsecondoftwo\fi
{\secondTry#4EOL}%
{The first try succeeded, I want to extract ``\detokenize{#2}''.}%
}%
}%
\csname @ifdefinable\endcsname{\secondTry}{%
\long\def\secondTry#1EOL{%
The second try succeeded, I want to extract ``\detokenize{#1}''.%
}%
}%
\begin{document}
\ttfamily
\firstTry foo SEPHere is the line I want to extractEOL
\firstTry Here is the line I want to extractEOL
\noindent \fbox{!!! There is brace stripping:}
\firstTry{ foo }SEP{Here is the line I want to extract}EOL
\firstTry{Here is the line I want to extract}EOL
\end{document}
当将事物作为分隔参数进行处理时,可能会丢失围绕整个参数的花括号对。
因此,如果需要保留括号,事情可能会变得更加棘手;也许我们不应该将事情视为“后备”,而应该将其视为“解析和分叉”:
\documentclass{article}
\newcommand\UDfirstoftwo[2]{#1}%
\newcommand\UDsecondoftwo[2]{#2}%
\newcommand\UDgobble[1]{}%
\csname @ifdefinable\endcsname{\UDgobbleToSEP}{%
\long\def\UDgobbleToSEP#1SEP{}%
}%
\csname @ifdefinable\endcsname{\UDstopromannumeral}{%
\chardef\UDstopromannumeral=`\^^00 %
}%
\newcommand\UDCheckWhetherNull[1]{%
\romannumeral\expandafter\UDsecondoftwo\string{\expandafter
\UDsecondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UDsecondoftwo\string}\expandafter\UDfirstoftwo\expandafter{\expandafter
\UDsecondoftwo\string}\expandafter\UDstopromannumeral\UDsecondoftwo}{%
\expandafter\UDstopromannumeral\UDfirstoftwo}%
}%
\newcommand\Try{\TryPreventBraceRemoval{}}%
\csname @ifdefinable\endcsname{\TryPreventBraceRemoval}{%
\long\def\TryPreventBraceRemoval#1EOL{%
\expandafter\UDCheckWhetherNull\expandafter{\UDgobbleToSEP#1SEP}{%
% There is no SEP that is not nested in braces in #1:
\expandafter\DoMyThingWithArgIfNoSEP\expandafter{\UDgobble#1}%
}{%
% There is SEP that is not nested in braces in #1:
\expandafter\DoMyThingWithArgIfSEP\expandafter{\UDgobbleToSEP#1}%
}%
}%
}%
\newcommand\DoMyThingWithArgIfNoSEP[1]{%
The second try succeeded, I want to extract ``\detokenize{#1}''.%
}%
\newcommand\DoMyThingWithArgIfSEP[1]{%
The first try succeeded, I want to extract ``\detokenize{#1}''.%
}%
\begin{document}
\ttfamily
\ttfamily
\Try foo SEPHere is the line I want to extractEOL
\Try Here is the line I want to extractEOL
\noindent \fbox{!!! There is no brace stripping:}
\Try{ foo }SEP{Here is the line I want to extract}EOL
\Try{Here is the line I want to extract}EOL
\end{document}
答案3
可能比较慢,但绝对不会更难写。
\documentclass{article}
\ExplSyntaxOn
\NewDocumentCommand{\firstTry}{}
{
\peek_regex_replace_once:nnF
{ ([^\c{par}]*?) MYSEP ([^\c{par}]*?) ENDOFLINE }% search
{ The\ first\ try\ succeeded,\ I\ want\ to\ extract\ \2 }% replace
{ NO~MATCH }
}
\ExplSyntaxOff
\begin{document}
\firstTry foo MYSEPHere is the line I want to extractENDOFLINE
\firstTry what?
\end{document}
由于搜索的是(几乎)任意标记,我选择取消资格\par
(这也会匹配空行)。