如何处理“字符扫描”宏中的右括号?

如何处理“字符扫描”宏中的右括号?

问题

我需要定义一个命令来抓取其后的所有数字,而保留其余部分。最终我想到了这个:

第一个解决方案

\RequirePackage{xstring} % for \IfInteger

\newcommand*{\num}{%
    \def\@tmpnum{}%
    \@num%
}

\newcommand*{\@num}[1]{%
    \IfInteger{#1}{%
        \edef\@tmpnum{\@tmpnum#1}\@num%
    }{%
        [I saw the number \@tmpnum!]#1%
    }%
}

\@num宏通过递归调用自身向前扫描。它遇到的每个数字都附加到\@tmpnum。它看到的第一个非数字结束序列并放回原处。

它在简单情况下按预期工作:

\num1234...

输出:

[I saw the number 1234!]...

如何处理\par- 第二种解决方案

我很快就想到\IfInteger无法处理\par,所以......第二种解决方案:

\newcommand{\@num}[1]{%
    \def\@end{%
        [I saw the number \@tmpnum!]#1%
    }%
    \ifdefequal{#1}{\par}\@end{%
        \IfInteger{#1}{%
            \edef\@tmpnum{\@tmpnum#1}\@num%
        }\@end%
    }%
}

如何处理右括号?

这个我搞不懂。如果我想接受这样的事情该怎么办:

\emph{\num42}

无论我怎么尝试,我都会收到一些错误,说右括号太多。我想我是在扫描完后把它放回去的。但这对我没有帮助。

  • 我可以测试右括号吗?还有其他方法可以解决这个问题吗?
  • 还有其他类似的问题在等着我吗?

答案1

要一次扫描一个字符,您需要使用\futurelet(或其 LaTeX 包装器\@ifnextchar),而不是宏参数#1,这样就安全了{}。但对于数字,我只会这样做:

在此处输入图片描述

\documentclass{article}

\begin{document}

\makeatletter
\def\num{\afterassignment\xnum\count@}


\def\xnum{the number was [\the\count@]}

\num44

\emph{\num99}

\end{document}

这里没有什么特别的\count@。它只是一个方便的临时计数寄存器,用 LaTeX 和纯文本声明。您可以改为\newcount\mycount使用\mycount。重点是,当将值分配给计数寄存器时,TeX 会自动解析数字并将值放入寄存器中。\afterassignment然后在分配完成后为您提供一个钩子以进行控制并对该值执行某些操作。

答案2

我不知道“数字”的用途和格式。有许多变体可以被视为数字:

  1. 0由最多 的数字组成的明确数字9

  2. TeX 数字,包括计数寄存器、由原语(、、\inputlineno)给出的内部数字,甚至维度()也可以解释为数字。\value{page}\numexpr\textwidth

此解决方案假设一个明确的数字,因为问题提到“抓取数字”。此外,后面的标记应该保留而不是删除。扫描数字时,TeX 有时会吞噬空格或在某些情况下扩展后面的标记。

测试依赖于\futurelet检查下一个标记。

\documentclass{article}

\makeatletter
\newcommand*{\num}{%
    \let\@tmpnum\@empty
    \futurelet\@tmptok\scan@getnum
}
\newcommand*{\scan@getnum}{%
  \let\scan@next\scan@addnum
  \ifx\@tmptok0%
  \else\ifx\@tmptok1%
  \else\ifx\@tmptok2%
  \else\ifx\@tmptok3%
  \else\ifx\@tmptok4%
  \else\ifx\@tmptok5%
  \else\ifx\@tmptok6%
  \else\ifx\@tmptok7%
  \else\ifx\@tmptok8%
  \else\ifx\@tmptok9%
  \else
    \def\scan@next{%
      [I saw the number \@tmpnum!]%
    }%
  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
  \scan@next
}
\newcommand*{\scan@addnum}[1]{%
  \edef\@tmpnum{\@tmpnum#1}%
  \futurelet\@tmptok\scan@getnum
}
\makeatother

\begin{document}

\num1234...

\num56 78 with spaces

\num9\par

\emph{\num42}

\end{document}

结果

答案3

我刚刚发现了一个功能etextools基于\futurelet,似乎就是为此目的而制定的:

\newcommand*{\num}{%
    \futuredef[0123456789]{\@nn}{[I saw the number \@nn!]}%
}

\futuredef获取它接受的令牌列表,并且当看到其他任何令牌时停止扫描。

相关内容