以特殊字符开头或结尾的单词边界会产生意外的结果
- 2024-12-03 08:45:00
- admin 原创
- 192
问题描述:
Sortesindex[persons]{Sortes}
假设我想匹配短语中短语的存在test Sortesindex[persons]{Sortes} text
。
使用 pythonre
我可以这样做:
>>> search = re.escape('Sortesindex[persons]{Sortes}')
>>> match = 'test Sortesindex[persons]{Sortes} text'
>>> re.search(search, match)
<_sre.SRE_Match object; span=(5, 34), match='Sortes\\index[persons]{Sortes}'>
这可行,但我想避免搜索模式Sortes
对短语给出肯定的结果test Sortesindex[persons]{Sortes} text
。
>>> re.search(re.escape('Sortes'), match)
<_sre.SRE_Match object; span=(5, 11), match='Sortes'>
因此我使用如下模式:
search = r'' + re.escape('Sortesindex[persons]{Sortes}') + r''
match = 'test Sortesindex[persons]{Sortes} text'
re.search(search, match)
现在,我没有找到匹配项。
如果搜索模式不包含任何字符[]{}
,则搜索有效。例如:
>>> re.search(r'' + re.escape('Sortesindex') + r'', 'test Sortesindex test')
<_sre.SRE_Match object; span=(5, 17), match='Sortes\\index'>
此外,如果我删除最后的r''
,它也可以起作用:
re.search(r'' + re.escape('Sortesindex[persons]{Sortes}'), 'test Sortesindex[persons]{Sortes} test')
<_sre.SRE_Match object; span=(5, 34), match='Sortes\\index[persons]{Sortes}'>
此外,文档还提到
请注意,正式地, \b 被定义为 \w 和 \W 字符之间的边界(反之亦然),或者 \w 和字符串的开始/结束之间的边界。
所以我尝试用以下内容替换最后一个
(W|$)
:
>>> re.search(r'' + re.escape('Sortesindex[persons]{Sortes}') + '(W|$)', 'test Sortesindex[persons]{Sortes} test')
<_sre.SRE_Match object; span=(5, 35), match='Sortes\\index[persons]{Sortes} '>
瞧,它成功了!这是怎么回事?我遗漏了什么?
解决方案 1:
查看单词边界匹配的内容:
单词边界可以出现在以下三个位置之一:
如果第一个字符是单词字符,则位于字符串中的第一个字符之前。
如果字符串的最后一个字符是单词字符,则位于最后一个字符之后。
字符串中的两个字符之间,其中一个是单词字符,另一个不是单词字符。
在您的模式中,}
仅当后面有一个单词字符}
(字母、数字或_
)时才匹配。
当您使用时,您明确(W|$)
需要一个非单词或字符串的结尾。
一个解决方案是自适应词边界:
re.search(r'(?:(?!w)|(?=w)){}(?:(?<=w)|(?<!w))'.format(re.escape('Sortesindex[persons]{Sortes}')), 'test Sortesindex[persons]{Sortes} test')
或者等效的:
re.search(r'(?!Bw){}(?<!wB)'.format(re.escape('Sortesindex[persons]{Sortes}')), 'test Sortesindex[persons]{Sortes} test')
这里使用自适应动态词边界,其含义如下:
(?:(?!w)|(?=w))
(等于(?!Bw)
)- 左边界,如果下一个字符是单词字符,则确保当前位置位于单词边界,如果下一个字符不是单词字符,则不应用任何上下文限制(请注意(?:B(?!w)|(?=w))
,如果下一个字符不是单词字符,则如果您想禁止左侧立即出现单词字符,则需要使用)(?:(?<=w)|(?<!w))
(等于(?<!wB)
) - 右侧边界,如果前一个字符是单词字符,则确保当前位置位于单词边界,如果前一个字符不是单词字符,则不应用任何上下文限制(请注意(?:(?<=w)|B(?<!w))
,如果前一个字符不是单词字符,则如果您想禁止右侧立即出现单词字符,则需要使用)。
在这些情况下,您可能还考虑使用基于负面环视的明确词边界:
re.search(r'(?<!w){}(?!w)'.format(re.escape('Sortesindex[persons]{Sortes}')), 'test Sortesindex[persons]{Sortes} test')
这里,(?<!w)
如果当前位置左边紧接着有一个单词 char,则负向后视将导致匹配失败,而(?!w)
如果当前位置右边紧接着有一个单词 char,则负向前视将导致匹配失败。
选择哪一个?自适应词边界与明确词边界相比更为宽松,因为后者假定匹配的两端都不能有单词字符,而前者允许在任何上下文中匹配前导和尾随非单词字符。
注意:进一步定制这些环视模式非常容易(例如,仅当模式周围有字母[^Wd_]
时匹配失败,使用而不是w
,或者如果只允许在空格周围匹配,请使用空格边界 (?<!S)
/(?!S)
环视边界)。
解决方案 2:
我认为这就是你遇到的情况:
`w
落在和的边界上
W,但在例子中这不起作用。
'{Sortes}'是和之间的边界
W,
W因为与的普通集合
'}'不匹配。
[a-zA-Z0-9_]`w
扫码咨询,免费领取项目管理大礼包!