Python 正则表达式在第二组中没有其他匹配项时规避可选的非捕获组

Python regex evading optional non capturing group when there's nothing else to match in the second group

提问人:Haider Khan 提问时间:11/12/2023 最后编辑:Haider Khan 更新时间:11/13/2023 访问量:57

问:

我正在尝试清理一个文件,其中有一些用项目名称写的 uoms,中间没有任何空格。我想出了一个正则表达式来匹配 uom 及其变体,它本身工作正常,但是当分组以捕获项目名称时,它无法给出预期的输出。

import re

uom_regex = 'box(?:es)?|bxs|bag(?:s)?'
test_text = ["box", "boxes", "bag", "bags", "bxs"]

for text in test_text:
    match = re.search(uom_regex, text)
    print(match.group() if match else "No match")

这个正则表达式本身运行良好,并完美地捕获了所有 uom。

但是,当我尝试将相同的正则表达式与其他部分结合使用以在它们到期的地方添加空间时,它工作正常,除非该单词实际上没有任何额外内容(例如以下示例的前 2 种情况

import re

uom_regex = 'box(?:es)?|bxs|bag(?:s)?'
regex = r'({0})([a-zA-Z]+)'.format(uom_regex)

test_strings = ["boxes", "bags", "boxesapple", "boxapple", "bagapple", 'bagsapple']

for test_string in test_strings:
    result = re.sub(regex, r'\1 \2', test_string)
    print(f"Original: {test_string}")
    print(f"Modified: {result}\n")

这是输出。

Original: boxes
Modified: box es

Original: bags
Modified: bag s

Original: boxesapple
Modified: boxes apple

Original: boxapple
Modified: box apple

Original: bagapple
Modified: bag apple

Original: bagsapple
Modified: bags apple

然而,前 2 个输出应该是这样的。

Original: boxes
Modified: boxes

Original: bags
Modified: bags
python-3.x 正则表达式

评论

0赞 AdrianHHH 11/12/2023
编辑问题以显示每个考试密钥所需的输出。

答:

0赞 Wiktor Stribiżew 11/12/2023 #1

您可以使用正则表达式,例如

(?=(box(?:es)?|bxs|bags?))\1([a-zA-Z]+)

请参阅正则表达式演示

注意:如果您使用的是 Python 3.11 及更高版本,其中模块支持所有格量词/原子组,则可以使用 中的原始替换模式。re(box(?:es)?+|bxs|bags?+)([a-zA-Z]+)re.sub

细节

  • (?=(box(?:es)?|bxs|bags?))- 积极的前瞻,捕获到第 1 组 、 、 或boxboxesbxsbagbags
  • \1- 与第 1 组中捕获的文本相同
  • ([a-zA-Z]+)- 第 2 组:任何一个或多个 ASCII 字母。

以下是如何在代码中使用它:

import re
 
uom_regex = 'box(?:es)?|bxs|bags?'
regex = r'(?=({0}))\1([a-zA-Z]+)'.format(uom_regex)
 
test_strings = ["boxes", "bags", "boxesapple", "boxapple", "bagapple", 'bagsapple']
 
for test_string in test_strings:
    result = re.sub(regex, r'\1 \2', test_string)
    print(f"Original: {test_string}")
    print(f"Modified: {result}\n")

观看 Python 演示

输出:

Original: boxes
Modified: boxes

Original: bags
Modified: bags

Original: boxesapple
Modified: boxes apple

Original: boxapple
Modified: box apple

Original: bagapple
Modified: bag apple

Original: bagsapple
Modified: bags apple
0赞 Cary Swoveland 11/13/2023 #2

如果使用 Python 的 PyPI 正则表达式模块(类似于 PCRE),则可以将以下正则表达式的匹配项替换为空格。

\b(?:box(?:(?!es)|es)|bag(?:(?!s)|s)|bxs)\K(?=[a-zA-Z])

演示

此表达式具有以下元素。

\b           # match a word boundary
(?:          # begin (outer) non-capture group
  box        # match literal
  (?:        # begin non-capture group
    (?!es)   # negative lookahead asserts next two chars are not 'es'
  |          # or
    es       # match literal
  )          # end non-capture group
|            # or
  bag        # match literal
  (?:        # begin non-capture group
    (?!s)    # negative lookahead asserts next char is not 's'
  |          # or
    s        # match literal
  )          # end non-capture group
|            # or
  bxs        # match literal
)            # end (outer) non-capture group
\K           # reset start of match and discard previously-consumed chars
(?=[a-zA-Z]) # positive lookahead asserts next char is a letter