好吧,您的规范是模棱两可的,因为接受十进制指示符为',',您允许将123,456 解析为数字123456 或数字123.456(千分之一)?如果您修复歧义,不允许仅使用三位小数,则可以解决歧义,但要付出高昂的代价,您需要用户理解,如果他犯了使用三位小数的错误,他/她会在奇怪的条件下得到奇怪的结果(123,456 将被解析为123456.0 而123,4560 将被解析为123.456)这对于用户来说很奇怪。更有趣的是使用单个, 或. 表示小数点的条件,而如果您有两个指标,则第一个将是组分隔符,而第二个将是小数点。
恕我直言,我不应该将空格用作小数指示符(如果将其用作组分隔符,只需将其用作 only 数字组分隔符 --- 一些编程语言,例如 Java,允许_ 用作数字组分隔符),只是没有人使用它。最好完全不使用小数指示符(将数字设为整数,缩放 10、100 或 1000 倍,这已在桌面计算器中长期使用),因为快速数据输入人们更喜欢键入额外的零,而不是移动手指定位小数点,然后在大多数情况下再输入两个数字。不要说他是否必须去字母键盘才能找到空格键。 (嗯,当然去那里找到下划线_ char 比较困难,但是快速打字机不使用组分隔符)
另一方面,人们通常不会键入千位分隔符,而只是为了可读性(计算机在打印时这样做,但从不读取)。在这种情况下,有时他们不希望三位数一组的僵化情况,而是任意使用它们。这会导致某些情况下,用户希望在小数点左侧以三个为一组来分隔数字,而在右侧使用五个或十个一组(这是您根本没有考虑的)进行制作,例如PI 显示为:
3.14159 26535 89793 23846 264338 3
我同意使用备用小数点作为分组分隔符可能会很有趣,但在实际小数点的两侧,并且从不强制三个一组。
无论如何,为了符合您的规范,我编写了以下 lex(1) 规范来解析您的输入。
pfx [1-9][0-9]?[0-9]?
grp [0-9][0-9][0-9]
dec [0-9]*
e1 [+-]?{pfx}([.]{grp})*([,]{dec})?
e2 [+-]?{pfx}([,]{grp})*([.]{dec})?
e3 [+-]?{pfx}([ ]{grp})*([.,]{dec})?
e4 [+-]?[1-9][0-9]*([,.]{dec})?
e5 [+-]?0?([,.]{dec})?
%%
{e1}|{e2}|{e3}|{e4}|{e5} printf("\033[32m[%s]\033[m\n", yytext);
[0-9., +-]* printf("\033[31m[%s]\033[m\n", yytext);
. |
\n |
\t ;
%%
int main()
{
yylex();
}
int yywrap()
{
return 1;
}
你的正则表达式,完整的,应该是这样的:
[+-]?[0-9]{1,3}([ ][0-9]{3})*([,.]([0-9]{3}[ ])*[0-9]{1,3})?|[+-]?[0-9]{1,3}([ ][0-9]{3})*([,.][0-9]{0,2})?|[+-]?[0-9]{0,2}[,.]([0-9]{3}[ ])*[0-9]{1,3}|[+-]?[0-9]{1,3}([,][0-9]{3})*([.]([0-9]{3}[,])*[0-9]{1,3})?|[+-]?[0-9]{1,3}([,][0-9]{3})*([.][0-9]{0,2})?|[+-]?[0-9]{0,2}[.]([0-9]{3}[,])*[0-9]{1,3}|[+-]?[0-9]{1,3}([.][0-9]{3})*([,]([0-9]{3}[.])*[0-9]{1,3})?|[+-]?[0-9]{1,3}([.][0-9]{3})*([,][0-9]{0,2})?|[+-]?[0-9]{0,2}[,]([0-9]{3}[.])*[0-9]{1,3}|[+-]?[0-9]*[,.][0-9]+|[+-]?[0-9]+[,.][0-9]*|[+-]?[0-9]+
注意
一些正则表达式库,没有正确实现| 运算符,使其实际上不能交换(我知道的最坏情况是 regex101.com,见下文),并迫使您将操作数放入一些特殊的顺序来匹配一些字符串(这是库中的一个错误,但不幸的是,这是传播的)下面是上面的(与sed(1) 一起工作),你会看到它是如何doesn't match correctly in reg101 (应该有更少的匹配项)。
我还编写了一个 bash 脚本(如下所示)以将 sed(1) 与上述正则表达式一起使用,因此您可以在您的站点上看到它是如何工作的:
dig="[0-9]"
af0="${dig}{0,2}"
af1="${dig}{1,3}"
grp="${dig}{3}"
t01="[+-]?${af1}([ ]${grp})*([,.](${grp}[ ])*${af1})?"
t02="[+-]?${af1}([ ]${grp})*([,.]${af0})?"
t03="[+-]?${af0}[,.](${grp}[ ])*${af1}"
t04="[+-]?${af1}([,]${grp})*([.](${grp}[,])*${af1})?"
t05="[+-]?${af1}([,]${grp})*([.]${af0})?"
t06="[+-]?${af0}[.](${grp}[,])*${af1}"
t07="[+-]?${af1}([.]${grp})*([,](${grp}[.])*${af1})?"
t08="[+-]?${af1}([.]${grp})*([,]${af0})?"
t09="[+-]?${af0}[,](${grp}[.])*${af1}"
t10="[+-]?${dig}*[,.]${dig}+"
t11="[+-]?${dig}+[,.]${dig}*"
t12="[+-]?${dig}+"
s01="${t01}|${t02}|${t03}"
s02="${t04}|${t05}|${t06}"
s03="${t07}|${t08}|${t09}"
s04="${t10}|${t11}|${t12}"
reg="${s01}|${s02}|${s03}|${s04}"
echo "$reg"
sed -E -e "s/${reg}/<&>/g"
您可以找到所有这些代码(和更新)here。