Python 中的正则表达式:仅当不在列表中时将单词与数字分开
Regex in Python: Separate words from numbers JUST when not in list
我有一个列表,其中包含我需要保留的一些替代品。例如,替换列表:['1st', '2nd', '10th', '100th', '1st nation', 'xlr8', '5pin', 'h20']
.
一般情况下,包含字母数字字符的字符串需要按如下方式拆分数字和字母:
text = re.sub(r'(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)', ' ', text, 0, re.IGNORECASE)
之前的正则表达式模式通过在以下之间添加 space 成功地将所有数字与字符分开:
Original Regex
ABC10 DEF --> ABC 10 DEF
ABC DEF10 --> ABC DEF 10
ABC 10DEF --> ABC 10 DEF
10ABC DEF --> 10 ABC DEF
但是,替换列表中有一些字母数字单词无法分开。例如,以下包含替换列表一部分的 1ST
的字符串不应被分隔,它们应该被省略而不是添加 space:
Original Regex Expected
1ST DEF 100CD --> 1 ST DEF 100 CD --> 1ST DEF 100 CD
ABC 1ST 100CD --> ABC 1 ST 100 CD --> ABC 1ST 100 CD
100TH DEF 100CD -> 100 TH DEF 100 CD -> 100TH DEF 100 CD
10TH DEF 100CD -> 10 TH DEF 100 CD -> 10TH DEF 100 CD
为了获得上例中的预期列,我尝试在正则表达式中使用 IF THEN ELSE
方法,但我在 Python:
中的语法中遇到错误
(?(?=condition)(then1|then2|then3)|(else1|else2|else3))
根据语法,我应该有如下内容:
?(?!1ST)((?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)))
其中 (?!...)
将包括匹配正则表达式模式时要避免的可能替换,在本例中为词 1ST 10TH 100TH
.
如何避免匹配字符串中的单词替换?
您可以使用 lambda 函数执行此操作,以检查匹配的字符串是否在您的排除列表中:
import re
subs = ['1st','2nd','1st nation','xlr8','5pin','h20']
text = """
ABC10 DEF
1ST DEF 100CD
ABC 1ST 100CD
AN XLR8 45X
NO H20 DEF
A4B PLUS
"""
def add_spaces(m):
if m.group().lower() in subs:
return m.group()
res = m.group(1)
if len(res):
res += ' '
res += m.group(2)
if len(m.group(3)):
res += ' '
res += m.group(3)
return res
text = re.sub(r'\b([^\d\s]*)(\d+)([^\d\s]*)\b', lambda m: add_spaces(m), text)
print(text)
输出:
ABC 10 DEF
1ST DEF 100 CD
ABC 1ST 100 CD
AN XLR8 45 X
NO H20 DEF
A 4 B PLUS
您可以将 lambda 函数简化为
def add_spaces(m):
if m.group().lower() in subs:
return m.group()
return m.group(1) + ' ' + m.group(2) + ' ' + m.group(3)
但这可能会导致输出字符串中出现额外的空格。然后可以用
删除
text = re.sub(r' +', ' ', text)
使用regex
、(*SKIP)(*FAIL)
和f-strings
的另一种方式:
import regex as re
lst = ['1st','2nd','1st nation','xlr8','5pin','h20']
data = """
ABC10 DEF
ABC DEF10
ABC 10DEF
10ABC DEF
1ST DEF 100CD
ABC 1ST 100CD"""
rx = re.compile(
rf"""
(?:{"|".join(item.upper() for item in lst)})(*SKIP)(*FAIL)
|
(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)
""", re.X)
data = rx.sub(' ', data)
print(data)
这会产生
ABC 10 DEF
ABC DEF 10
ABC 10 DEF
10 ABC DEF
1ST DEF 100 CD
ABC 1ST 100 CD
处理异常时,最简单、最安全的方法是使用“best trick ever”方法。替换时,这个技巧意味着:保留捕获的内容,删除匹配的内容,反之亦然。在正则表达式中,您必须使用 alternation 并使用 capturing group 围绕其中一个(或一些复杂场景)才能分析遇到匹配后的匹配结构
因此,首先,使用异常列表构建交替的第一部分:
exception_rx = "|".join(map(re.escape, exceptions))
注意 re.escape
在需要的地方添加反斜杠以支持异常中的任何特殊字符。如果您的例外都是字母数字,则不需要,您可以只使用 exception_rx = "|".join(exceptions)
。甚至 exception_rx = rf'\b(?:{"|".join(exceptions)})\b'
只将它们作为整个单词进行匹配。
Next,您需要一种模式,无论上下文如何都能找到所有匹配项,我 :
generic_rx = r'(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)'
最后,使用(exceptions_rx)|generic_rx
方案加入他们:
rx = re.compile(rf'({exception_rx})|{generic_rx}', re.I)
并使用 .sub()
:
替换
s = rx.sub(lambda x: x.group(1) or " ", s)
此处,lambda x: x.group(1) or " "
表示return组1值,如果第1组匹配,否则,替换为space.
参见 Python demo:
import re
exceptions = ['1st','2nd','10th','100th','1st nation','xlr8','5pin','h20', '12th'] # '12th' added
exception_rx = '|'.join(map(re.escape, exceptions))
generic_rx = r'(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)'
rx = re.compile(rf'({exception_rx})|{generic_rx}', re.I)
string_lst = ['1ST DEF 100CD','ABC 1ST 100CD','WEST 12TH APARTMENT']
for s in string_lst:
print(rx.sub(lambda x: x.group(1) or " ", s))
输出:
1ST DEF 100 CD
ABC 1ST 100 CD
WEST 12TH APARTMENT
我有一个列表,其中包含我需要保留的一些替代品。例如,替换列表:['1st', '2nd', '10th', '100th', '1st nation', 'xlr8', '5pin', 'h20']
.
一般情况下,包含字母数字字符的字符串需要按如下方式拆分数字和字母:
text = re.sub(r'(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)', ' ', text, 0, re.IGNORECASE)
之前的正则表达式模式通过在以下之间添加 space 成功地将所有数字与字符分开:
Original Regex
ABC10 DEF --> ABC 10 DEF
ABC DEF10 --> ABC DEF 10
ABC 10DEF --> ABC 10 DEF
10ABC DEF --> 10 ABC DEF
但是,替换列表中有一些字母数字单词无法分开。例如,以下包含替换列表一部分的 1ST
的字符串不应被分隔,它们应该被省略而不是添加 space:
Original Regex Expected
1ST DEF 100CD --> 1 ST DEF 100 CD --> 1ST DEF 100 CD
ABC 1ST 100CD --> ABC 1 ST 100 CD --> ABC 1ST 100 CD
100TH DEF 100CD -> 100 TH DEF 100 CD -> 100TH DEF 100 CD
10TH DEF 100CD -> 10 TH DEF 100 CD -> 10TH DEF 100 CD
为了获得上例中的预期列,我尝试在正则表达式中使用 IF THEN ELSE
方法,但我在 Python:
(?(?=condition)(then1|then2|then3)|(else1|else2|else3))
根据语法,我应该有如下内容:
?(?!1ST)((?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)))
其中 (?!...)
将包括匹配正则表达式模式时要避免的可能替换,在本例中为词 1ST 10TH 100TH
.
如何避免匹配字符串中的单词替换?
您可以使用 lambda 函数执行此操作,以检查匹配的字符串是否在您的排除列表中:
import re
subs = ['1st','2nd','1st nation','xlr8','5pin','h20']
text = """
ABC10 DEF
1ST DEF 100CD
ABC 1ST 100CD
AN XLR8 45X
NO H20 DEF
A4B PLUS
"""
def add_spaces(m):
if m.group().lower() in subs:
return m.group()
res = m.group(1)
if len(res):
res += ' '
res += m.group(2)
if len(m.group(3)):
res += ' '
res += m.group(3)
return res
text = re.sub(r'\b([^\d\s]*)(\d+)([^\d\s]*)\b', lambda m: add_spaces(m), text)
print(text)
输出:
ABC 10 DEF
1ST DEF 100 CD
ABC 1ST 100 CD
AN XLR8 45 X
NO H20 DEF
A 4 B PLUS
您可以将 lambda 函数简化为
def add_spaces(m):
if m.group().lower() in subs:
return m.group()
return m.group(1) + ' ' + m.group(2) + ' ' + m.group(3)
但这可能会导致输出字符串中出现额外的空格。然后可以用
删除text = re.sub(r' +', ' ', text)
使用regex
、(*SKIP)(*FAIL)
和f-strings
的另一种方式:
import regex as re
lst = ['1st','2nd','1st nation','xlr8','5pin','h20']
data = """
ABC10 DEF
ABC DEF10
ABC 10DEF
10ABC DEF
1ST DEF 100CD
ABC 1ST 100CD"""
rx = re.compile(
rf"""
(?:{"|".join(item.upper() for item in lst)})(*SKIP)(*FAIL)
|
(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)
""", re.X)
data = rx.sub(' ', data)
print(data)
这会产生
ABC 10 DEF
ABC DEF 10
ABC 10 DEF
10 ABC DEF
1ST DEF 100 CD
ABC 1ST 100 CD
处理异常时,最简单、最安全的方法是使用“best trick ever”方法。替换时,这个技巧意味着:保留捕获的内容,删除匹配的内容,反之亦然。在正则表达式中,您必须使用 alternation 并使用 capturing group 围绕其中一个(或一些复杂场景)才能分析遇到匹配后的匹配结构
因此,首先,使用异常列表构建交替的第一部分:
exception_rx = "|".join(map(re.escape, exceptions))
注意 re.escape
在需要的地方添加反斜杠以支持异常中的任何特殊字符。如果您的例外都是字母数字,则不需要,您可以只使用 exception_rx = "|".join(exceptions)
。甚至 exception_rx = rf'\b(?:{"|".join(exceptions)})\b'
只将它们作为整个单词进行匹配。
Next,您需要一种模式,无论上下文如何都能找到所有匹配项,我
generic_rx = r'(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)'
最后,使用(exceptions_rx)|generic_rx
方案加入他们:
rx = re.compile(rf'({exception_rx})|{generic_rx}', re.I)
并使用 .sub()
:
s = rx.sub(lambda x: x.group(1) or " ", s)
此处,lambda x: x.group(1) or " "
表示return组1值,如果第1组匹配,否则,替换为space.
参见 Python demo:
import re
exceptions = ['1st','2nd','10th','100th','1st nation','xlr8','5pin','h20', '12th'] # '12th' added
exception_rx = '|'.join(map(re.escape, exceptions))
generic_rx = r'(?<=\d)(?=[^\d\s])|(?<=[^\d\s])(?=\d)'
rx = re.compile(rf'({exception_rx})|{generic_rx}', re.I)
string_lst = ['1ST DEF 100CD','ABC 1ST 100CD','WEST 12TH APARTMENT']
for s in string_lst:
print(rx.sub(lambda x: x.group(1) or " ", s))
输出:
1ST DEF 100 CD
ABC 1ST 100 CD
WEST 12TH APARTMENT