我正在寻找一种可靠的方法来确定是否可以将字符串输入到 中\pgfmathprintnumber
。我以前使用过包\IfDecimal
中的xstring
,它对于 和 等输入来说效果很好3.14
。但是\pgfmathprintnumber
无法处理将被 整理出来的科学记数法,\IfDecimal
因为它认为它不是数字。
以下是 MWE:
\documentclass{article}
\usepackage{pgf}
\usepackage{xstring}
\begin{document}
\def\testNum{3E+64}
\IfDecimal{\testNum}{
\pgfmathprintnumber[sci]{\testNum} is a number
}{
\pgfmathprintnumber[sci]{\testNum} is not a number
}
\end{document}
正如您所看到的,该数字可以被正确识别,\pgfmathprintnumber
但不能\IfDecimal
。
(我也尝试了包\DTLisnumerical
中的命令datatool
但它也不起作用)。
是否有人知道一个宏,它也可以检测科学符号为数字(最好是通过 PGF 本身进行验证 - 它会尝试用数学引擎解析字符串,但如果字符串不能解析为数字,它就会退出,而不是用错误杀死 LaTeX)?
答案1
好吧,我做了大量的网络研究,偶然发现这个问题上一个回答中有一个有趣的方法。此外,我发现这个帖子。
从第一个链接问题中可以看出,有一个键/pgf/fpu/handlers/invalid number/.code
定义当遇到错误的数字格式时的行为\pgfmathfloatparsenumber
。引用手册:
如果在 \pgfmathfloatparsenumber 中解析了无效字符串,则会调用此命令键。您可以覆盖它以分配替换的 \pgfmathresult(浮点数!)。初始设置是生成错误消息。
从第二个问题中我发现,有一些标志可以指示解析的数字是否有效。所以我将它们两个结合起来得到
\documentclass{article}
\usepackage{pgf}
\usepgflibrary{fpu}
\newcommand{\isPGFNumber}[3]{% \isPGFNumber{<Number>}{<True>}{<False>}
\begingroup
\pgfkeys{/pgf/fpu/handlers/invalid number/.code={}}%
\pgfmathfloatparsenumber{#1}%
\global\pgfmathfloatgetflagstomacro\pgfmathresult\pgfFloatParseFlag%
\endgroup
\ifnum\pgfFloatParseFlag=3%
#3%
\else%
#2%
\fi%
}
\begin{document}
\isPGFNumber{3}{T}{F}\\
\isPGFNumber{3.23}{T}{F}\\
\isPGFNumber{.3}{T}{F}\\
\isPGFNumber{3E+12}{T}{F}\\
\isPGFNumber{3e-8}{T}{F}\\
\isPGFNumber{3z}{T}{F}\\
\isPGFNumber{Is there a problem?}{T}{F}\\\\
%\pgfmathfloatparsenumber{z}
\end{document}
\begingroup
宏定义中的 and是\endgroup
为了在调用宏后返回到正常的无效数字错误行为(可以通过取消注释文档中的最后一条语句来测试)。如您所见,即使以科学格式提供,它也能正确处理数字。
答案2
您可以使用正则表达式来完成此操作。
\documentclass{article}
\usepackage{xparse}
\usepackage{siunitx} % for the test
\ExplSyntaxOn
\prg_new_protected_conditional:Nnn \raven_if_fpnumber:n { T,F,TF }
{
\regex_match:nnTF { [eE] } { #1 }
{% there is e or E
\regex_match:nnTF
{
% optional sign,
% optional integer part,
% optional period,
% optional fractional part,
% one e or E
% optional sign
% mandatory integer
\A
[-+]?([0-9]+(\.[0-9]+)?|\.[0-9]+)
[eE]{1}
[-+]?[0-9]+
\Z
}
{ #1 } { \prg_return_true: } { \prg_return_false: }
}
{% no e nor E
\regex_match:nnTF
{
\A
[-+]?([0-9]+(\.[0-9]+)?|\.[0-9]+)
\Z
}
{ #1 } { \prg_return_true: } { \prg_return_false: }
}
}
\prg_generate_conditional_variant:Nnn \raven_if_fpnumber:n { x } { T,F,TF }
\NewDocumentCommand{\isfpnumberTF}{mmm}
{
\raven_if_fpnumber:xTF { #1 } { #2 } { #3 }
}
\ExplSyntaxOff
% for the test
\newcommand{\test}[1]{%
\texttt{\detokenize\expandafter{#1}}:
\isfpnumberTF{#1}{\num{#1}}{``#1'' is not a number}%
}
\newcommand{\anumber}{3E+64}
\begin{document}
\test{\anumber}
\test{3}
\test{3.23}
\test{.3}
\test{3E+12}
\test{3e-8}
\test{3z}
\test{Is there a problem?}
\end{document}
答案3
以下内容基于我在其他地方实施的测试。我没有删除所有不必要的内容以使此答案有效,因此可能会添加不必要的内容。对论证中组的测试来自检查组作为参数(可扩展)。
该测试应该是完全可扩展的。
\documentclass[]{article}
\makeatletter
\def\endMYifargument{\endMYifargument}
\newcommand\MYif@endMYifargument[1]% >>>
{%
\ifx\endMYifargument#1%
\MYif@fiAa
\else
\MYif@fiBb
\fi
}% <<<
% MYif@fi macros >>>
\long\def\MYif@fiAa#1\fi#2#3{\fi#2}
\long\def\MYif@fiAb#1\fi#2#3{\fi#3}
\long\def\MYif@fiBa\fi#1#2{\fi#1}
\long\def\MYif@fiBb\fi#1#2{\fi#2}
\long\def\MYif@fiAy#1\fi#2{\fi#2}
\long\def\MYif@fiAn#1\fi#2{\fi}
\long\def\MYif@fiBy\fi#1{\fi#1}
\long\def\MYif@fiBn\fi#1{\fi}
\long\def\MYif@fifiAa#1\fi\fi#2#3{\fi#2}
\long\def\MYif@fifiAb#1\fi\fi#2#3{\fi#3}
\long\def\MYif@fifiBAa#1\fi\fi#2#3{\fi\fi#2}
\long\def\MYif@fifiBAb#1\fi\fi#2#3{\fi\fi#3}
\long\def\MYif@fifiBBa\fi\fi#1#2{\fi\fi#1}
\long\def\MYif@fifiBBb\fi\fi#1#2{\fi\fi#2}
% <<<
\newcommand*\MYif@groupify@TF[1]% >>>
{%
\expandafter\long\expandafter\edef\csname MYif#1GTF\endcsname##1%
{%
\unexpanded{\MYifGroupTF}{##1}%
{\unexpanded\expandafter{\csname MYif#1GTF\endcsname}##1}%
{\unexpanded\expandafter{\csname MYif#1TF\endcsname}{##1}}%
}%
}% <<<
\newcommand*\MYif@create@TandF[1]% >>>
{%
\expandafter\long\expandafter\edef\csname MYif#1T\endcsname##1%
{%
\unexpanded\expandafter{\csname MYif#1TF\endcsname}{##1}%
{\noexpand\@firstofone}
{\noexpand\@gobble}%
}%
\expandafter\long\expandafter\edef\csname MYif#1F\endcsname##1%
{%
\unexpanded\expandafter{\csname MYif#1TF\endcsname}{##1}%
{\noexpand\@gobble}
{\noexpand\@firstofone}%
}%
}% <<<
\newcommand*\MYif@create@TandF@twoargs[1]% >>>
{%
\expandafter\long\expandafter\edef\csname MYif#1T\endcsname##1##2%
{%
\unexpanded\expandafter{\csname MYif#1TF\endcsname}{##1}{##2}%
{\noexpand\@firstofone}
{\noexpand\@gobble}%
}%
\expandafter\long\expandafter\edef\csname MYif#1F\endcsname##1##2%
{%
\unexpanded\expandafter{\csname MYif#1TF\endcsname}{##1}{##2}%
{\noexpand\@gobble}
{\noexpand\@firstofone}%
}%
}% <<<
\newcommand*\MYif@create@N@TandF[1]% >>>
{%
\expandafter\long\expandafter\edef\csname MYif#1NTF\endcsname##1%
{%
\noexpand\expandafter
\expandafter\noexpand\csname MYif#1TF\endcsname
\noexpand\expandafter{##1}%
}%
\MYif@create@TandF{#1N}%
}% <<<
\newcommand*\MYif@create@N@TandF@twoargs[1]% >>>
{%
\expandafter\long\expandafter\edef\csname MYif#1NNTF\endcsname##1##2%
{%
\noexpand\expandafter
\expandafter\noexpand\csname MYif#1NNTF@a\endcsname
\noexpand\expandafter{##2}{##1}%
}%
\expandafter\long\expandafter\edef\csname MYif#1NNTF@a\endcsname##1##2%
{%
\noexpand\expandafter
\expandafter\noexpand\csname MYif#1TF\endcsname
\noexpand\expandafter{##2}{##1}%
}%
\MYif@create@TandF@twoargs{#1NN}%
\expandafter\long\expandafter\edef\csname MYif#1NnTF\endcsname##1##2%
{%
\noexpand\expandafter
\expandafter\noexpand\csname MYif#1TF\endcsname
\noexpand\expandafter{##1}{##2}%
}%
\MYif@create@TandF@twoargs{#1Nn}%
\expandafter\long\expandafter\edef\csname MYif#1nNTF\endcsname##1##2%
{%
\noexpand\expandafter
\expandafter\noexpand\csname MYif#1nNTF@a\endcsname
\noexpand\expandafter{##2}{##1}%
}%
\expandafter\long\expandafter\edef\csname MYif#1nNTF@a\endcsname##1##2%
{%
\expandafter\noexpand\csname MYif#1TF\endcsname{##2}{##1}
}%
\MYif@create@TandF@twoargs{#1nN}%
}% <<<
\newcommand*\MYif@create@TandF@N@TandF[1]% >>>
{%
\MYif@create@TandF{#1}%
\MYif@create@N@TandF{#1}%
}% <<<
\newcommand*\MYif@create@TandF@N@TandF@twoargs[1]% >>>
{%
\MYif@create@TandF@twoargs{#1}%
\MYif@create@N@TandF@twoargs{#1}%
}% <<<
\newcommand\MYif@notEmpty@noGroup@noSpace[2]% >>>
{%
\MYifEmptyTF{#1}
{\@secondoftwo}
{%
\MYifContainsGroupTF{#1}
{\@secondoftwo}
{%
\MYifContainsSpaceTF{#1}
{\@secondoftwo}
{#2}%
}%
}%
}% <<<
% MYifEmpty >>>
\newcommand\MYifEmptyTF[1]% >>>
{%
\if\relax\detokenize{#1}\relax
\MYif@fiAa
\else
\MYif@fiBb
\fi
}% <<<
\newcommand\MYifEmptyT[1]{% >>>
\if\relax\detokenize{#1}\relax
\MYif@fiAy
\else
\MYif@fiBn
\fi}
% <<<
\newcommand\MYifEmptyF[1]{% >>>
\if\relax\detokenize{#1}\relax
\MYif@fiAn
\else
\MYif@fiBy
\fi}
% <<<
\newcommand\MYifEmptyNTF[1]% >>>
{\expandafter\MYifEmptyTF\expandafter{#1}}% <<<
\newcommand\MYifEmptyNT[1]% >>>
{\expandafter\MYifEmptyT\expandafter{#1}}% <<<
\newcommand\MYifEmptyNF[1]% >>>
{\expandafter\MYifEmptyF\expandafter{#1}}% <<<
\MYif@groupify@TF{Empty}
\MYif@create@TandF@N@TandF{EmptyG}
% <<<
% MYifContainsGroup >>>
\newcommand\MYifContainsGroupTF[1]% >>>
{%
\expandafter\MYifContainsGroup@a
\MYifContainsGroup@eat#1{}%
\MYifContainsGroup@endargument
}% <<<
\long\def\MYifContainsGroup@eat#1#{}
\long\def\MYifContainsGroup@a#1\MYifContainsGroup@endargument% >>>
{%
\MYifEmptyTF{#1}
{\@secondoftwo}
{\@firstoftwo}%
}% <<<
\MYif@groupify@TF{ContainsGroup}
\MYif@create@TandF@N@TandF{ContainsGroup}
\MYif@create@TandF@N@TandF{ContainsGroupG}
% <<<
% MYifContainsSpace >>>
\newcommand\MYifContainsSpaceTF[1]% >>>
{%
\expandafter\MYifContainsSpace@a
\MYifContainsSpace@eat#1
\MYifContainsSpace@endargument
}% <<<
\long\def\MYifContainsSpace@a#1\MYifContainsSpace@endargument% >>>
{%
\MYifEmptyTF{#1}
{\@secondoftwo}
{\@firstoftwo}%
}% <<<
\long\def\MYifContainsSpace@eat#1 {}
\MYif@groupify@TF{ContainsSpace}
\MYif@create@TandF@N@TandF{ContainsSpace}
\MYif@create@TandF@N@TandF{ContainsSpaceG}
% <<<
% MYifDigit >>>
\newcommand\MYifDigitTF[1]% >>>
{%
\MYifOneTokenNoGroupTF{#1}
{\MYifDigit@a{#1}}
{\@secondoftwo}%
}% <<<
\newcommand\MYifDigit@a[1]% >>>
{%
\ifx0#1\MYif@fiAa
\else\MYifDigit@b1#1%
\else\MYifDigit@b2#1%
\else\MYifDigit@b3#1%
\else\MYifDigit@b4#1%
\else\MYifDigit@b5#1%
\else\MYifDigit@b6#1%
\else\MYifDigit@b7#1%
\else\MYifDigit@b8#1%
\else\MYifDigit@b9#1%
\else\MYif@fiBb
\fi
}% <<<
\newcommand\MYifDigit@b[2]% >>>
{%
\fi\ifx#1#2\MYif@fiAa
}% <<<
\newcommand\MYifDigitGTF[1]% >>>
{%
\MYifGroupTF{#1}
{\MYifDigitGTF#1}
{%
\MYifOneTokenTF{#1}
{\MYifDigit@a{#1}}
{\@secondoftwo}%
}%
}% <<<
\MYif@create@TandF@N@TandF{Digit}
\MYif@create@TandF@N@TandF{DigitG}
% <<<
% MYifNumber >>>
\newcommand\MYifNumberTF[1]% >>>
{%
\MYif@notEmpty@noGroup@noSpace{#1}{\MYifNumber@ifSign#1\endMYifargument}%
}% <<<
\newcommand\MYifNumber@ifSign[1]% >>>
{%
\ifx+#1%
\MYif@fifiAa
\else
\ifx-#1%
\MYif@fifiBAa
\else
\MYif@fifiBBb
\fi
\fi
{\MYifNumber@ifDigit@noend}
{\MYifNumber@ifDigit#1}
}% <<<
\newcommand\MYifNumber@ifDigit[1]% >>>
{%
\MYif@endMYifargument{#1}
{\@firstoftwo}
{%
\MYifDigit@a{#1}
{\MYifNumber@ifDigit}
{\MYifNumber@false}%
}%
}% <<<
\newcommand\MYifNumber@ifDigit@noend[1]% >>>
{%
\MYif@endMYifargument{#1}
{\@secondoftwo}
{%
\MYifDigit@a{#1}
{\MYifNumber@ifDigit}
{\MYifNumber@false}%
}%
}% <<<
\long\def\MYifNumber@false#1\endMYifargument% >>>
{%
\@secondoftwo
}% <<<
\MYif@groupify@TF{Number}
\MYif@create@TandF@N@TandF{Number}
\MYif@create@TandF@N@TandF{NumberG}
% <<<
% MYifFloat >>>
\newcommand\MYifFloatTF[1]% >>>
{%
\MYif@notEmpty@noGroup@noSpace{#1}{\MYifFloat@a{#1}}
}% <<<
%\newcommand\MYifFloat@a \newcommand\MYifFloat@ifDot >>>
\begingroup
\catcode`\?=4
\def\MYif@zz
{%
\endgroup
\newcommand\MYifFloat@a[1]% >>>
{%
\MYifFloat@ifDot{##1}
{\MYifFloat@withDot##1\MYifFloat@endargument}
{\MYifNumber@ifSign##1\endMYifargument}%
}% <<<
\newcommand\MYifFloat@ifDot[1]% >>>
{%
\MYifFloat@ifDot@a##1.?\MYifFloat@endargument
}% <<<
\long\def\MYifFloat@ifDot@a##1.##2\MYifFloat@endargument% >>>
{%
\ifx?##2%
\MYif@fiAb
\else
\MYif@fiBa
\fi
}% <<<
}
\MYif@zz
% <<<
\long\def\MYifFloat@withDot#1.#2\MYifFloat@endargument% >>>
{%
\MYifEmptyTF{#1}
{\MYifNumber@ifDigit@noend#2\endMYifargument}
{%
\MYifNumber@ifSign#1\endMYifargument
{\MYifNumber@ifDigit#2\endMYifargument}
{\@secondoftwo}%
}%
}% <<<
\MYif@groupify@TF{Float}
\MYif@create@TandF@N@TandF{Float}
\MYif@create@TandF@N@TandF{FloatG}
% <<<
% MYifContainsE >>>
\newcommand\MYifContainseTF[1]% >>>
{%
\MYifContainsE@e#1e\MYifContainsE@end
{\@secondoftwo}
{\@firstoftwo}%
}% <<<
\long\def\MYifContainsE@e#1e#2\MYifContainsE@end% >>>
{%
\MYifEmptyTF{#2}%
}% <<<
\newcommand\MYifContainsETF[1]% >>>
{%
\MYifContainsE@E#1E\MYifContainsE@end
{\@secondoftwo}
{\@firstoftwo}%
}% <<<
\long\def\MYifContainsE@E#1E#2\MYifContainsE@end% >>>
{%
\MYifEmptyTF{#2}%
}% <<<
% <<<
% MYifScientific >>>
\newcommand\MYifScientificTF[1]% >>>
{%
\MYifContainsETF{#1}
{\MYifScientific@withE#1\endMYifScientific}
{%
\MYifContainseTF{#1}
{\MYifScientific@withe#1\endMYifScientific}
{\MYifFloatTF{#1}}%
}%
}% <<<
\long\def\MYifScientific@withE#1E#2\endMYifScientific% >>>
{%
\MYifFloatTF{#1}
{\MYifNumberTF{#2}}
{\@secondoftwo}%
}% <<<
\long\def\MYifScientific@withe#1e#2\endMYifScientific% >>>
{%
\MYifFloatTF{#1}
{\MYifNumberTF{#2}}
{\@secondoftwo}%
}% <<<
\MYif@groupify@TF{Scientific}
\MYif@create@TandF@N@TandF{Scientific}
\MYif@create@TandF@N@TandF{ScientificG}
% <<<
\makeatother
\usepackage{pgf}
\begin{document}
\def\testNum{3e+64}
\MYifScientificNTF{\testNum}
{%
\pgfmathprintnumber[sci]{\testNum} is a number
}
{%
\pgfmathprintnumber[sci]{\testNum} is not a number
}
\end{document}