Регулярное выражение пропускает совпадение в начале строки - PullRequest
3 голосов
/ 29 мая 2019

У меня есть строки, как и бс. Я хочу извлечь все перекрывающиеся подпоследовательности, где подпоследовательность - это единичное окружение с любым числом bs. Это регулярное выражение, которое я написал:

import re

pattern = """(?=            # inside lookahead for overlapping results
             (?:a|^)        # match at beginning of str or after a
             (b* (?:a) b*)  # one a between any number of bs
             (?:a|$))       # at end of str or before next a
          """
a_between_bs = re.compile(pattern, re.VERBOSE)

Кажется, что он работает, как и ожидалось, за исключением случаев, когда самый первый символ в строке является a, и в этом случае эта подпоследовательность пропускается:

a_between_bs.findall("bbabbba")
# ['bbabbb', 'bbba']
a_between_bs.findall("abbabb")
# ['bbabb']

Я не понимаю, что происходит. Если я изменю порядок начала возможного совпадения, результаты также изменятся:

pattern = """(?=
             (?:^|a)        # a and ^ swapped
             (b* (?:a) b*)
             (?:a|$))
          """
a_between_bs = re.compile(pattern, re.VERBOSE)

a_between_bs.findall("abbabb")
# ['abb']

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

Редактировать

Я предполагал, что решения в приведенном выше примере с игрушкой приведут к моей полной проблеме, но, похоже, это не так, поэтому я сейчас уточняю (извините за это). Я пытаюсь извлечь "слоги" из расшифрованных слов. «Слог» - это гласный или дифтонг , перед которым следует любое количество согласных. Это мое регулярное выражение для их извлечения:

vowels = 'æɑəɛiɪɔuʊʌ'
diphtongues = "|".join(('aj', 'aw', 'ej', 'oj', 'ow'))
consonants = 'θwlmvhpɡŋszbkʃɹdnʒjtðf'

pattern = f"""(?=
          (?:[{vowels}]|^|{diphtongues})
          ([{consonants}]* (?:[{vowels}]|{diphtongues}) [{consonants}]*)
          (?:[{vowels}]|$|{diphtongues})
          )
          """
syllables = re.compile(pattern, re.VERBOSE)

Хитрость заключается в том, что дифтонги заканчиваются согласными (j или w), которые я не хочу включать в следующий слог. Таким образом, замена первой не захватывающей группы двойным отрицательным (?<![{consonants}]) не работает. Вместо этого я попытался заменить эту группу положительным прогнозом (?<=[{vowels}]|^|{diphtongues}), но регулярное выражение не будет принимать разную длину (даже удаление дифтонгов не работает, очевидно, ^ имеет другую длину).

Так что это проблемный случай с рисунком выше:

syllables.findall('æbə')
# ['bə'] 
# should be: ['æb', 'bə']

Редактировать 2: Я переключился на использование регулярных выражений, которые позволяют просматривать переменную ширину, что решает проблему. К моему удивлению, он даже кажется быстрее, чем модуль re в стандартной библиотеке. Я все еще хотел бы знать, как заставить это работать с модулем re, все же. (

Ответы [ 2 ]

1 голос
/ 30 мая 2019

Я предлагаю исправить это с двойным отрицанием:

(?=         # inside lookahead for overlapping results
 (?<![^a])  # match at beginning of str or after a
 (b*ab*)    # one a between any number of bs
 (?![^a])   # at end of str or before next a
)

См. Демоверсию regex

Примечание. Я заменил группировки на lookarounds : (?:a|^) на (?<![^a]) и (?:a|$) на (?![^a]). Последнее не очень важно, но первое здесь очень важно.

(?:a|^) в начале шаблона внешнего прогнозирования соответствует a или началу строки, что бы ни предшествовало. Если a находится в начале, оно совпадает, и когда входное значение равно abbabb, вы получаете bbabb, так как оно соответствует шаблону группы захвата и сразу после него есть конец строки. Следующая итерация начинается после первой a и не может найти никакого совпадения, так как единственная a, оставшаяся в строке, не имеет a после b s.

Обратите внимание, что порядок альтернативных вопросов . Если вы измените значение на (?:^|a), совпадение начнется в начале строки, b* совпадет с пустой строкой, ab* захватит первый abb в abbabb, и, так как сразу после этого будет a, вы получить abb в качестве совпадения. Нет никакого способа сопоставить что-либо после первого a.

0 голосов
/ 30 мая 2019

Помните, что python "замыкает накоротко", поэтому, если он соответствует "^", он не будет продолжать искать, не совпадает ли он и с "a". Это будет «потреблять» соответствующий символ, поэтому в случаях, когда он соответствует «a», «a» используется и недоступен для сопоставления следующей группе, и поскольку использование синтаксиса (? :) не захватывает, то « a «потерян» и недоступен для захвата следующей группой (b * (?: a) b *), тогда как, когда «^» используется первой группой, это первое «a» будет совпадать в вторая группировка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...