【问题标题】:Replace named captured groups with arbitrary values in Python在 Python 中用任意值替换命名的捕获组
【发布时间】:2018-05-03 23:00:24
【问题描述】:

我需要用某个任意值替换正则表达式的捕获组内的值;我看过re.sub,但它似乎以不同的方式工作。

我有一个这样的字符串:

s = 'monthday=1, month=5, year=2018'

我有一个正则表达式与捕获的组匹配,如下所示:

regex = re.compile('monthday=(?P<d>\d{1,2}), month=(?P<m>\d{1,2}), year=(?P<Y>20\d{2})')

现在我想将名为 d 的组替换为 aaa,将名为 m 的组替换为 bbb,并将名为 Y 的组替换使用ccc,如下例所示:

'monthday=aaa, month=bbb, year=ccc'

基本上我想保留所有不匹配的字符串并用一些任意值替换匹配组。

有没有办法达到预期的效果?

注意

这只是一个例子,我可以有其他结构不同但名称相同的输入正则表达式捕获组...

更新

由于似乎大多数人都在关注样本数据,所以我添加了另一个样本,假设我有这个其他输入数据和正则表达式:

input = '2018-12-12'
regex = '((?P<Y>20\d{2})-(?P<m>[0-1]?\d)-(?P<d>\d{2}))'

如您所见,我仍然有相同数量的捕获组 (3),并且它们的命名方式相同,但结构完全不同......我需要的是和之前一样用一些任意替换捕获组文字:

'ccc-bbb-aaa'

将名为Y 的捕获组替换为ccc,将名为m 的捕获组替换为bbb,将名为d 的捕获组替换为aaa

在这种情况下,正则表达式并不是这项工作的最佳工具,我愿意接受其他一些可以实现我的目标的建议。

【问题讨论】:

  • regex.sub('monthday=aaa, month=bbb, year=ccc', s)
  • @Rawing 使用您的解决方案我需要对新结果进行硬编码,但这不是我想要的......我想用一些任意值替换匹配组。这只是一个示例,我可以使用其他结构不同但名称相同的输入正则表达式捕获组...
  • @Rawing 阅读了问题的第一行:“我需要用一些任意值替换正则表达式的捕获组内的值”,这不是您的解决方案实际在做的事情.. .
  • @Rawing 输入正则表达式和输入文本可能会改变,固定的是我需要用其他一些数据替换的捕获组的名称,如果你愿意,我可以再添加十几个示例数据具有不同结构但相同数量和命名的捕获组...
  • @RomanPerekhrest 我已更新问题以使其更清晰。

标签: python regex python-2.7


【解决方案1】:

这是对正则表达式的完全向后使用。捕获组的重点是保存您想要保留的文本,而不是您想要替换的文本。

由于您以错误的方式编写正则表达式,因此您必须手动执行大部分替换操作:

"""
Replaces the text captured by named groups.
"""
def replace_groups(pattern, string, replacements):
    pattern = re.compile(pattern)
    # create a dict of {group_index: group_name} for use later
    groupnames = {index: name for name, index in pattern.groupindex.items()}

    def repl(match):
        # we have to split the matched text into chunks we want to keep and
        # chunks we want to replace
        # captured text will be replaced. uncaptured text will be kept.
        text = match.group()
        chunks = []
        lastindex = 0
        for i in range(1, pattern.groups+1):
            groupname = groupnames.get(i)
            if groupname not in replacements:
                continue

            # keep the text between this match and the last
            chunks.append(text[lastindex:match.start(i)])
            # then instead of the captured text, insert the replacement text for this group
            chunks.append(replacements[groupname])
            lastindex = match.end(i)
        chunks.append(text[lastindex:])
        # join all the junks to obtain the final string with replacements
        return ''.join(chunks)

    # for each occurence call our custom replacement function
    return re.sub(pattern, repl, string)
>>> replace_groups(pattern, s, {'d': 'aaa', 'm': 'bbb', 'Y': 'ccc'})
'monthday=aaa, month=bbb, year=ccc'

【讨论】:

  • +1 for This is a completely backwards use of regex. The point of capture groups is to hold text you want to keep, not text you want to replace. 这也解决了我的心理模型和问题。
【解决方案2】:

您可以使用带有正则表达式替换的字符串格式:

import re
s = 'monthday=1, month=5, year=2018'
s = re.sub('(?<=\=)\d+', '{}', s).format(*['aaa', 'bbb', 'ccc'])

输出:

'monthday=aaa, month=bbb, year=ccc'

编辑:给定任意输入字符串和正则表达式,您可以使用如下格式:

input = '2018-12-12'
regex = '((?P<Y>20\d{2})-(?P<m>[0-1]?\d)-(?P<d>\d{2}))'
new_s = re.sub(regex, '{}', input).format(*["aaa", "bbb", "ccc"])

【讨论】:

  • 这似乎与位置有关...如果输入和相对正则表达式更改为这种格式怎么办:year=2018, monthday=1, month=5?正如已经写过的,不要太在意样本数据,问题的要求是:“我需要用一些任意值替换正则表达式的捕获组内的值”。建议的解决方法似乎没有这样做......
  • 查看更新后的答案,该答案应阐明我的需求以及问题的真正含义。谢谢。
  • @aleroot 您发布的输入和您匹配的组似乎是相当随意的。我建议你从模板的角度来解决这个问题。
【解决方案3】:

Python 3.x 扩展示例解决方案(re.sub() with replacement 功能):

import re

d = {'d':'aaa', 'm':'bbb', 'Y':'ccc'}  # predefined dict of replace words
pat = re.compile('(monthday=)(?P<d>\d{1,2})|(month=)(?P<m>\d{1,2})|(year=)(?P<Y>20\d{2})')

def repl(m):
    pair = next(t for t in m.groupdict().items() if t[1])
    k = next(filter(None, m.groups()))  # preceding `key` for currently replaced sequence (i.e. 'monthday=' or 'month=' or 'year=')
    return k + d.get(pair[0], '')

s = 'Data: year=2018, monthday=1, month=5, some other text'
result = pat.sub(repl, s)

print(result)

输出:

Data: year=ccc, monthday=aaa, month=bbb, some other text

对于 Python 2.7: 将k = next(filter(None, m.groups())) 行更改为:

k = filter(None, m.groups())[0]

【讨论】:

  • k = next(filter(None, m.groups())) # 前面key 用于当前替换的序列(即'monthday=' or 'month=' or 'year=') TypeError :元组对象不是我在 python 2.7 上的迭代器
  • 我在 python 2.7 上,有没有办法让它在 2.7 上工作?我当时无法升级。
【解决方案4】:

我建议你使用循环

import re
regex = re.compile('monthday=(?P<d>\d{1,2}), month=(?P<m>\d{1,2}), year=(?P<Y>20\d{2})')
s = 'monthday=1, month=1, year=2017   \n'
s+= 'monthday=2, month=2, year=2019'


regex_as_str =  'monthday={d}, month={m}, year={Y}'
matches = [match.groupdict() for match in regex.finditer(s)]
for match in matches:
    s = s.replace(
        regex_as_str.format(**match),
        regex_as_str.format(**{'d': 'aaa', 'm': 'bbb', 'Y': 'ccc'})
    )    

您可以使用不同的正则表达式模式多次执行此操作

或者您可以将两种模式连接(“或”)在一起

【讨论】:

    猜你喜欢
    • 2015-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多