Путаница с использованием регулярных выражений в 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]
, вы сопоставляете смежные символы любой длины.
ИЗМЕНИТЬ
Играя с этот инструмент может помочь.