【问题标题】:re.sub after matching. all instances of a repeated matching group, pythonre.sub 匹配后。重复匹配组的所有实例,python
【发布时间】: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.matchre.iter 或任何其他用于查找数字模式的方法之后,有没有一种简单的方法来执行re.sub

@abarnert 给了我正确的答案 - 使用 lambda 函数。我在@abarnert's answer 下的评论,以“已验证!”开头显示所有步骤。以防万一该评论导致链接失效,


我的尝试

顺便说一句,我在 SO(replace portion of matchextract part of a matchreplace after matching patternrepeated 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


【解决方案1】:

我看不出有什么理由你不能在这里只使用re.sub 而不是re.finditer。您的 repl 每次匹配都会应用一次,并返回将 repl 替换为 string 中的每个 pattern 的结果,这正是您想要的。

我实际上无法运行您的示例,因为复制和粘贴 test 会给我一个 SyntaxError,复制和粘贴 ANY_NUMBER_SRCH 给我一个编译正则表达式的错误,我不想让兔子失望试图修复所有错误的漏洞,其中大多数甚至可能不在您的真实代码中。所以让我举一个更简单的例子:

>>> test = '3,254,236,948,348.884423 cold things and 8d523c'
>>> pattern = re.compile(r'[\d,]+')
>>> pattern.findall(test) # just to verify that it works
['3,254,236,948,348', '884423', '8', '523']
>>> pattern.sub(lambda match: match.group().replace(',', ''), test)
'3254236948348.884423 cold things and 8d523c'

显然,您的 repl 函数将比仅删除所有逗号更复杂一些 - 您可能希望将 def 脱节而不是将其塞进 lambda .但无论您的规则是什么,如果您将其编写为一个函数,该函数接受一个 match 对象并返回您想要的字符串来代替该匹配对象,您可以将该函数传递给 sub

【讨论】:

  • 我很抱歉没有仔细检查我的问题。我试图让test 字符串中的东西看起来不错,但它使它无法复制/粘贴。我已经做到了,所以它可以在我的机器上运行,希望也可以在其他机器上运行。当我从 regex101.com 复制/粘贴时,我在“&lt;three_digits&gt;”之前错过了“P”——我添加了命名组以试图澄清事情。感谢您“阅读”这些错误并为我提供所需的答案!
  • 已验证! #ANY_NUMBER_SRCH as in question; ; #test as in question; ; &gt;&gt;&gt;pattern=re.compile(ANY_NUMBER_SRCH); ; test_corrected=pattern.sub(lambda match: match.group().replace(',', '').replace(' ', ''), test); ; &gt;&gt;&gt;test_corrected; #result# '3254236948348.884423 cold things, 123242 falling birds, .84973 of a French pen , 65243 turtle gloves, 8001457.2328009 units, and 8d523c.' ;根据需要。
猜你喜欢
  • 2019-06-08
  • 2015-12-18
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 2014-08-24
相关资源
最近更新 更多