首页 > 解决方案 > 正则表达式:删除带有一个或两个字母的字符串之间的空格

问题描述

考虑下表第一列中显示的以下原始字符串:

Original String                  Parsed String                   Desired String
'W. & J. JOHNSON LMT.COM'       #W    J  JOHNSON LIMITED        #WJ JOHNSON LIMITED
'NORTH ROOF & WORKS CO. LTD.'   #NORTH ROOF   WORKS CO  LTD     #NORTH ROOF WORKS CO LTD
'DAVID DOE & CO., LIMITED'      #DAVID DOE   CO   LIMITED       #DAVID DOE CO LIMITED
'GEORGE TV & APPLIANCE LTD.'    #GEORGE TV   APPLIANCE LTD      #GEORGE TV APPLIANCE LTD 
'LOVE BROS. & OTHERS LTD.'      #LOVE BROS    OTHERS LTD        #LOVE BROS OTHERS LTD
'A. B. & MICHAEL CLEAN CO. LTD.'#A  B    MICHAEL CLEAN CO  LTD  #AB MICHAEL CLEAN CO LTD
'C.M. & B.B. CLEANER INC.'      #C M    B B  CLEANER INC        #CMBB CLEANER INC

标点符号需要被删除,我做了如下:

def transform(word):
    word = re.sub(r'(?<=[A-Za-z])\'(?=[A-Za-z])[A-Z]|[^\w\s]|(.com|COM)',' ',word)

但是,我无法得到最后一点。删除标点符号后,我得到了很多空格。我怎样才能有一个正则表达式将首字母组合在一起并为常规单词保留单个空格(无首字母)?

这是替代上述字符以获得所需字符串的一种不好的方法吗?

谢谢你让我继续学习 :)

标签: pythonregexetlregex-lookarounds

解决方案


仅在正则表达式中执行此操作并不漂亮,也不是最好的解决方案,但是,就在这里!你最好做一个多步骤的方法。我所做的是确定所有可能的情况,并选择找到没有替换字符串的解决方案,因为您并不总是用空格替换字符。


规则

  1. 非“堆叠”缩写
    • 这些是类似A. B.or的位置W. & J.,但不是C.M. & B.B.
    • 我已将这些标识为A.前后存在缩写部分(例如 )的位置,但后者后面没有另一个字母字符
  2. 前面的空格
    • 这些位置在您的文本中不存在,但如果非字母字符前面有一个空格,后面没有空格(比如在行尾),则可能存在这些位置
    • 在这些情况下,我们匹配第一个空格之后的字符
  3. 进行空间
    • 这些是像&和点一样的位置J.
    • 在这些示例中,我们匹配最后一个空格之前的字符
  4. 没有空间
    • 这些位置类似于'LOVE(该字符串中的撇号)
    • 我们只匹配非字母非空白字符

正则表达式

完成此操作的多合一正则表达式如下:

请参阅此处使用的正则表达式

(?<=\b[a-z])[^a-z]+(?=[a-z]\b(?![^a-z][a-z]))|(?<= ) *(?:\.com\b|[^a-z\s]+) *| *(?:\.com\b|[^a-z\s]+) *(?= )|(?<! )(?:\.com\b|[^a-z\s]+)(?! )

工作方式如下(分成每个交替):

  • (?<=\b[a-z])[^a-z]+(?=[a-z]\b(?![^a-z][a-z]))A.匹配andB.但不匹配A.and之间的非字母字符B.B
    • (?<=\b[a-z])正向向后看,确保前面是一个字母字符,并在其左侧声明一个单词边界位置
    • [^a-z]+匹配任何非字母字符一次或多次
    • (?=[a-z]\b(?![^a-z][a-z]))积极的前瞻确保以下存在
      • [a-z]\b匹配任何字母字符并在其右侧声明一个单词边界位置
      • (?![^a-z][a-z])负前瞻确保后面的不是非字母字符后跟字母字符
  • (?<= ) *(?:\.com\b|[^a-z\s]+) *确保前面有一个空格,然后匹配任何空格.com或任何非单词非空白字符一次或多次,然后匹配任何空格
    • (?<= )积极的后视确保空间先于
    • *匹配任意数量的空格
    • (?:\.com\b|[^a-z\s]+)匹配.com并确保后面跟着非单词字符,或匹配任何非单词非空白字符一次或多次
    • *匹配任意数量的空格
  • *(?:\.com\b|[^a-z\s]+) *(?= )匹配任何空格.com或任何非单词非空白字符一次或多次,然后匹配任何空格,然后确保后面有空格
    • 与上一个相同,但不是在开始时正面向后看,而是在结尾处正面向前看
  • (?<! )(?:\.com\b|[^a-z\s]+)(?! )匹配.com或任何非字母非空白字符一次或多次,确保其周围没有空格
    • 与前两个选项相同,但使用负后瞻和负前瞻

代码

在此处查看正在使用的代码

import re

strings = [
    "'W. & J. JOHNSON LMT.COM'",
    "'NORTH ROOF & WORKS CO. LTD.'",
    "'DAVID DOE & CO., LIMITED'",
    "'GEORGE TV & APPLIANCE LTD.'",
    "'LOVE BROS. & OTHERS LTD.'",
    "'A. B. & MICHAEL CLEAN CO. LTD.'",
    "'C.M. & B.B. CLEANER INC.'"
]

r = re.compile(r'(?<=\b[a-z])[^a-z]+(?=[a-z]\b(?![^a-z][a-z]))|(?<= ) *(?:\.com\b|[^a-z\s]+) *| *(?:\.com\b|[^a-z\s]+) *(?= )|(?<! )(?:\.com\b|[^a-z\s]+)(?! )', re.IGNORECASE)

def transform(word):
    return re.sub(r, '', word)

for s in strings:
    print(transform(s))

输出:

WJ JOHNSON LMT
NORTH ROOF WORKS CO LTD
DAVID DOE CO LIMITED
GEORGE TV APPLIANCE LTD
LOVE BROS OTHERS LTD
AB MICHAEL CLEAN CO LTD
CM BB CLEANER INC

编辑

使用回调,您可以扩展此逻辑以包含我的答案下方评论中提到的特殊情况,以匹配特定情况并进行条件替换。

这些特殊情况包括:

  • FONTAINE'SFONTAINE
  • PREMIUM-FIT AUTOPREMIUM FIT AUTO
  • 62325 W.C.62325 WC

我为正则表达式添加了一个新的替代:(\b[\'-]\b(?:[a-z\d] )?)捕获'S-在字母之间(也-S或类似)并使用回调将其替换为空格(如果捕获组存在)。

我仍然建议使用多个正则表达式来完成此操作,但我想表明使用单个模式是可能的。

在此处查看正在使用的代码

import re

strings = [
    "'W. & J. JOHNSON LMT.COM'",
    "'NORTH ROOF & WORKS CO. LTD.'",
    "'DAVID DOE & CO., LIMITED'",
    "'GEORGE TV & APPLIANCE LTD.'",
    "'LOVE BROS. & OTHERS LTD.'",
    "'A. B. & MICHAEL CLEAN CO. LTD.'",
    "'C.M. & B.B. CLEANER INC.'",
    "'FONTAINE'S PREMIUM-FIT AUTO 62325 W.C.'"
]

r = re.compile(r'(?<=\b[a-z\d])[^a-z\d]+(?=[a-z\d]\b(?![^a-z\d][a-z\d]))|(?<= ) *(?:\.com\b|[^a-z\d\s]+) *| *(?:\.com\b|[^a-z\d\s]+) *(?= )|(\b[\'-]\b(?:[a-z\d] )?)|(?<! )(?:\.com\b|[^a-z\d\s]+)(?! )', re.IGNORECASE)

def repl(m):
    return ' ' if m.group(1) else ''

for s in strings:
    print(r.sub(repl, s))

推荐阅读