【问题标题】:Regex for custom decimal and thousand separator自定义小数和千位分隔符的正则表达式
【发布时间】:2020-05-11 12:37:00
【问题描述】:

我正在使用下面的正则表达式来处理自定义千位分隔符,它可以是,.space 字符中的任何一个,它适用于千位分隔符而不是小数指示符。

我正在尝试添加一个新的捕获组来处理最多 2 个小数的小数指示符(,.),但正则表达式会因千位分隔符而中断。

^[+]?(?:\d{1,3}(?:(,|.| )\d{3})*|\d+)?,?$

如何添加捕获组来处理带有自定义字符的小数?有什么想法吗?

有效输入:

1234
123.45
123,45
1234.56
1234,56
123
1,234
12,345
1,234,567
12,345,678
123,456,789

12
1.234
12.345
1.234.567
12.345.678
123.456.789

123
1 234
12 345
123 456
1 234 567
12 345 678
123 456 789

123.4567
123,4567

1,345.67
1.345,67
1 345.67

12,345.67
12.345,67
12 345.67
123,456,789.34
123.456.789,34
123 456 789.34

无效:

12.345.67
12,345,67
12 345 67
123 456 789 34

【问题讨论】:

  • 如果space不能是十进制指示符,123 45如何有效?
  • @Asocia,已更正...
  • 为什么123 4567 有效?还有123.4567123,4567
  • this 怎么样?

标签: javascript regex


【解决方案1】:

好吧,您的规范是模棱两可的,因为接受十进制指示符为',',您允许将123,456 解析为数字123456 或数字123.456(千分之一)?如果您修复歧义,不允许使用三位小数,则可以解决歧义,但要付出高昂的代价,您需要用户理解,如果他犯了使用三位小数的错误,他/她会在奇怪的条件下得到奇怪的结果(123,456 将被解析为123456.0123,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

【讨论】:

  • 我没有使用space 作为Decimal Indicator
  • 感谢您告诉我,这是无效的。我已经改正了
  • 那么我和你的评论也不需要了。让我们抹去它们。但这并没有改变我的答案,假设在第一种情况下,没有分组分隔符的空间(它们在部分上引入了严重的歧义,正如我在那里注意到的那样)
【解决方案2】:

以下正则表达式将匹配您示例中的所有情况:

^[+]?(?:\d{1,3}(?:([,. ])\d{3})*|\d+)?(?:[,.]\d+?){0,1}$

最后一部分 (?:[,.]?\d+?){0,1},使小数部分的匹配可选。

【讨论】:

  • 这是匹配 1,234.567 890,00.123456,789 不确定应该是有效数字。
  • 感谢您发现最后一部分的无限重复。现在已经修好了。
【解决方案3】:

你去吧:

^[+]?(?:\d{1,3}(?:(,|.| )\d{3})*|\d+)?((?<!,\d{3})(,\d+)|(?<!\.\d{3})(\.\d+))?$

Regex 101 demo

【讨论】:

  • 你有一些无效的匹配,比如1,123.123 123.123 123,123.123:regex101.com/r/o9tQxa/2
  • 是的,没错。我修改了 OP 的正则表达式并决定保留数千部分,因为他声称它适用于他的输入。
【解决方案4】:

假设

123.4567
123,4567
123 4567

无效,您可以使用:

^[+-]?(?:(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d\d)?|(?:\d{1,3}(?:\.\d{3})*|\d+)(?:,\d\d)?|(?:\d{1,3}(?: \d{3})*|\d+)(?:[,.]\d\d)?)$

Demo & explanation

【讨论】:

  • 这与12341234.56 不匹配。见my answer
  • 干得好!通过查看正则表达式模式,我可以看到它应该匹配 2 个小数点,因为最后是 \d\d,但它最多接受 3 个小数点,我不明白为什么。你有什么主意吗? see this. 编辑:仅当千位分隔符为 space 时才会发生这种情况。但我还是不明白为什么会这样。
  • @Asocia:我们必须将正则表达式分成 3 个部分,具体取决于千位分隔符逗号、点或空格,每个千位分隔符允许使用小数点分隔符。查看我的编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 2017-09-06
  • 1970-01-01
相关资源
最近更新 更多