以特殊字符开头或结尾的单词的单词边界会产生意想不到的结果 [英] Word boundary with words starting or ending with special characters gives unexpected results

查看:53
本文介绍了以特殊字符开头或结尾的单词的单词边界会产生意想不到的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想匹配短语 test Sortes\index[persons]{Sortes} text 中存在的短语 Sortes\index[persons]{Sortes}.

使用 python re 我可以这样做:

<预><代码>>>>search = re.escape('Sortes\index[persons]{Sortes}')>>>match = 'test Sortes\index[persons]{Sortes} text'>>>研究(搜索,匹配)<_sre.SRE_Match 对象;span=(5, 34), match='Sortes\\index[persons]{Sortes}'>

这有效,但我想避免使用搜索模式 Sortes 对短语 test Sortes\index[persons]{Sortes} text 给出肯定的结果.

<预><代码>>>>re.search(re.escape('Sortes'), 匹配)<_sre.SRE_Match 对象;span=(5, 11), match='Sortes'>

所以我使用 \b 模式,就像这样:

search = r'\b' + re.escape('Sortes\index[persons]{Sortes}') + r'\b'match = 'test Sortes\index[persons]{Sortes} text'研究(搜索,匹配)

现在,我没有匹配项.

如果搜索模式不包含任何字符 []{},则它有效.例如:

<预><代码>>>>re.search(r'\b' + re.escape('Sortes\index') + r'\b', 'test Sortes\index test')<_sre.SRE_Match 对象;span=(5, 17), match='Sortes\\index'>

另外,如果我删除最后的 r'\b',它也可以工作:

re.search(r'\b' + re.escape('Sortes\index[persons]{Sortes}'), 'test Sortes\index[persons]{Sortes} test')<_sre.SRE_Match 对象;span=(5, 34), match='Sortes\\index[persons]{Sortes}'>

此外,文档 介绍了 \b

<块引用>

请注意,形式上,\b 被定义为 \w 和 \W 字符之间的边界(反之亦然),或 \w 和字符串开头/结尾之间的边界.

所以我尝试用 (\W|$) 替换最后的 \b :

<预><代码>>>>re.search(r'\b' + re.escape('Sortes\index[persons]{Sortes}') + '(\W|$)', 'test Sortes\index[persons]{Sortes} test')<_sre.SRE_Match 对象;span=(5, 35), match='Sortes\\index[persons]{Sortes} '>

瞧,它有效!这里发生了什么?我错过了什么?

解决方案

查看单词边界匹配的内容:

单词边界可以出现在以下三个位置之一:

<块引用>
  • 在字符串的第一个字符之前,如果第一个字符是单词字符.
  • 在字符串的最后一个字符之后,如果最后一个字符是单词字符.
  • 在字符串中的两个字符之间,其中一个是单词字符,另一个不是单词字符.

在您的模式中,}\b 仅在 } 之后有字符字符(字母、数字或 _)时才匹配.

当您使用 (\W|$) 时,您需要一个非单词或字符串结尾明确.

在这些情况下,我总是建议基于负面环视的明确词边界:

re.search(r'(?

这里,(?<!\w) 如果当前位置的左边有一个单词 char,则负向后视将失败匹配,而 (?!\w) 如果当前位置的右侧有一个单词 char ,则负向前瞻将使匹配失败.

实际上,进一步自定义这些环视模式很容易(例如,如果模式周围有字母,则匹配失败,请使用[^\W\d_] 而不是 \w,或者如果您只允许匹配空格,请使用 (?<!\S)/(?!\S) 环视边界).

Say I want to match the presence of the phrase Sortes\index[persons]{Sortes} in the phrase test Sortes\index[persons]{Sortes} text.

Using python re I could do this:

>>> search = re.escape('Sortes\index[persons]{Sortes}')
>>> match = 'test Sortes\index[persons]{Sortes} text'
>>> re.search(search, match)
<_sre.SRE_Match object; span=(5, 34), match='Sortes\\index[persons]{Sortes}'>

This works, but I want to avoid the search pattern Sortes to give a positive result on the phrase test Sortes\index[persons]{Sortes} text.

>>> re.search(re.escape('Sortes'), match)
<_sre.SRE_Match object; span=(5, 11), match='Sortes'>

So I use the \b pattern, like this:

search = r'\b' + re.escape('Sortes\index[persons]{Sortes}') + r'\b'
match = 'test Sortes\index[persons]{Sortes} text'
re.search(search, match)

Now, I don't get a match.

If the search pattern does not contain any of the characters []{}, it works. E.g.:

>>> re.search(r'\b' + re.escape('Sortes\index') + r'\b', 'test Sortes\index test')
<_sre.SRE_Match object; span=(5, 17), match='Sortes\\index'>

Also, if I remove the final r'\b', it also works:

re.search(r'\b' + re.escape('Sortes\index[persons]{Sortes}'), 'test Sortes\index[persons]{Sortes} test')
<_sre.SRE_Match object; span=(5, 34), match='Sortes\\index[persons]{Sortes}'>

Furthermore, the documentation says about \b

Note that formally, \b is defined as the boundary between a \w and a \W character (or vice versa), or between \w and the beginning/end of the string.

So I tried replacing the final \b with (\W|$):

>>> re.search(r'\b' + re.escape('Sortes\index[persons]{Sortes}') + '(\W|$)', 'test Sortes\index[persons]{Sortes} test')
<_sre.SRE_Match object; span=(5, 35), match='Sortes\\index[persons]{Sortes} '>

Lo and behold, it works! What is going on here? What am I missing?

解决方案

See what a word boundary matches:

A word boundary can occur in one of three positions:

  • Before the first character in the string, if the first character is a word character.
  • After the last character in the string, if the last character is a word character.
  • Between two characters in the string, where one is a word character and the other is not a word character.

In your pattern }\b only matches if there is a word char after } (a letter, digit or _).

When you use (\W|$) you require a non-word or end of string explicitly.

I always recommend unambiguous word boundaries based on negative lookarounds in these cases:

re.search(r'(?<!\w){}(?!\w)'.format(re.escape('Sortes\index[persons]{Sortes}')), 'test Sortes\index[persons]{Sortes} test')

Here, (?<!\w) negative lookbehind will fail the match if there is a word char immediately to the left of the current location, and (?!\w) negative lookahead will fail the match if there is a word char immediately to the right of the current location.

Actually, it is easy to customize these lookaround patterns further (say, to only fail the match if there are letters around the pattern, use [^\W\d_] instead of \w, or if you only allow matches around whitespaces, use (?<!\S) / (?!\S) lookaround boundaries).

这篇关于以特殊字符开头或结尾的单词的单词边界会产生意想不到的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆