【发布时间】:2018-08-01 18:56:26
【问题描述】:
我正在使用 Python 中的正则表达式匹配 str 中的数字。我的愿望是捕获可能有千位分隔符(对我来说是逗号或空格)或可能只是一串数字的数字。以下显示了我的正则表达式捕获的内容
>>> import re
>>> test = '3,254,236,948,348.884423 cold things, ' + \
'123,242 falling birds, .84973 of a French pen , ' + \
'65 243 turtle gloves, 8 001 457.2328009 units, and ' + \
'8d523c.'
>>> matches = re.finditer(ANY_NUMBER_SRCH, test, flags=re.MULTILINE)
>>> for match in matches:
... print (str(match))
...
<_sre.SRE_Match object; span=(0, 24), match='3,254,236,948,348.884423'>
<_sre.SRE_Match object; span=(27, 34), match='123,242'>
<_sre.SRE_Match object; span=(37, 43), match='.84973'>
<_sre.SRE_Match object; span=(46, 52), match='65 243'>
<_sre.SRE_Match object; span=(55, 72), match='8 001 457.2328009'>
<_sre.SRE_Match object; span=(73, 74), match='8'>
<_sre.SRE_Match object; span=(75, 78), match='523'>
这是我想要的匹配行为。现在,我想获取每个匹配的数字并删除千位分隔符(',' 或 ' ')(如果存在)。这应该留给我
'3254236948348.884423 cold things, ' + \
'123242 falling birds, .84973 of a French pen ,' + \
'65243 turtle gloves, 8001457.2328009 units, ' + \
'and 8d523c.'
基本上,我有一个正则表达式来捕获数字。此正则表达式用于多个地方,例如查找美元金额,获取序数,...因此,我将正则表达式命名为 ANY_NUMBER_SRCH。
我想要做的事情如下:
matches = some_method_to_get_all_matches(ANY_NUMBER_SRCH)
for match in matches:
corrected_match = re.sub(r"[, ]", "", match)
change_match_to_corrected_match_in_the_test_string
事实上,我不能使用替换组。如果只是想看正则表达式,可以查看https://regex101.com/r/AzChEE/3 基本上我的部分正则表达式如下
r"(?P<whole_number_w_thous_sep>(?P<first_group>\d{1,3})(?P<thousands_separator>[ ,])(?P<three_digits_w_sep>(?P<three_digits>\d{3})(?P=thousands_separator))*(?P<last_group_of_three>\d{3})(?!\d)"
我将在没有“滚动线”的情况下表示:
(r"(?P<whole_number_w_thous_sep>(?P<first_group>\d{1,3})"
"(?P<thousands_separator>[ ,])"
"(?P<three_digits_w_sep>(?P<three_digits>\d{3})"
"(?P=thousands_separator))*"
"(?P<last_group_of_three>\d{3})(?!\d)")
正则表达式引擎不会保留重复的 three_digits_with_separator,因为 * 用于重复捕获组。
我确信有一种方法可以使用 _sre.SRE_Match objects 的 span 部分。但是,这将非常复杂,并且我正在处理具有数千到数十万个字符的字符串。 在re.match 或re.iter 或任何其他用于查找数字模式的方法之后,有没有一种简单的方法来执行re.sub?
@abarnert 给了我正确的答案 - 使用 lambda 函数。我在@abarnert's answer 下的评论,以“已验证!”开头显示所有步骤。以防万一该评论导致链接失效,。
我的尝试
顺便说一句,我在 SO(replace portion of match、extract part of a match、replace after matching pattern、repeated capturing group stuff)上查看了这些问题,但它们只是展示了如何使用替换组。我也尝试使用re.finditer,如下所示。
>>> matches = re.finditer(lib_re.ANY_NUMBER_SRCH, test, flags=re.MULTILINE)
>>> for match in matches:
... print ("match: " + str(match))
... corrected_match = re.sub(r"[, ]", "", match)
... print ("corrected_match: " + str(corrected_match))
...
match: <_sre.SRE_Match object; span=(0, 24), match='3,254,236,948,348.884423'>
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "/usr/lib/python3.6/re.py", line 191, in sub
return _compile(pattern, flags).sub(repl, string, count)
TypeError: expected string or bytes-like object
>>> print ("corrected_match: " + str(corrected_match))
大正则表达式
如果regex101.com link 出现问题,这里是巨大的正则表达式:
ANY_NUMBER_SRCH = r"(?P<number_capture>(?P<pre1>(?<![^0-9,.+-])|)(?P<number>(?P<sign_symbol_opt1>(?<![0-9])[+-])?(?P<whole_number_w_thous_sep>(?P<first_group>\d{1,3})(?P<thousands_separator>[ ,])(?P<three_digits_w_sep>(?P<three_digits>\d{3})(?P=thousands_separator))*(?P<last_group_of_three>\d{3})(?!\d)|(?P<whole_number_w_o_thous_sep>\d+))(?P<decimal_separator_1>[.])?(?P<fractional_w_whole_before>(?<=[.])(?P<digits_after_decimal_sep_1>\d+))?(?P<post1>(?<![^0-9,.+-])|)|(?P<pre2>(?<![^0-9,.+-])|)(?P<fractional_without_whole_before>(?P<sign_symbol_opt2>(?<![0-9])[+-])?(?P<decimal_separator_2>[.])(?P<digits_after_decimal_sep_2>\d+)))(?P<post2>(?<![^0-9,.+-])|))"
【问题讨论】:
-
同时,您希望
re.sub(r"[, ]", "", match)做什么?您只能在字符串上调用它,而不是匹配对象。而且,即使您解决了这个问题,一旦您拥有corrected_match,您将如何处理它?显然,您为创建新字符串所做的任何事情都不会以任何方式影响test。 -
我没想到
re.sub可以在match上工作。我不确定如何在那里进行替换——这是我的问题,@abarnert。我不知道如何从比赛中得到这些东西。您对 lambda 函数的回答正是我想要的。非常感谢您回答了一个不完美的问题。你所做的确实会影响测试,这正是我想要的。 -
好吧,它允许我影响测试。 @abarnert,我接受了你的回答。我现在正在尝试实现它来替换数字中的空格,例如“8 001 457.2328009”,它应该转到“8001457.2328009”。
标签: python regex string replace string-matching