检查参数是否为浮点数,且不中断参数中的控制序列

检查参数是否为浮点数,且不中断参数中的控制序列

灵感来自这个答案为了测试一个参数是否为正整数,我想将其扩展到浮点数。我想要一个宏来\TestNumber检查它的第一个参数是浮点数还是整数,然后有条件地输出它的第二个或第三个参数。关键是,当像 一样调用时它不应该中断\TestNumber{\textbf{Hello}},例如\IfDecimalxstring包中调用时就是这种情况。

下面的代码适用于正整数(\TestNumber{\textbf{123}}{Number}{Not a number}打印Not a number),但如前所述,我想将其扩展到浮点数。因此,\TestNumber{12.3}{Number}{Not a number}应该输出Number,而\TestNumber{\textbf{12.3}}{Number}{Not a number}应该输出Not a number

\makeatletter
\def\TestNumber#1{%
  \afterassignment\get@args\count@=0#1\hfuzz#1\hfuzz}
\def\get@args#1\hfuzz#2\hfuzz{%
  \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo%
  \else
   \expandafter\@secondoftwo%
  \fi
}
\makeatother

答案1

与更简单的整数情况相比,代码需要更多标记处理。我让函数可扩展,因此这也增加了一些代码,但事实并非如此很多。

首先,该函数使用\detokenize来确保 TeX 以后不会尝试扩展任何奇怪的东西。之后,代码继续使用 删除可能的整数部分和可能的符号\romannumeral-0#1(以及一些其他东西),然后代码删除可能的小数分隔符,并删除尾随的小数部分。之后,代码检查剩余的标记列表是否为空。如果是,则参数是有效数字,否则不是。

测试一些可能性,返回所需的输出:

在此处输入图片描述

代码可扩展意味着您可以执行以下操作:

\def\TestIfIsAPositiveNumber#1{%
  \ifdim
    \TestNumber{#1}{#1}{-1}pt
      > 0pt
    Positive :)
  \else
    Negative or weird :(
  \fi
}

并获得预期的输出。

代码如下:

\documentclass{article}
\makeatletter
\def\TestNumber#1{%
  \Test@ifempty{#1}%
    {\@secondoftwo}%
    {\expandafter\Test@integer\expandafter{\detokenize{#1}}}}%
\def\Test@integer#1{%
  \expandafter\Test@after@integer\expandafter{%
    \romannumeral-0\expandafter\Test@remove@leading@minus\expandafter{%
      \romannumeral-0\number0#1}}}
\def\Test@after@integer#1{%
  \expandafter\Test@ifempty\expandafter{%
    \romannumeral-0\Test@remove@leading@dot{#1}}}
\def\Test@remove@leading@minus#1{%
  \Test@remove@leading-{#1}}
\def\Test@remove@leading@dot#1{%
  \Test@remove@leading.{#1}}
\def\Test@remove@leading#1#2{%
  \Test@ifempty{#2}{}%
    {\Test@@remove@leading#1#2\qstop}}
\def\Test@@remove@leading#1#2#3\qstop{%
  \if\noexpand#1\noexpand#2%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi{#3}{#2#3}}
\def\Test@ifempty#1{%
  \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo
  \else
   \expandafter\@secondoftwo
  \fi}
\makeatother
\begin{document}
\def\test#1{\texttt{\detokenize{#1} = }\TestNumber{#1}{Number}{Not a number}\par}
\test{0}
\test{1}
\test{-1}
\test{.23}
\test{-.23}
\test{1.23}
\test{-1.23}
\test{\textbf{1.23}}
\end{document}

或者,使用更简单(但不可扩展)的版本l3regex(表达式是从 复制而来,并根据interface3更改了控制空间,与 匹配):\␣\s[\ \^^I\^^J\^^L\^^M]

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\regex_const:Nn \c_jessepeng_float_regex { ^[\+\-\s]*(\d+|\d*\.\d+)\s*$ }
\NewDocumentCommand \TestNumber { m m m }
  { \jessepeng_if_float:nTF {#1} {#2} {#3} }
\prg_new_protected_conditional:Npnn \jessepeng_if_float:n #1 { T, F, TF }
 {
   \regex_match:NnTF \c_jessepeng_float_regex {#1}
     { \prg_return_true: }
     { \prg_return_false: }
 }
\ExplSyntaxOff
\begin{document}
\def\test#1{\texttt{\detokenize{#1} = }\TestNumber{#1}{Number}{Not a number}\par}
\test{0}
\test{1}
\test{-1}
\test{.23}
\test{-.23}
\test{1.23}
\test{-1.23}
\test{\textbf{1.23}}
\end{document}

它产生相同的结果,但速度慢得多并且不可扩展。

答案2

从其他答案借用一个测试文件,因为你没有在问题中提供一个,对于 pdflatex 至少你可以使用它的内置正则表达式支持

在此处输入图片描述

\documentclass{article}

\makeatletter
\def\TestNumber#1{%
\ifnum1=\pdfmatch{^\string\s*-?[0-9]*[.0-9][0-9]*\string\s*$}{\detokenize{#1}}
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}


\begin{document}
\def\test#1{\texttt{\detokenize{#1} = }\TestNumber{#1}{Number}{Not a number}\par}
\test{}
\test{0}
\test{1}
\test{-1}
\test{.23}
\test{-.23}
\test{1.23}
\test{-1.23}
\test{\textbf{1.23}}
\end{document}

或者使用 luatex 的 Lua 模式版本

\documentclass{article}

\makeatletter
\ifx\pdfmatch\@undefined
\ifx\directlua\@undefined
% xetex
\typeout{use l3regex from the other answer}
\else
% luatex
\def\TestNumber#1{%
\ifnum1=\directlua{if (string.find("\luaescapestring{\detokenize{#1}}","^[-]?\@percentchar d*[.]?\@percentchar d+$"))
then tex.write("1 ")
else tex.write("0 ")
end
}
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
\fi
\else
% pdftex
\def\TestNumber#1{%
\ifnum1=\pdfmatch{^\string\s*-?[0-9]*[.0-9][0-9]*\string\s*$}{\detokenize{#1}}
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
\fi
\makeatother


\begin{document}
\def\test#1{\texttt{\detokenize{#1} = }\TestNumber{#1}{Number}{Not a number}\par}
\test{}
\test{0}
\test{1}
\test{-1}
\test{.23}
\test{-.23}
\test{1.23}
\test{-1.23}
\test{\textbf{1.23}}
\end{document}

相关内容