Вопрос-Ответ

Word boundary with words starting or ending with special characters gives unexpected results

Граница слова со словами, начинающимися или заканчивающимися специальными символами, дает неожиданные результаты

Допустим, я хочу сопоставить наличие фразы Sortes\index[persons]{Sortes} во фразе test Sortes\index[persons]{Sortes} text.

Используя python re я мог бы это сделать:

>>> 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}'>

Это работает, но я хочу избежать шаблона поиска, Sortes чтобы дать положительный результат по фразе test Sortes\index[persons]{Sortes} text.

>>> re.search(re.escape('Sortes'), match)
<_sre.SRE_Match object; 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(search, match)

Теперь я не получаю совпадения.

Если шаблон поиска не содержит ни одного из символов []{}, это работает. Например.:

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

Кроме того, если я удалю final r'\b', это тоже сработает:

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}'>

Кроме того, в документации говорится о \b


Обратите внимание, что формально \b определяется как граница между символом \w и символом \W (или наоборот), или между \w и началом / концом строки.


Итак, я попытался заменить final \b на (\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} '>

О чудо, это работает!
Что здесь происходит? Что я упускаю?

Переведено автоматически
Ответ 1

Посмотрите, чему соответствует граница слова:

Граница слова может находиться в одной из трех позиций:



  • Перед первым символом в строке, если первый символ является символом word.

  • После последнего символа в строке, если последний символ является символом word.

  • Между двумя символами в строке, где один является символом слова, а другой не является символом слова.


В вашем шаблоне }\b совпадает только в том случае, если после } стоит слово char (буква, цифра или _).

При использовании (\W|$) вам явно требуется не слово или конец строки.

Решение заключается в адаптивных границах слов:

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

Или эквивалент:

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

Здесь используются адаптивные динамические границы слов, которые означают следующее:


  • (?:(?!\w)|\b(?=\w)) (равно (?!\B\w)) - граница слева, обеспечивающая, что текущая позиция находится на границе слова, если следующий символ является символом слова, или не применяется контекстное ограничение, если следующий символ не является символом слова (обратите внимание, что вам нужно будет использовать (?:\B(?!\w)|\b(?=\w)), если вы хотите запретить использование символа слова сразу слева, если следующий символ не является символом слова)

  • (?:(?<=\w)\b|(?<!\w)) (равно (?<!\w\B)) - граница справа, обеспечивающая, что текущая позиция находится на границе слова, если предыдущий символ является word char , или не применяется контекстное ограничение, если предыдущий символ не является word char (обратите внимание, что вам нужно будет использовать (?:(?<=\w)\b|\B(?<!\w)), если вы хотите запретить использование word char непосредственно справа, если предыдущий символ не является word char).

В этих случаях вы также можете рассмотреть возможность использования однозначных границ слов, основанных на отрицательных результатах поиска:

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

Здесь (?<!\w) отрицательный поиск сзади не приведет к совпадению, если слово char находится непосредственно слева от текущего местоположения, и (?!\w) отрицательный поиск впереди не приведет к совпадению, если слово char находится непосредственно справа от текущего местоположения.

Что выбрать? Адаптивные границы слов более щадящие по сравнению с однозначными границами слов, поскольку последние предполагают, что на обоих концах совпадения не должно быть символов слов, в то время как первые позволяют сопоставлять начальные и конечные символы, не содержащие слов, в любом контексте.

Примечание: Эти шаблоны поиска можно легко настроить дополнительно (скажем, чтобы не совпадать только при наличии букв вокруг шаблона, используйте [^\W\d_] вместо \w, или если вы разрешаете совпадения только вокруг пробелов, используйте границы пробела (?<!\S) / (?!\S) границы поиска).

Ответ 2

Я думаю, это то, с чем вы сталкиваетесь:

\b попадает на границу \w и \W, но в приведенном примере это не работает. '{Sortes}\b' является границей между \W и \W из-за '}', который не соответствует [a-zA-Z0-9_] обычному набору для \w.

python regex