匹配数字与正则表达式 - 只有数字和逗号

use*_*618 52 .net regex numbers matching

我无法弄清楚如何为示例值构造一个正则表达式:

123,456,789
-12,34
1234
-8
Run Code Online (Sandbox Code Playgroud)

你可以帮帮我吗?

tch*_*ist 475

什么是数字?

对于你的 "简单"问题我有一个简单的问题:你用"数字"究竟是什么意思?

  • ?0一个数字?
  • 你觉??1得怎么样?
  • ??数字?
  • 186,282.42±0.02英里/秒的一个数字 - 还是两个或三个?
  • 6.02e23一个数字?
  • 3.141_592_653_589一个数字?怎么样?,或者??而且?2??³ ?
  • 中有多少个0.083?
  • 中有多少个128.0.0.1
  • ?拿着什么号码?怎么样??
  • 里面10,5 mm有一个数字 - 或者它有两个?
  • ?8³一个数字 - 或者它是三个?
  • ??????? AUC代表什么号码,2762或2009?
  • ????????数字?
  • 怎么样0377,0xDEADBEEF0b111101101
  • Inf一个数字?是NaN吗?
  • ??一个数字?怎么样?
  • 你觉?得怎么样?
  • 什么??,并??与数字呢?或者?,??

建议的模式

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

  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]+))|))/

我怀疑上面的一些模式可能满足您的需求.但是我不能告诉你哪一个 - 或者,如果没有一个,那么为你提供另一个 - 因为你没有用"数字"说出你的意思.

正如你看到的,有数量庞大的数量可能:大抵相当ℵ₁值得他们,其实.☺

建议模式的关键

下面列出的每个编号说明描述了上面列出的相应编号图案的图案.

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

来源和可维护性

模式编号1,2,7-11来自问题"如何验证输入?" 中的Perl 常见问题列表的先前版本.该部分已被使用由AbigailDamian Conway编写的Regexp :: Common模块的建议所取代.原始模式仍然可以在Perl Cookbook的 Recipe 2.1中找到,"检查字符串是否为有效数字",可以找到令人眼花缭乱的多种语言的解决方案,包括ada,common lisp,groovy,guile,在PLEAC项目中,haskell,java,merd,ocaml,php,pike,python,rexx,ruby和tcl .

模式12可以更清晰地重写

m{
    ^
    (
        ( \d )
        (?(?= ( \d ) ) | $ )
        (?(?{ ord $3 == 1 + ord $2 }) (?1) | $ )
    )
    $
}x
Run Code Online (Sandbox Code Playgroud)

它使用正则表达式递归,它可以在许多模式引擎中找到,包括Perl和所有PCRE派生的语言.但它也使用嵌入式代码标注作为其第二个条件模式的测试; 据我所知,代码标注仅在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
Run Code Online (Sandbox Code Playgroud)

从软件工程的角度来看,/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/);
Run Code Online (Sandbox Code Playgroud)

这是坦率的可恶!很容易让编号错误,很难记住哪些符号名称在哪里,写起来很乏味,特别是如果你不需要所有这些.重写使用命名组而不是编号的组.同样,我将对变量使用Perl语法,但Pattern的内容应该适用于支持命名组的任何位置.

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;
Run Code Online (Sandbox Code Playgroud)

现在抽象被命名,这有所帮助.你可以按名称拉出组,你只需要你关心的组.例如:

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

还有一件事要做这个模式,以使其更易于维护.问题是仍然有太多的重复,这意味着它很容易在一个地方改变而在另一个地方不容易改变.如果您正在进行McCabe分析,您会说它的复杂度指标太高了.我们大多数人只会说它太缩进了.这使得很难遵循.为了解决所有这些问题,我们需要的是一个"语法模式",一个用于创建命名抽象的定义块,然后我们将其视为稍后在匹配中的子例程调用.

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;
Run Code Online (Sandbox Code Playgroud)

看看语法模式比原始的线条噪声模式更好吗?获得正确的语法也要容易得多:我输入的内容甚至没有需要纠正的一个正则表达式语法错误.(好吧,我输入了所有其他的没有任何语法错误,但我已经这样做了一段时间.:)

语法模式看起来更像是BNF而不是人们讨厌的丑陋的旧正则表达式.它们更容易阅读,编写和维护.所以,让我们没有更难看的模式,好吗?

  • 好的,+1,因为我不得不对这个答案微笑. (15认同)
  • 在一粒沙子中看宇宙 - 正则表达式,可读性,框架问题,稳健性,清晰度,光照 - 一小时内的永恒 - 在这个答案中压缩了多少智慧? (14认同)
  • @ThiefMaster,我希望现在通过精心解释回答我原来的,*非常非修辞性的问题,你不只是微笑,而是确实咧嘴一笑!:) (6认同)
  • @ʞɔɐɯɹoↃɔWsǝɯɐs:不知道如何使用微软,但最新的Perl版本完全无关紧要:`perl -Mv5.14 -MUnicode :: UCD = num -CSA -E'说"$ _ is",num( $ _)for @ARGV'"4567""314159"`打印出`4567是4567 314159是314159`.这意味着你需要为数字字符串做的就是用`/(\ d +)/`来获取它们并调用[Unicode :: UCD :: num](http://search.cpan.org/~jesse/perl- 5.14.0/lib/Unicode/UCD.pm #num)函数就可以了.我不知道微软的国际支持有多好,但它必须比Java更好. (2认同)
  • 很棒的答案 - 但你有21种模式和20种解释.:) (2认同)
  • 我有一个很好的想法,让我们讨论是否有meaningful1有意义的数字正则表达式或只有它们的ℵ0. (2认同)
  • 此答案已添加到[Stack Overflow Regular Expression FAQ](http://stackoverflow.com/a/22944075/2736496),在"Advanced Regex-Fu> Common validation> Numeric"下,大约2/3 down. (2认同)

Thi*_*ter 38

如果你只想允许数字和逗号,那么^[-,0-9]+$你的正则表达式是什么.如果您还想允许空格,请使用^[-,0-9 ]+$.

但是,如果你想允许正确的数字,最好使用这样的东西:

^([-+] ?)?[0-9]+(,[0-9]+)?$
Run Code Online (Sandbox Code Playgroud)

或者只是使用.net的数字解析器(对于各种NumberStyles,请参阅MSDN):

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

  • 那太讨厌了.我认为它甚至没有回答他的问题.然后,@ user278618问的问题与@ user278618提供的示例不一致.我在自己的答案中提出了许多解决方案,*没有*其中有许多问题都存在于你自己的问题中.有可能我的`/ ^(?:(?:[+ - ]?)(?:[0123456789] {1,3}(?:,?[0123456789] {3})*))$ /`满足他的需要,但由于措辞不精确和冲突,不可能说出来.但当然我的工作比你的好得多! (2认同)

Gar*_*een 9

试试这个:

^-?\d{1,3}(,\d{3})*(\.\d\d)?$|^\.\d\d$
Run Code Online (Sandbox Code Playgroud)

允许:

1
12
.99
12.34 
-18.34
12,345.67
999,999,999,999,999.99
Run Code Online (Sandbox Code Playgroud)


zx8*_*x81 6

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

A.如果可能,请勿使用正则表达式验证数字

如果可能的话,请使用您的语言.可能有一些函数可帮助您确定字符串包含的值是否为有效数字.话虽这么说,如果你接受各种格式(逗号等),你可能没有选择.

B.不要手动编写正则表达式以验证数字范围

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

C.明智地花费你的正则表达能源:使用工具

  • 在给定范围内匹配数字是已经解决的问题.没有必要尝试重新发明轮子.这是一个问题,可以通过程序以一种保证无差错的方式机械地解决.充分利用免费乘车.
  • 解决数字范围的正则表达式可能对于学习目的而言有趣几次.除此之外,如果你有能力投资进一步提高你的正则表达能力,可以把它花在一些有用的东西上,比如加深对正则表达式贪婪的理解,阅读Unicode正则表达式,玩零宽度匹配或递归,阅读SO正则表达式常见问题解答并发现巧妙的技巧,如如何从正则表达式匹配中排除某些模式 ......或阅读经典材料,如Matering Regular Expressions,3rd EdThe Regular Expressions Cookbook,2nd Ed.

对于工具,您可以使用:

  • 在线:Regex_for_range
  • 离线:我所知道的唯一一个是RegexMagic正则表达的大师Jan Goyvaerts(不是免费的).这是他的初学者正则表达式产品,我记得它有很多选项可以在给定范围内生成数字,以及其他功能.
  • 如果条件过于复杂,则自动生成两个范围...然后使用交替运算符将它们连接起来 |

D.练习:为问题中的规范构建正则表达式

这些规格相当广泛......但不一定含糊不清.让我们再看一下样本值:

123,456,789
-12,34
1234
-8
Run Code Online (Sandbox Code Playgroud)

前两个值如何相关?在第一个中,逗号匹配三个权力组.在第二种情况下,它可能与欧洲大陆式数字格式的小数点相匹配.这并不意味着我们应该在任何地方允许数字,如1,2,3,44.出于同样的原因,我们不应该是限制性的.例如,接受的答案中的正则表达式将不符合其中一个要求123,456,789(参见演示).

我们如何构建我们的正则表达式以符合规范?

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

完整的正则表达式:

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

演示.

这个正则表达式不允许以欧洲风格的数字开头0,例如0,12.这是一个功能,而不是一个bug.为了匹配这些,一个小的调整将做:

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

演示.