Python:使用正则表达式从字符串解析数值

时间:2014-01-02 04:40:55

标签: python regex

我正在编写python代码,使用正则表达式从字符串中解析不同类型的数字,并遇到了一个我不明白的恼人问题。

我的代码如下:

import re    

test_string = "Distributions $54.00 bob $26 and 0.30 5% ($0.23) 2,333,450"

num_values = re.findall(r"\(?\$?[0-9]+.?[0-9]*%?\)?|[0-9]+(?:,[0-9]+)*", test_string)

输出结果为:

['$54.00', '0.30', '5%', '($0.23)', '2,333', '450']

所以代码对于除了'2,333,450'之外的所有内容都很有用,因为某些原因它被分成两个元素。

令人烦恼的是,当我颠倒正则表达式的顺序时:

num_values = re.findall(r"[0-9]+(?:,[0-9]+)*|\(?\$?[0-9]+.?[0-9]*%?\)?", test_string)

我得到了这个输出:

['$54.00', '$26 ', '0', '30', '5', '($0.23)', '2,333,450']

所以我的'2,333,450'字符串很好但是我无法正确解析带有%符号或小数点的数字(除非它在括号中)。

任何见解都将受到赞赏。

2 个答案:

答案 0 :(得分:2)

首先,我怀疑正则表达式的第一部分中的句点应该使用前导反斜杠进行转义(如果它旨在匹配小数点),目前它匹配任何字符,这就是为什么你有匹配包含a空间'$26 '

2,333因此与正则表达式的第一部分匹配(,与未转义的.匹配),这就是它与{{1}不匹配的原因这个数字的一​​部分。

虽然您的(更正的)正则表达式适用于可能足够好的有限样本数据,但它可能对于一般用途而言过于宽泛 - 例如它与,450匹配。你可以用较小的部分构建一个更大的正则表达式,但这可能会很快变得难看:

($1267.3%

请注意,这并未考虑数字周围的括号,并且由于匹配{import re test_string = "Distributions $54.00 bob $26 and 0.30 5% ($0.23) 2,333,450" test_string += " $12,354.00 43 43.12 1234,12 ($123,456.78" COMMA_SEP_NUMBER = r'\d{1,3}(?:,\d{3})*' # require groups of 3 DECIMAL_NUMBER = r'\d+(?:\.\d*)?' COMMA_SEP_DECIMAL = COMMA_SEP_NUMBER + r'(?:\.(?:\d{3},)*\d{0,3})?' # are commas used after the decimal point? regex_items = [] regex_items.append('\$' + COMMA_SEP_DECIMAL) regex_items.append('\$' + DECIMAL_NUMBER) regex_items.append(COMMA_SEP_DECIMAL + '\%') regex_items.append(DECIMAL_NUMBER + '\%') regex_items.append(COMMA_SEP_DECIMAL) regex_items.append(DECIMAL_NUMBER) r = re.compile('|'.join(regex_items)) print r.findall(test_string) (可能会被解释为两个数字1234,121234),因此无效{1}}针对COMMA_SEP_NUMBER模式。

这是此技术的问题,因为如果DECIMAL_NUMBER模式首先出现,则COMMA_SEP_NUMBER永远不会匹配。

最后,这是一个可视化正则表达式的好工具

12

Regular expression visualization

Debuggex Demo

答案 1 :(得分:1)

如何将两个部分合并为一个?

>>> test_string = "Distributions $54.00 bob $26 and 0.30 5% ($0.23) 2,333,450"
>>> re.findall(r'\(?\$?\d+(?:,\d+)*\.?\d*%?\)?', test_string)
['$54.00', '$26', '0.30', '5%', '($0.23)', '2,333,450']
  • .替换为\.以匹配点字面而非匹配任何字符。
  • [0-9]替换\d。 (\d匹配数字)