正则表达式从包含数字的字符串中提取数字

Regex to extract number from a string with numbers

提问人:Henley Wing Chiu 提问时间:9/6/2023 最后编辑:MarkHenley Wing Chiu 更新时间:9/9/2023 访问量:101

问:

我想要一个正则表达式从至少有一个数字的字符串中提取第一个数字(假设 . 代表十进制,而 , 代表分隔千位)

examples  = ["I earned $100,000", "I earned $100000", "I earned $100000.05"]
desired_output =  ["100000", "100000", "100000.05"]

这是我尝试过的正则表达式:

regex = /((\d{1,3}(?:,\d{3})*)(?:\.(\d{0,2}))?)/

但是,对于字符串“I earned $100000”,它提取“100”,而不是“100000”。

regex.match("I earned $100000") #returns 100

如何修改此正则表达式?

正则表达式 Ruby

评论

3赞 Al Sweigart 9/6/2023
请记住,在欧洲,数字 1,000,000.5 将写成 1.000.000,5,因为千位分隔符是一个句点,小数点是一个逗号。
0赞 Henley Wing Chiu 9/6/2023
英国也是这样吗?
0赞 Mark 9/6/2023
@Henley不,不是
0赞 Mark 9/6/2023
试试这个 regex101.com/r/81DpzM/1
1赞 Mark 9/6/2023
@CarySwoveland大胆地假设 OP 不希望它匹配该:P

答:

0赞 Chiến Lê 9/6/2023 #1

您应该首先匹配整个文本,然后删除不需要的分隔符。 模式:["100,000","100000","100000.5"][\d\,\.]+

您的正则表达式模式仅匹配 100,因为您将 000 保留在未捕获的组中

0赞 Rajagopalan 9/6/2023 #2
examples = ["I earned $100,000", "I earned $100000", "I earned $100000.05"]

法典

p examples.map { |string| /\$([\d,.]+)/.match(string)[1] }

输出

["100,000", "100000", "100000.05"]

评论

0赞 Cary Swoveland 9/6/2023
也许。然而,两者都匹配,这可能是不想要的。{ |string| string[/\$([\d,.]+)/] }',,,,,,,'
0赞 mechnicov 9/6/2023
应该是 ,不是100000100,000
0赞 The fourth bird 9/6/2023 #3

您可以附加单词边界以防止部分单词匹配,而是匹配 1+ 数字,如果您不需要捕获组,可以省略它们:

\b\d+(?:,\d{3})*(?:\.\d{0,2})?\b

正则表达式演示

评论

0赞 Cary Swoveland 9/6/2023
例如,您可能希望确保第一个数字不是零,假设不匹配。'007'
0赞 The fourth bird 9/6/2023
@CarySwoveland 嗨,一切都好吗?我认为您可以为这种情况使用捕获组 regex101.com/r/2gr4wP/1\b0*(\d+(?:,\d{3})*(?:\.\d{0,2})?)\b
1赞 Cary Swoveland 9/7/2023
#4,我更多地沿着 .我很高兴地报告,这里一切都很好,尽管上周六是令人沮丧的一天,因为我掷了零并成为了八旬老人。回想起来,我的第二个生日特别令人难忘,因为每个人都在外面庆祝。甚至还有游行。\b[1-9](?:\d{0,2}(?:,\d{3})*|\d*)(?:\.\d{2})?\b
1赞 The fourth bird 9/7/2023
@CarySwoveland 很高兴听到你仍然年轻的心,摇摆着一些 Ruby 和 Regex。这个月我就要四十多岁了。没有派对,但我是蛋糕的傻瓜:-)
0赞 Todd A. Jacobs 9/9/2023
@CarySwoveland我不确定它对 OP 的示例是否重要,但可能完全适用于各种用例。例如,零填充和小数精度比您在会计中经常看到的情况要高。这就是为什么我宁愿在 OP 的情况下以未捕获的货币符号而不是数字开始比赛,因为你是对的:它本身非常模棱两可。$007.122007
0赞 Todd A. Jacobs 9/9/2023 #4

提取美元值,然后验证和转换

假设您始终使用美元而不是其他货币,即使您有一个空的 String 或多个设置的货币值,以下操作也有效。

examples = [
  "I earned $100,000", "I earned $100000",
  "I earned $100000.05", "", 
  "I earned $2.50, which is half of $5.00"
]

examples.map { _1.scan(/\$(\d[\d,.]+)\b/).first }
  .compact.flatten.map { _1.delete ?, }

#=> ["100000", "100000", "100000.05", "2.50"]

其工作原理是提取所有带有前导美元符号的值,然后操作匹配项。这些步骤包括:

  1. 使用 String#scan 捕获以美元符号为前缀并后跟单词边界的所有数字(包括小数和逗号)。

    • 没有努力验证示例集中的边缘情况,例如显式值、格式不正确的值(如 )或负值(如 或 )。nil$1.00.2-$1.00($5.00)

    • 另外,请注意,前导零或尾随零不一定是错误; 对于某些用例可能完全有效,因此前导零、填充、十进制精度或问题范围之外的其他内容不会得到解决。你可以用更复杂的正则表达式来做很多这样的验证,但我个人认为,你应该在事后验证你的结果,而不是试图在一个单一的正则表达式中完成所有验证,以降低认知负荷。YMMV。$00.00

  2. 如果找到多个美元金额,则仅选择字符串中的第一个美元金额。

  3. 从结果中删除任何值。nil

  4. 展平压缩的结果数组。

  5. 从扁平化的 String 值数组中删除逗号。

当然有更短的解决方案,并且解决方案将更明确地说明什么是有效的美元金额,但对我来说,扫描只是在概念上似乎更简单。这样一来,您就可以将对格式设置和验证的关注从核心正则表达式中移出。/\$(\d[\d,.]+)\b/

是使用链式方法处理结果集,还是通过对结果调用一系列“清理步骤”来处理结果集,都由您决定。恕我直言,您在正则表达式中所做的工作越少,就越容易调试对您很重要的任何后续转换或验证。