regex - 正则表达式匹配文本中带或不带逗号和小数的数字

标签 regex

我正在尝试查找并替换文本正文中的所有数字。我找到了一些示例正则表达式,它们几乎解决了问题,但还没有一个是完美的。我遇到的问题是我文本中的数字可能有也可能没有小数和逗号。例如:

"The 5000 lb. fox jumped over a 99,999.99998713 foot fence."



正则表达式应该返回“5000”和“99,999.99998713”。我发现的示例将逗号上的数字分解或限制为两位小数。我开始理解正则表达式足以理解为什么某些示例仅限于两位小数,但我还没有学会如何克服它,并且还包括逗号以获取整个序列。

这是我的最新版本:
[0-9]+(\.[0-9][0-9]?)?

对于上述文本,它返回“5000”、“99,99”、“9.99”和“998713”。

最佳答案

编辑:由于这已经获得了很多观点,让我首先为每个人提供他们在 Google 上搜索到的内容:

#ALL THESE REQUIRE THE WHOLE STRING TO BE A NUMBER
#For numbers embedded in sentences, see discussion below

#### NUMBERS AND DECIMALS ONLY ####
#No commas allowed
#Pass: (1000.0), (001), (.001)
#Fail: (1,000.0)
^\d*\.?\d+$

#No commas allowed
#Can't start with "."
#Pass: (0.01)
#Fail: (.01)
^(\d+\.)?\d+$

#### CURRENCY ####
#No commas allowed
#"$" optional
#Can't start with "."
#Either 0 or 2 decimal digits
#Pass: ($1000), (1.00), ($0.11)
#Fail: ($1.0), (1.), ($1.000), ($.11)
^\$?\d+(\.\d{2})?$

#### COMMA-GROUPED ####
#Commas required between powers of 1,000
#Can't start with "."
#Pass: (1,000,000), (0.001)
#Fail: (1000000), (1,00,00,00), (.001)
^\d{1,3}(,\d{3})*(\.\d+)?$

#Commas required
#Cannot be empty
#Pass: (1,000.100), (.001)
#Fail: (1000), ()
^(?=.)(\d{1,3}(,\d{3})*)?(\.\d+)?$

#Commas optional as long as they're consistent
#Can't start with "."
#Pass: (1,000,000), (1000000)
#Fail: (10000,000), (1,00,00)
^(\d+|\d{1,3}(,\d{3})*)(\.\d+)?$

#### LEADING AND TRAILING ZEROES ####
#No commas allowed
#Can't start with "."
#No leading zeroes in integer part
#Pass: (1.00), (0.00)
#Fail: (001)
^([1-9]\d*|0)(\.\d+)?$

#No commas allowed
#Can't start with "."
#No trailing zeroes in decimal part
#Pass: (1), (0.1)
#Fail: (1.00), (0.1000)
^\d+(\.\d*[1-9])?$
既然这已经不在了,下面的大部分内容都是关于如果您尝试巧妙地使用正则表达式会变得多么复杂,以及为什么您应该寻求替代方案的评论。阅读风险自负。

这是一项非常常见的任务,但到目前为止我在这里看到的所有答案都将接受与您的数字格式不匹配的输入,例如 ,111 , 9,9,9 ,甚至 .,,. .即使数字嵌入在其他文本中,这也很容易修复。恕我直言,任何未能拉动 1,234.56 和 1234 的东西— 只有那些数字 ——出abc22 1,234.56 9.9.9.9 def 1234是错误的答案。
首先,如果您不需要在一个正则表达式中完成所有这些操作,请不要这样做。两种不同数字格式的单个正则表达式即使没有嵌入其他文本也很难维护。您真正应该做的是在空白处拆分整个内容,然后对结果运行两个或三个较小的正则表达式。如果这不是您的选择,请继续阅读。
基本模式
考虑到您给出的示例,这里有一个简单的正则表达式,它几乎允许 0000 中的任何整数或小数。格式化并阻止其他所有内容:
^\d*\.?\d+$
这是一个需要 0,000格式:
^\d{1,3}(,\d{3})*(\.\d+)?$
把它们放在一起,只要它们是一致的,逗号就成为可选的:
^(\d*\.?\d+|\d{1,3}(,\d{3})*(\.\d+)?)$
嵌入数字
上面的模式要求整个输入都是一个数字。您正在寻找嵌入在文本中的数字,因此您必须松开该部分。另一方面,您不希望它看到 catch22并认为它找到了数字 22。如果您正在使用具有后向支持的东西(如 .NET),这很容易:替换 ^(?<!\S)$(?!\S)你很高兴去:
(?<!\S)(\d*\.?\d+|\d{1,3}(,\d{3})*(\.\d+)?)(?!\S)
如果您正在使用 JavaScript 或 Ruby 或其他东西,事情开始看起来更复杂:
(?:^|\s)(\d*\.?\d+|\d{1,3}(?:,\d{3})*(?:\.\d+)?)(?!\S)
您必须使用捕获组;如果没有后视支持,我想不出替代方案。您想要的号码将在第 1 组(假设整场比赛是第 0 组)。
验证和更复杂的规则
我认为这涵盖了您的问题,因此,如果您只需要这些,请立即停止阅读。如果你想变得更漂亮,事情很快就会变得非常复杂。根据您的情况,您可能想要阻止以下任何或所有内容:
  • 空输入
  • 前导零(例如 000123)
  • 尾随零(例如 1.2340000)
  • 以小数点开头的小数(例如 .001 而不是 0.001)

  • 顺便说一句,假设您想阻止前 3 个,但允许最后一个。你应该怎么做?我会告诉你你应该做什么,你应该为每个规则使用不同的正则表达式,并逐渐缩小你的匹配范围。但为了挑战,以下是您如何以一种巨大的模式完成所有工作:
    (?<!\S)(?=.)(0|([1-9](\d*|\d{0,2}(,\d{3})*)))?(\.\d*[1-9])?(?!\S)
    
    这就是它的意思:
    (?<!\S) to (?!\S) #The whole match must be surrounded by either whitespace or line boundaries. So if you see something bogus like :;:9.:, ignore the 9.
    (?=.)             #The whole thing can't be blank.
    
    (                    #Rules for the integer part:
      0                  #1. The integer part could just be 0...
      |                  #
      [1-9]              #   ...otherwise, it can't have leading zeroes.
      (                  #
        \d*              #2. It could use no commas at all...
        |                #
        \d{0,2}(,\d{3})* #   ...or it could be comma-separated groups of 3 digits each.
      )                  # 
    )?                   #3. Or there could be no integer part at all.
    
    (       #Rules for the decimal part:
      \.    #1. It must start with a decimal point...
      \d*   #2. ...followed by a string of numeric digits only.
      [1-9] #3. It can't be just the decimal point, and it can't end in 0.
    )?      #4. The whole decimal part is also optional. Remember, we checked at the beginning to make sure the whole thing wasn't blank.
    
    在这里测试:http://rextester.com/YPG96786
    这将允许以下内容:
    100,000
    999.999
    90.0009
    1,000,023.999
    0.111
    .111
    0
    
    它会阻止以下内容:
    1,1,1.111
    000,001.111
    999.
    0.
    111.110000
    1.1.1.111
    9.909,888
    
    有几种方法可以使这个正则表达式更简单和更短,但要明白改变模式会放松它认为的数字。
    由于许多正则表达式引擎(例如 JavaScript 和 Ruby)不支持负回顾,因此正确执行此操作的唯一方法是使用捕获组:
    (:?^|\s)(?=.)((?:0|(?:[1-9](?:\d*|\d{0,2}(?:,\d{3})*)))?(?:\.\d*[1-9])?)(?!\S)
    
    您要查找的数字将在捕获组 1 中。
    在这里测试:http://rubular.com/r/3HCSkndzhT
    最后一点
    显然,这是一个庞大的、复杂的、几乎不可读的正则表达式。我很喜欢这个挑战,但您应该考虑是否真的想在生产环境中使用它。与其尝试一步完成所有事情,您还可以分两步完成:一个正则表达式来捕捉任何可能是数字的东西,然后另一个来清除任何不是数字的东西。或者您可以进行一些基本处理,然后使用您语言的内置数字解析函数。你的选择。

    关于regex - 正则表达式匹配文本中带或不带逗号和小数的数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5917082/

    相关文章:

    正则表达式替换字符串中至少 5 位数字

    javascript - 使用字符串中的键/值对创建对象?

    javascript - 正则表达式字符串替换,同时保留原始换行符

    java - 正则表达式 for/someChars1@someChars2BOT

    java - Jmeter CPU 使用率未超过 25%

    javascript - JS RegEx 找不到表达式

    python - 正则表达式问题组名称重新定义?

    正则表达式搜索批量替换

    regex - 从 Jmeter 的站点地图中替换 ${url} 的一部分

    c# - 用于格式化单词的正则表达式