用正则expression式匹配数字 – 只有数字和逗号

我不知道如何构build示例值的正则expression式:

123,456,789 -12,34 1234 -8 

你可以帮帮我吗?

如果你只想允许数字和逗号, ^[-,0-9]+$是你的正则expression式。 如果您还想要空格,请使用^[-,0-9 ]+$

但是,如果你想允许正确的数字,最好去这样的事情:

 ^([-+] ?)?[0-9]+(,[0-9]+)?$ 

或简单地使用.net的数字parsing器 (对于各种NumberStyles,请参阅MSDN ):

 try { double.Parse(yourString, NumberStyle.Number); } catch(FormatException ex) { /* Number is not in an accepted format */ } 

什么是数字?

对于你的 “简单”问题,我有一个简单的问题:“一个数字”是什么意思?

  • −0是一个数字?
  • 你觉得√−1怎么样?
  • 是数字吗?
  • 一个号码是186,282.42±0.02英里/秒?还是两三个呢?
  • 6.02e23一个数字?
  • 3.141_592_653_589一个数字? π或How怎么 ? 和−2π⁻³ ͥ
  • 0.083̄有多less个数字?
  • 128.0.0.1有多less个数字?
  • 什么号码持有? 怎么⚂⚃
  • 10,5 mm有一个数字,还是有两个?
  • ∛8³是一个数字 – 还是三个?
  • ↀↀⅮⅭⅭⅬⅫ AUC代表2762或2009年的数字是ↀↀⅮⅭⅭⅬⅫ AUC
  • ४५६७৭৮৯৮号码?
  • 那么0b1111011010b111101101呢?
  • 是不是一个数字? NaN
  • ④②是一个数字吗? 那么
  • 你觉
  • ℵ₀ℵ₁与数字有什么关系? 或

build议模式

另外,你是否熟悉这些模式? 你能解释每个人的利弊吗?

  1. /\D/
  2. /^\d+$/
  3. /^\p{Nd}+$/
  4. /^\pN+$/
  5. /^\p{Numeric_Value:10}$/
  6. /^\P{Numeric_Value:NaN}+$/
  7. /^-?\d+$/
  8. /^[+-]?\d+$/
  9. /^-?\d+\.?\d*$/
  10. /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
  11. /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
  12. /^((\d)(?(?=(\d))|$)(?(?{ord$3==1+ord$2})(?1)|$))$/
  13. /^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$/
  14. /^(?:(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}))$/
  15. /^(?:(?:[+-]?)(?:[0123456789]+))$/
  16. /(([+-]?)([0123456789]{1,3}(?:,?[0123456789]{3})*))/
  17. /^(?:(?:[+-]?)(?:[0123456789]{1,3}(?:,?[0123456789]{3})*))$/
  18. /^(?:(?i)(?:[+-]?)(?:(?=[0123456789]|[.])(?:[0123456789]*)(?:(?:[.])(?:[0123456789]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[0123456789]+))|))$/
  19. /^(?:(?i)(?:[+-]?)(?:(?=[01]|[.])(?:[01]{1,3}(?:(?:[,])[01]{3})*)(?:(?:[.])(?:[01]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[01]+))|))$/
  20. /^(?:(?i)(?:[+-]?)(?:(?=[0123456789ABCDEF]|[.])(?:[0123456789ABCDEF]{1,3}(?:(?:[,])[0123456789ABCDEF]{3})*)(?:(?:[.])(?:[0123456789ABCDEF]{0,}))?)(?:(?:[G])(?:(?:[+-]?)(?:[0123456789ABCDEF]+))|))$/
  21. /((?i)([+-]?)((?=[0123456789]|[.])([0123456789]{1,3}(?:(?:[_,]?)[0123456789]{3})*)(?:([.])([0123456789]{0,}))?)(?:([E])(([+-]?)([0123456789]+))|))/

我怀疑上面的一些模式可能会满足您的需求。 但是我不能告诉你哪一个或那个,或者如果没有,就提供给你另一个 – 因为你没有说出“number”是什么意思。

正如你所看到的,有很多数字的可能性:事实上,这个数字可能大概是1/1。 ☺

build议模式的关键

下面列出的每个编号说明都描述了上面列出的相应编号模式的模式。

  1. 匹配string中是否有任何非数字 ,包括换行符等空格。
  2. 只有当string只包含数字时才匹配,可能除了尾部换行符。 请注意,数字被定义为具有属性常规类别十进制数字,该数字可用为\p{Nd}\p{Decimal_Number}\p{General_Category=Decimal_Number} 。 这实际上只是数字types类别为十进制的那些代码点的反映,它可以用作\p{Numeric_Type=Decimal}
  3. 这与大多数正则expression式语言中的2相同。 Java在这里是一个例外,因为它不会像\w\W\d\D\s\S\b\B这样简单的charclass转义映射到适当的Unicode属性中。 这意味着您不得在Java中使用任何八位单字符转义符,因为即使Java在内部始终使用Unicode字符,它们也只能用于ASCII。
  4. 这与3略有不同,因为它不限于小数,而可以是任何数字; 也就是任何带有\pN\p{Number}\p{General_Category=Number}属性的\p{General_Category=Number} 。 这些包括\p{Nl}\p{Letter_Number}用于罗马数字和\p{No}\p{Other_Number}等下标和下标数字,分数和圈数。
  5. 这只匹配那些完全由十进制数字组成的string,比如罗马数字十,
  6. 只有那些包含缺less数字值NaN的字符的string; 换句话说,所有的字符必须有一些数字值。
  7. 只匹配十进制数字,可select带领先的HYPHEN MINUS。
  8. 与7相同,但现在如果符号是正数而不是负数,也可以使用。
  9. 查找十进制数字,可选的HYPHEN MINUS和可选的FULL STOP加上零或更多的十进制数字。
  10. 与9相同,但在点之前不需要数字。
  11. 每个C和许多其他语言的标准浮点标记,允许使用科学记数法。
  12. 查找任意脚本的两位或更多小数的数字,按照降序排列,如987或54321.这个recursion正则expression式包含一个Perl代码调用,用于检查向前看的数字是否具有当前数字的后继代码点值; 也就是说,它的序数值更大一些。 人们可以在PCRE中使用C函数作为标注。
  13. 这将在有效范围内查找具有四个十进制数字的有效IPv4地址,如128.0.0.1或255.255.255.240,但不是999.999.999.999。
  14. 这寻找一个有效的MAC地址,所以六个冒号分开的两个ASCIIhex数字对。
  15. 这在整个ASCII范围内寻找可选的前导符号。 这是匹配ASCII整数的正常模式。
  16. 这就像15,除了需要一个逗号来分隔三个组。
  17. 这就像15,除了逗号分隔组现在是可选的。
  18. 这是在ASCII中匹配C样式浮点数的正常模式。
  19. 这就像18,但需要一个逗号分隔3组,而不是以10为底。
  20. 这就像19,但在hex。 请注意,可选的指数现在由G代替E来表示,因为E是有效的hex数字。
  21. 这将检查该string是否包含一个C风格的浮点数字,但是在它们之间用逗号或下划线(LOW LINE)的每三位数字组成一个可选的分组分隔符。 它还将该string存储到\1捕获组中,在匹配成功后以$1提供。

来源和可维护性

模式编号1,2,7-11来自问题“我如何validationinput?”中Perl 常见问题列表的前一个版本。 该部分已被build议使用由Abigail和Damian Conway编写的Regexp :: Common模块。 原始模式仍然可以在Perl Cookbook的 Recipe 2.1中find,“检查一个string是否是有效的数字”,可以find令人眼花缭乱的多种语言的解决scheme,包括ada,common lisp,groovy,guile, haskell,java,merd,ocaml,php,pike,python,rexx,ruby和tcl在PLEAC项目中的使用 。

模式12可以更明确地重写

 m{ ^ ( ( \d ) (?(?= ( \d ) ) | $ ) (?(?{ ord $3 == 1 + ord $2 }) (?1) | $ ) ) $ }x 

它使用regexrecursion ,在许多模式引擎中都可以find它,包括Perl和所有PCRE派生语言。 但它也使用embedded式代码标注作为其第二个条件模式的testing; 据我所知,代码标注仅在Perl和PCRE中可用。

模式13-21从前面提到的Regexp :: Common模块中得到。 请注意,为了简洁起见,这些代码都是没有空格和注释的,你在生产代码中肯定会想要的。 下面是在/x模式下看起来如何:

 $real_rx = qr{ ( # start $1 to hold entire pattern ( [+-]? ) # optional leading sign, captured into $2 ( # start $3 (?= # look ahead for what next char *will* be [0123456789] # EITHER: an ASCII digit | [.] # OR ELSE: a dot ) # end look ahead ( # start $4 [0123456789]{1,3} # 1-3 ASCII digits to start the number (?: # then optionally followed by (?: [_,]? ) # an optional grouping separator of comma or underscore [0123456789]{3} # followed by exactly three ASCII digits ) * # repeated any number of times ) # end $4 (?: # begin optional cluster ( [.] ) # required literal dot in $5 ( [0123456789]{0,} ) # then optional ASCII digits in $6 ) ? # end optional cluster ) # end $3 (?: # begin cluster group ( [E] ) # base-10 exponent into $7 ( # exponent number into $8 ( [+-] ? ) # optional sign for exponent into $9 ( [0123456789] + ) # one or more ASCII digits into $10 ) # end $8 | # or else nothing at all ) # end cluster group ) }xi; # end $1 and whole pattern, enabling /x and /i modes 

从软件工程angular度来看,在上面的/x模式版本中使用的样式仍然有几个问题。 首先,有大量的代码重复,你看到相同的[0123456789] ; 如果其中一个序列意外地遗漏了数字,会发生什么? 其次,你依靠位置参数,你必须数。 这意味着你可能会写如下的东西:

 ( $real_number, # $1 $real_number_sign, # $2 $pre_exponent_part, # $3 $pre_decimal_point, # $4 $decimal_point, # $5 $post_decimal_point, # $6 $exponent_indicator, # $7 $exponent_number, # $8 $exponent_sign, # $9 $exponent_digits, # $10 ) = ($string =~ /$real_rx/); 

这是坦率的可恶! 很容易弄错编号,很难记住哪些符号名字在哪里,而且很难写,特别是如果你不需要所有这些部分。 重写,以使用命名的组,而不是只是编号的。 再一次,我将使用Perl语法作为variables,但模式的内容应该可以在任何支持指定组的地方工作。

 use 5.010; # Perl got named patterns in 5.10 $real_rx = qr{ (?<real_number> # optional leading sign (?<real_number_sign> [+-]? ) (?<pre_exponent_part> (?= # look ahead for what next char *will* be [0123456789] # EITHER: an ASCII digit | [.] # OR ELSE: a dot ) # end look ahead (?<pre_decimal_point> [0123456789]{1,3} # 1-3 ASCII digits to start the number (?: # then optionally followed by (?: [_,]? ) # an optional grouping separator of comma or underscore [0123456789]{3} # followed by exactly three ASCII digits ) * # repeated any number of times ) # end <pre_decimal_part> (?: # begin optional anon cluster (?<decimal_point> [.] ) # required literal dot (?<post_decimal_point> [0123456789]{0,} ) ) ? # end optional anon cluster ) # end <pre_exponent_part> # begin anon cluster group: (?: (?<exponent_indicator> [E] ) # base-10 exponent (?<exponent_number> # exponent number (?<exponent_sign> [+-] ? ) (?<exponent_digits> [0123456789] + ) ) # end <exponent_number> | # or else nothing at all ) # end anon cluster group ) # end <real_number> }xi; 

现在抽象被命名,这有助于。 你可以通过名字把这些小组拉出来,而你只需要那些你关心的小组。 例如:

 if ($string =~ /$real_rx/) { ($pre_exponent, $exponent_number) = @+{ qw< pre_exponent exponent_number > }; } 

还有一件事要做到这一点,使其更容易维护。 问题是还有太多的重复,这意味着在一个地方太容易改变,而另一个地方却不太容易改变。 如果你正在做McCabe分析,你会说它的复杂性度量太高。 我们大多数人会说这是太过于缩进。 这使得难以遵循。 为了解决所有这些问题,我们需要的是一个“语法模式”,一个带有一个定义块来创build命名抽象,然后在稍后的比赛中,我们就像一个子例程调用。

 use 5.010; # Perl first got regex subs in v5.10 $real__rx = qr{ ^ # anchor to front (?&real_number) # call &real_number regex sub $ # either at end or before final newline ################################################## # the rest is definition only; think of ## # each named buffer as declaring a subroutine ## # by that name ## ################################################## (?(DEFINE) (?<real_number> (?&mantissa) (?&abscissa) ? ) (?<abscissa> (?&exponent_indicator) (?&exponent) ) (?<exponent> (&?sign) ? (?&a_digit) + ) (?<mantissa> # expecting either of these.... (?= (?&a_digit) | (?&point) ) (?&a_digit) {1,3} (?: (?&digit_separator) ? (?&a_digit) {3} ) * (?: (?&point) (?&a_digit) * ) ? ) (?<point> [.] ) (?<sign> [+-] ) (?<digit_separator> [_,] ) (?<exponent_indicator> [Ee] ) (?<a_digit> [0-9] ) ) # end DEFINE block }x; 

看看语法模式比原来的线条模式有多疯狂吗? 获得正确的语法也容易得多:我input了,甚至没有一个正则expression式需要纠正的语法错误。 (好的,我input了所有其他的语法错误,但是我一直在这样做一段时间:)

语法模式看起来更像一个BNF,而不像人们讨厌的丑陋的旧正则expression式。 他们阅读,写作和维护要容易得多。 那么让我们没有更丑陋的模式,好吗?

尝试这个:

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

允许:

 1 12 .99 12.34 -18.34 12,345.67 999,999,999,999,999.99 

由于这个问题在四年后重新开放,我想提出一个不同的看法。 由于有人花了很多时间处理正则expression式,我的观点是这样的:

答:如果可能的话,不要使用正则expression式来validation数字

如果可能的话,使用你的语言。 可能有些函数可以帮助您确定string中包含的值是否是有效的数字。 这就是说,如果你接受各种格式(逗号等),你可能没有select。

B.不要手动编写正则expression式来validation数字范围

  • 写一个正则expression式来匹配给定范围内的数字是很困难的。 即使写一个正则expression式来匹配1到10之间的数字,你也可能犯了一个错误。
  • 一旦你有一个数字范围的正则expression式,很难debugging。 首先,看看是很糟糕的。 其次,你怎么能确定它匹配你想要的所有值,而不匹配任何你不想要的值? 坦率地说,如果你是独自一人,没有同伴看你的肩膀,你不能。 最好的debugging技术是以编程方式输出一系列数字,并根据正则expression式进行检查。
  • 幸运的是,有一些工具可以自动生成一个数字范围的正则expression式。

C.明智地消耗正则expression能量:使用工具

  • 匹配给定范围内的数字是一个已经解决的问题。 没有必要尝试重新发明轮子。 这是一个可以通过一个程序机械地解决的问题,保证没有错误。 利用这个免费的旅程。
  • 解决一个数字范围的正则expression式可能会有趣的几次学习的目的。 除此之外,如果你有精力投资于提高你的正则expression式技能,把它花在一些有用的东西上,比如加深你对正则expression式的理解,阅读Unicode正则expression式 ,使用零宽度匹配或者recursion,阅读SO正则expression式常见问题并发现整洁的技巧,如如何从正则expression式匹配排除某些模式 …或阅读经典,如Matering正则expression式,第三版正则expression式食谱,第二版

对于工具,您可以使用:

  • 在线: Regex_for_range
  • 离线:我知道的唯一一个是正则expression式大师Jan Goyvaerts的RegexMagic (不是免费的)。 这是他的初学者正则expression式产品,我记得它有一个范围广泛的select,在给定的范围内产生数字,除其他function。
  • 如果条件过于复杂,则自动生成两个范围…然后用交替操作符|将它们连接起来

D.练习:为问题中的规范build立一个正则expression式

这些规格是相当广泛的…但不一定含糊不清。 我们再看一下示例值:

 123,456,789 -12,34 1234 -8 

前两个值如何关联? 首先,逗号与三个权力组相匹配。 在第二种情况下,它可能与欧式风格的数字格式中的小数点匹配。 这并不意味着我们应该在任何地方都允许数字,就像1,2,3,44 。 同样道理,我们不应该是限制性的。 例如,接受的答案中的正则expression式将不符合123,456,789中的一个要求(请参阅演示 )。

我们如何build立我们的正则expression式来匹配规格?

  • 让我们锚定^$之间的expression式来避免子匹配
  • 让我们允许一个可选的减号: -?
  • 让我们在交替的两边匹配两种types的数字(?:this|that)
  • 左边是一个欧式数字,可选用逗号分隔小数部分: [1-9][0-9]*(?:,[0-9]+)?
  • 在右边,有数千个分隔符: [1-9][0-9]{1,2}(?:,[0-9]{3})+

完整的正则expression式:

 ^-?(?:[1-9][0-9]*(?:,[0-9]+)?|[1-9][0-9]{1,2}(?:,[0-9]{3})+)$ 

看演示 。

这个正则expression式不允许从0开始的欧式风格的数字,比如0,12 。 这是一个function,而不是一个错误。 为了匹配这些,一个小的调整将做到:

 ^-?(?:(?:0|[1-9][0-9]*)(?:,[0-9]+)?|[1-9][0-9]{1,2}(?:,[0-9]{3})+)$ 

看演示 。

尝试这个:

 ^-?[\d\,]+$ 

它将允许一个可选的-作为第一个字符,然后是逗号和数字的任意组合。

 ^-? # start of line, optional - (\d+ # any number of digits |(\d{1,3}(,\d{3})*)) # or digits followed by , and three digits ((,|\.)\d+)? # optional comma or period decimal point and more digits $ # end of line 
 ^[-+]?(\d{1,3})(,?(?1))*$ 

正则表达式可视化

Debuggex演示

那么它是什么?!

  • ^标记string的开头
  • [-+]? 在string开始之后允许减号加号
  • (\d{1,3})在一行中匹配至less一个和最多三个( {1,3} )数字( \d – 通常为[0-9] )并对它们进行分组(括号(...)该组)作为第一组
  • (,?(?1))*好吧,我们来分解一下
    • (...)build立另一个组( 不是那么重要
    • ,? 匹配一个逗号(如果存在)在第一个数字序列之后
    • (?1)再次匹配第一组的模式(记住(\d{1,3}) ); 换句话说:在这一点上,expression式匹配一个符号(加号/减号/无),后跟一串数字,后面可能跟着一个逗号,接着是另一个数字序列。
    • (,?(?1))**重复第二部分(逗号&序列)尽可能经常
  • $最后匹配string的结尾

这种expression的优点是,为了避免在你的expression中一遍又一遍地定义相同的模式…呃,有时候缺点是复杂性: – /

在Java中,您可以使用java.util.Scanner及其useLocale方法

 Scanner myScanner = new Scanner(input).useLocale( myLocale) isADouble = myScanner.hasNextDouble() 

例如:

  ^(-)?([,0-9])+$ 

它应该工作。 用你想要的任何语言来实现它。

尝试这个:

  boxValue = boxValue.replace(/[^0-9\.\,]/g, ""); 

此RegEx仅匹配数字,点和逗号。