【问题标题】:Use regex to contextually replace dots in a string使用正则表达式根据上下文替换字符串中的点
【发布时间】:2021-01-09 01:27:20
【问题描述】:

我想删除所有由单个字符分隔的点,我想用空格替换所有由多个连续字符分隔的点(如果一侧有 len > 1字符)。

例如。给定一个字符串,

s = ' A.B.C.D.E. FGH.IJ K.L.M.NO PQ.R.S T.U.VWXYZ'

处理后的输出应如下所示:

'ABCDE FGH IJ KLM NO PQ RS TU VWXYZ'

  • 请注意,在 A.B.C.D.E. 的情况下,所有点都被删除(当没有尾随点时也是如此)
  • 请注意,在K.L.M.NO 的情况下,前两个点被删除,最后一个被替换为空格(因为 NO 不是单个字符)
  • 请注意,在 PQ.R.S 的情况下,第一个点被替换为空格,第二个点被删除。

几乎有一个可行的解决方案:

re.sub(r'(?<!\w)([A-Z])\.', r'\1', s)

但在给出的示例中,T.U.VWXYZ 被转换为 TUVWXYZ,而它应该是 TU VWXYZ

注意:使用单个正则表达式解决此问题很重要,甚至根本就没有正则表达式。

编辑:在示例字符串中将PQ.RS 更改为PQ.R.S

【问题讨论】:

  • 您对非正则表达式解决方案满意吗?
  • 是的,它不一定是正则表达式

标签: python regex


【解决方案1】:

我会采取两个步骤。

  1. (\b[A-Z])\.(?=[A-Z]\b|\s|$) 替换为r'\1'
  2. (\b[A-Z]{2,})\.(?=[A-Z])|(\b[A-Z])\.(?=[A-Z]{2,}) 替换为r'\1\2 '

样本

重新导入

re1 = re.compile(r'(\b[A-Z])\.(?=[A-Z]\b|\s|$)')
re2 = re.compile(r'(\b[A-Z]{2,})\.(?=[A-Z])|(\b[A-Z])\.(?=[A-Z]{2,})')

s = ' A.B.C.D.E. FGH.IJ K.L.M.NO PQ.RS T.U.VWXYZ'

r = re2.sub(r'\1\2 ', re1.sub(r'\1', s)).strip()
print(r)

输出

'ABCDE FGH IJ KLM NO PQ RS TU VWXYZ'

符合您想要的结果:

'ABCDE FGH IJ KLM NO PQ RS TU VWXYZ'
  • re1 匹配前面有一个独立字母,后跟另一个独立字母、空格或字符串结尾的所有点。
  • re2 匹配前面至少有 2 且后面至少有 1 个字母(或相反)的所有点

【讨论】:

    【解决方案2】:

    您可以先用空格替换所有点后跟两个字符,然后删除剩余的点:

    re.sub(r'\.([A-Z]{2})', r' \1', s).replace(".", "")
    

    这会在您的示例中给出" ABCDE FGH IJ KLM NO PQ RS TU VWXYZ"

    【讨论】:

    • 非常感谢!虽然我相信这会因s = ' PQ.R.S 的情况而中断,这将给出PQRS 而不是预期的PQ RS
    • 我不确定您的规则是什么,但您可以例如使用re.sub(r'([A-Z]{2})\.', r' \1 ', s) 进行第二遍(在删除点之前),它将前面有两个字符的点替换为空格。
    【解决方案3】:

    希望这更整洁:

    import re
    
    s = ' A.B.C.D.E. FGH.IJ K.L.M.NO PQ.RS T.U.VWXYZ'
    
    s = re.sub(r"\.(\w{2})", r" \1", s)
    s = re.sub(r"(\w{2})\.(\w)", r"\1 \2", s)
    s = re.sub(r"\.", "",s)
    s = s.strip()
    print(s)
    

    【讨论】:

    • 非常感谢!虽然我相信这对于s = ' PQ.R.S 的情况会中断,这将给出PQRS 而不是预期的PQ RS。我更新了示例以包含此案例。
    • 刚刚注意到您对另一个答案的回复。从你的问题来看并不明显。更新
    【解决方案4】:

    如果您考虑使用动态替换,则可以使用单个正则表达式解决方案:

    import re
    rx = r'\b([A-Z](?:\.[A-Z])+\b(?:\.(?![A-Z]))?)|\.'
    s = ' A.B.C.D.E. FGH.IJ K.L.M.NO PQ.R.S T.U.VWXYZ'
    print( re.sub(rx, lambda x: x.group(1).replace('.', '') if x.group(1) else ' ', s.strip()) )
    # => ABCDE FGH IJ KLM NO PQ RS TU VWXYZ
    

    查看Python demoregex demo

    正则表达式匹配:

    • \b([A-Z](?:\.[A-Z])+\b(?:\.(?![A-Z]))?) - 一个单词边界,然后是第 1 组(在剥离所有句点后将被其自身替换)捕获:
      • [A-Z] - 一个大写的 ASCII 字母
      • (?:\.[A-Z])+ - 零个或多个点和大写 ASCII 字母的序列
      • \b - 字边界
      • (?:\.(?![A-Z]))? - . 的可选序列,后面不跟大写 ASCII 字母
    • | - 或
    • \. - 任何其他上下文中的 .(它将被替换为空格)。

    lambda x: x.group(1).replace('.', '') if x.group(1) else ' ' 替换表示如果 Group 1 匹配,替换字符串是 Group 1 不带点的值,如果 Group 1 不匹配,替换是单个正则空格。

    【讨论】:

      猜你喜欢
      • 2013-10-24
      • 2022-11-23
      • 2014-09-01
      • 2015-02-27
      • 1970-01-01
      • 1970-01-01
      • 2018-07-13
      相关资源
      最近更新 更多