Путаница с использованием регулярных выражений в Python

Я путаюсь со следующими тремя шаблонами: кто-нибудь объяснит это более подробно?

## IPython with Python 2.7.3
In [62]: re.findall(r'[a-z]*',"f233op")
Out[62]: ['f', '', '', '', 'op', '']  ## why does the last '' come out?

In [63]: re.findall(r'([a-z])*',"f233op")
Out[63]: ['f', '', '', '', 'p', '']  ## why does the character 'o' get lost?

In [64]: re.findall(r'([a-z]*)',"f233op")
Out[64]: ['f', '', '', '', 'op', '']  ## what the different than line 63 above?

Ответы

Ответ 1

Пример 1

re.findall(r'[a-z]*',"f233op")

Этот шаблон соответствует нулевым или большему числу символов алфавита в нижнем регистре. Часть ZERO-or-more является ключевой здесь, поскольку совпадение ничего, начиная с каждой позиции индекса в строке, столь же корректно, как и соответствие f или op. Последней пустой строкой является совпадение, начинающееся с конца строки (позиция между p и $ (конец строки).

Пример 2

re.findall(r'([a-z])*',"f233op")

Теперь вы соответствуете группам символов, состоящим из одного символа алфавита в нижнем регистре. o больше не возвращается, потому что это жадный поиск, и будет возвращена последняя действительная согласованная группа. Поэтому, если вы изменили строку на f233op12fre, окончательный e будет возвращен, но не предыдущий f или r. Аналогично, если вы вытащите p в своей строке, вы все равно увидите, что o возвращается как допустимое соответствие.

И наоборот, если вы попытаетесь сделать это регулярное выражение не жадным, добавив ? (например. ([a-z])*?), возвращаемый набор совпадений будет пустым, поскольку действительное совпадение ничего не имеет более высокого приоритета действительного соответствия чего-либо.

Пример 3

re.findall(r'([a-z]*)',"f233op")

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

IN : re.findall(r'([a-z]*)([0-9]*)',"f233op")
OUT: [('f', '233'), ('op', ''), ('', '')]  

Сравните это с тем же шаблоном, минус круглые скобки (группы), и вы увидите, почему они важны:

IN : re.findall(r'[a-z]*[0-9]*',"f233op")
OUT: ['f233', 'op', ''] 

Также...

Может быть полезно подключить шаблоны регулярных выражений, подобные этим, в генераторы диаграмм регулярных выражений, например Regexplained, чтобы увидеть, как работает логика соответствия шаблону. Например, в качестве объяснения того, почему ваше регулярное выражение всегда возвращает пустые совпадения с символьной строкой, обратите внимание на разницу между шаблонами [a-z]* и [a-z]+.

Не забудьте проверить документы Python для библиотеки re, если вы застряли, они действительно дают довольно звездное объяснение для стандартный синтаксис регулярного выражения.

Ответ 2

  • Вы получите окончательный '', потому что [a-z]* соответствует пустой строке в конце.

  • Символ 'o' отсутствует, потому что вы сказали re.findall, чтобы сопоставлять группы, и каждая группа имеет один символ. Иными словами, вы делаете эквивалент

    m = re.match(r'([a-z])*', 'op')
    m.group(1)
    

    который вернет 'p', потому что это последняя вещь, захваченная парнерами (группа захвата 1).

  • Опять же, вы сопоставляете группы, но на этот раз многосимвольные группы.

Ответ 3

Ваши удивительные результаты связаны с регулярным выражением квантования *.

Рассмотрим:

[a-z]*

Regular expression visualization

Демоверсия Debuggex

Vs:

[a-z]+

Regular expression visualization

Демоверсия Debuggex

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

>>> re.findall(r'[a-z]*', '123456789')
['', '', '', '', '', '', '', '', '', '']

В строке [a-z] нет символов в наборе [a-z]. Тем не менее, поскольку * означает " ноль или больше", все позиции символов "совпадают", не сопоставляя любые символы в этой позиции.

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

>>> re.search(r'[a-z]*', '1234')
<_sre.SRE_Match object at 0x1069b6988>     # a 'match' is returned, but this is 
                                           # probably not what was intended

Теперь рассмотрим:

>>> re.findall(r'[a-z]*', '123abc789')
['', '', '', 'abc', '', '', '', '']

Vs:

>>> re.findall(r'([a-z])*', '123abc789')
['', '', '', 'c', '', '', '', '']

Первый шаблон - [a-z]*. Часть [a-z] представляет собой класс символов, соответствующий одному символу в наборе a-z, если не изменен; добавление квантора * будет с жадностью соответствовать как можно большему количеству символов, если оно больше нуля - следовательно, совпадение "abc", но также позволит совпадению нулевых символов (или символ за пределами набора символов, соответствующего позиции так как 0 является совпадением).

Добавление группировки в ([a-z])* эффективно уменьшает совпадение в количественном наборе обратно на один символ и возвращается последний символ, сопоставленный в наборе.

Если вы хотите, чтобы эффект группировки (скажем, в более сложном шаблоне) использовал не захватную группу:

>>> re.findall(r'(?:[a-z])*', '123abc789')
['', '', '', 'abc', '', '', '', '']

Ответ 4

В строке 63 вы обнаружите все экземпляры группы (обозначенные символами parens) символов длиной 1. * здесь мало чем для вас (просто заставляя вас сопоставлять группы нулевой длины).

В других примерах, имеющих * рядом с [a-z], вы сопоставляете смежные символы любой длины.

ИЗМЕНИТЬ

Играя с этот инструмент может помочь.