灵感来自这个答案为了测试一个参数是否为正整数,我想将其扩展到浮点数。我想要一个宏来\TestNumber
检查它的第一个参数是浮点数还是整数,然后有条件地输出它的第二个或第三个参数。关键是,当像 一样调用时它不应该中断\TestNumber{\textbf{Hello}}
,例如\IfDecimal
从xstring
包中调用时就是这种情况。
下面的代码适用于正整数(\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}