我想要一个宏,将像“1.2.3”这样的字符串分成两部分:直到最后一个点的部分(称为语境) 和最后一个数字。这似乎是辅助宏和的一个非常基本的应用程序\expandafter
,但我无法让它终止。
这是我的 MWE,其中包含一些调试宏:
\documentclass{article}
\makeatletter
\newcommand{\parse}[1]{%
Parsing #1:\par%
\let\my@context\@empty%
\let\my@number\@empty%
\expandafter\@parse#1.\@nil
Context: \my@context\par
Number: \my@number
}
\def\@parse#1.#2\@nil{%
\if#2\@empty%
\edef\my@number{#1}%
\show\my@number%
\else%
\edef\my@context{\if\my@context\@empty\else\my@context.\fi#1}%
\show\my@context%
\expandafter\@parse#2\@nil%
\fi
}
\makeatother
\begin{document}
\tracingmacros=1
\parse{1.2.3}
\parse{4.5.6.7}
\parse{1.2}
\tracingmacros=0
\end{document}
当我编译它时,我得到了应该是终止的情况,但它没有成功。这是日志文件:
\parse #1->Parsing #1:\par \let \my@context \@empty \let \my@number \@empty \ex
pandafter \@parse #1.\@nil Context: \my@context \par Number: \my@number
#1<-1.2.3
\@parse #1.#2\@nil ->\if #2\@empty \edef \my@number {#1}\show \my@number \else
\edef \my@context {\if \my@context \@empty \else \my@context .\fi #1}\show \my@
context \expandafter \@parse #2\@nil \fi
#1<-1
#2<-2.3.
\my@context ->
\@empty ->
> \my@context=macro:
->1.
\@parse ... \my@context .\fi #1}\show \my@context
\expandafter \@parse #2\@n...
l.30 \parse{1.2.3}
?
\@parse #1.#2\@nil ->\if #2\@empty \edef \my@number {#1}\show \my@number \else
\edef \my@context {\if \my@context \@empty \else \my@context .\fi #1}\show \my@
context \expandafter \@parse #2\@nil \fi
#1<-2
#2<-3.
\my@context ->1
\@empty ->
\my@context ->1
> \my@context=macro:
->1.2.
\@parse ... \my@context .\fi #1}\show \my@context
\expandafter \@parse #2\@n...
l.30 \parse{1.2.3}
?
\@parse #1.#2\@nil ->\if #2\@empty \edef \my@number {#1}\show \my@number \else
\edef \my@context {\if \my@context \@empty \else \my@context .\fi #1}\show \my@
context \expandafter \@parse #2\@nil \fi
#1<-3
#2<-
\@empty ->
\my@number ->
\my@context ->1.2
\my@context ->1.2
> \my@context=macro:
->1.2.3.
\@parse ... \my@context .\fi #1}\show \my@context
\expandafter \@parse #2\@n...
l.30 \parse{1.2.3}
?
! Undefined control sequence.
\@parse ...y@context \expandafter \@parse #2\@nil
\fi
l.30 \parse{1.2.3}
?
在最后一次成功的扩展中\@parse
,#1
是“3”,#2
是“”。似乎\@empty
也扩展为“”。那么为什么不成功呢\if#2\empty
?
我欢迎奇特的xparse
解决l3regex
方案,但也希望有一个基本的。有些简单的东西我不明白。
答案1
代码存在问题原样是以下行:
\if#2\@empty
假设第二个参数为空。当 TeX 替换参数文本时,它会将第二个参数插入到 的位置#2
,因此#2
它不是扩展为参数文本的宏。它是参数文本。因此流变成\if\empty
,并且 TeX 尝试找到下一个要比较\empty
的东西(在这种情况\edef
下,测试失败)。
解决此问题的一种方法是将内容存储#2
在宏中,然后测试该宏,只需对代码进行最少的更改即可。由于我们现在正在测试宏,\ifx
所以这是正确的选择。所以现在您需要:
\def\arg@two{#2}%
\ifx\arg@two\@empty
这将按照您的需要测试它是否#2
为空。
在全:
\documentclass{article}
\makeatletter
\newcommand{\parse}[1]{%
Parsing #1:\par%
\let\my@context\@empty%
\let\my@number\@empty%
\expandafter\@parse#1.\@nil
Context: \my@context\par
Number: \my@number
}
\def\@parse#1.#2\@nil{%
\def\arg@two{#2}%
\ifx\arg@two\@empty%
\edef\my@number{#1}%
\show\my@number%
\else%
\edef\my@context{\if\my@context\@empty\else\my@context.\fi#1}%
\show\my@context%
\expandafter\@parse#2\@nil%
\fi
}
\makeatother
\begin{document}
\tracingmacros=1
\parse{1.2.3}
\parse{4.5.6.7}
\parse{1.2}
\tracingmacros=0
\end{document}
生成:
答案2
听了巫师们的讲解后,我们来看一下一个实用的解决方案:
\documentclass{article}
\usepackage{xstring}
\newcommand{\parse}[1]{
Parsing #1:\par%
\StrCount{#1}{.}[\dotnum]
Context : \StrBefore[\dotnum]{#1}{.}\par
Number: \StrBehind[\dotnum]{#1}{.}
}
\begin{document}
\parse{1.2.3}
\parse{4.5.6.7}
\parse{1.2}
\end{document}
答案3
一个简单的 TeX 解决方案:
\def\parse#1{%
\def\a{}%
\def\b{}%
\xparse#1.\relax.}
\def\xparse#1.#2.{%
\ifx\relax#2
\def\b{#1}%
\expandafter\parsestop
\else
\edef\a{\a\ifx\a\empty\else.\fi#1}%
\expandafter\xparse
\fi
#2.}
\def\parsestop\relax.{%
\immediate\write20{[a=\a] [b=\b]}}
\parse{1}
\parse{1.2.3}
\parse{4.5.6.7}
\parse{1.2}
\bye
产生
[a=] [b=1]
[a=1.2] [b=3]
[a=4.5.6] [b=7]
[a=1] [b=2]
)
No pages of output.
答案4
这是 David Carlisle 解决方案的另一种变体。在此解决方案中,命令\parsestop
不是必需的,并且\a
是作用域内的。此外,我们还要留意空/空标记。
\documentclass{article}
\def\parse#1{\begingroup\def\a{}\xparse#1.\relax.}
\def\xparse#1.#2.{%
\ifx\relax#2%
\edef\xparse\relax.{%
\endgroup\ifx\a\empty\else[a=\a]\space\fi\ifx\\#1\\\else[b=#1]\fi
}%
\else
\ifx\\#1\\\else
\edef\a{\a\ifx\a\empty\else.\fi#1}%
\fi
\fi
\xparse#2.%
}
\begin{document}
\parse{}
\parse{1}
\parse{1.2.3}
\parse{1..2..3}
\parse{4.5.6.7}
\parse{1.2}
\end{document}