Python: регулярное выражение не работает должным образом

Я использую следующее регулярное выражение, он должен найти строку 'U.S.A.', но получает только 'A.', кто-нибудь знает, что не так?

#INPUT
import re

text = 'That U.S.A. poster-print costs $12.40...'

print re.findall(r'([A-Z]\.)+', text)

#OUTPUT
['A.']

Ожидаемый результат:

['U.S.A.']

Я следую Книге NLTK, Глава 3.7 здесь, это имеет набор регулярных выражений, но он просто не работает. Я пробовал это как в Python 2.7, так и в 3.4.

>>> text = 'That U.S.A. poster-print costs $12.40...'
>>> pattern = r'''(?x)    # set flag to allow verbose regexps
...     ([A-Z]\.)+        # abbreviations, e.g. U.S.A.
...   | \w+(-\w+)*        # words with optional internal hyphens
...   | \$?\d+(\.\d+)?%?  # currency and percentages, e.g. $12.40, 82%
...   | \.\.\.            # ellipsis
...   | [][.,;"'?():-_`]  # these are separate tokens; includes ], [
... '''
>>> nltk.regexp_tokenize(text, pattern)
['That', 'U.S.A.', 'poster-print', 'costs', '$12.40', '...']

nltk.regexp_tokenize() работает так же, как re.findall(), я думаю, что мой питон почему-то не распознает регулярное выражение, как ожидалось. Упомянутое выше регулярное выражение выводит следующее:

[('', '', ''),
 ('A.', '', ''),
 ('', '-print', ''),
 ('', '', ''),
 ('', '', '.40'),
 ('', '', '')]

person LingxB    schedule 31.01.2016    source источник
comment
Поскольку вы не упомянули шаблон и если ваш единственный мотив состоит в том, чтобы найти U.S.A., используя (U.S.A.), будет достаточно.   -  person    schedule 31.01.2016
comment


Ответы (4)


Возможно, это как-то связано с тем, как регулярные выражения компилировались ранее с использованием nltk.internals.compile_regexp_to_noncapturing(), который упразднен в версии 3.1, см. py#L119" rel="nofollow">здесь)

>>> import nltk
>>> nltk.__version__
'3.0.5'
>>> pattern = r'''(?x)               # set flag to allow verbose regexps
...               ([A-Z]\.)+         # abbreviations, e.g. U.S.A.
...               | \$?\d+(\.\d+)?%? # numbers, incl. currency and percentages
...               | \w+([-']\w+)*    # words w/ optional internal hyphens/apostrophe
...               | [+/\-@&*]        # special characters with meanings
...             '''
>>> 
>>> from nltk.tokenize.regexp import RegexpTokenizer
>>> tokeniser=RegexpTokenizer(pattern)
>>> line="My weight is about 68 kg, +/- 10 grams."
>>> tokeniser.tokenize(line)
['My', 'weight', 'is', 'about', '68', 'kg', '+', '/', '-', '10', 'grams']

Но это не работает в NLTK v3.1:

>>> import nltk
>>> nltk.__version__
'3.1'
>>> pattern = r'''(?x)               # set flag to allow verbose regexps
...               ([A-Z]\.)+         # abbreviations, e.g. U.S.A.
...               | \$?\d+(\.\d+)?%? # numbers, incl. currency and percentages
...               | \w+([-']\w+)*    # words w/ optional internal hyphens/apostrophe
...               | [+/\-@&*]        # special characters with meanings
...             '''
>>> from nltk.tokenize.regexp import RegexpTokenizer
>>> tokeniser=RegexpTokenizer(pattern)
>>> line="My weight is about 68 kg, +/- 10 grams."
>>> tokeniser.tokenize(line)
[('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', '')]

С небольшим изменением того, как вы определяете свои группы регулярных выражений, вы можете заставить тот же шаблон работать в NLTK v3.1, используя это регулярное выражение:

pattern = r"""(?x)                   # set flag to allow verbose regexps
              (?:[A-Z]\.)+           # abbreviations, e.g. U.S.A.
              |\d+(?:\.\d+)?%?       # numbers, incl. currency and percentages
              |\w+(?:[-']\w+)*       # words w/ optional internal hyphens/apostrophe
              |(?:[+/\-@&*])         # special characters with meanings
            """

В коде:

>>> import nltk
>>> nltk.__version__
'3.1'
>>> pattern = r"""
... (?x)                   # set flag to allow verbose regexps
... (?:[A-Z]\.)+           # abbreviations, e.g. U.S.A.
... |\d+(?:\.\d+)?%?       # numbers, incl. currency and percentages
... |\w+(?:[-']\w+)*       # words w/ optional internal hyphens/apostrophe
... |(?:[+/\-@&*])         # special characters with meanings
... """
>>> from nltk.tokenize.regexp import RegexpTokenizer
>>> tokeniser=RegexpTokenizer(pattern)
>>> line="My weight is about 68 kg, +/- 10 grams."
>>> tokeniser.tokenize(line)
['My', 'weight', 'is', 'about', '68', 'kg', '+', '/', '-', '10', 'grams']

Без NLTK, используя модуль re Python, мы видим, что старые шаблоны регулярных выражений изначально не поддерживаются:

>>> pattern1 = r"""(?x)               # set flag to allow verbose regexps
...               ([A-Z]\.)+         # abbreviations, e.g. U.S.A.
...               |\$?\d+(\.\d+)?%? # numbers, incl. currency and percentages
...               |\w+([-']\w+)*    # words w/ optional internal hyphens/apostrophe
...               |[+/\-@&*]        # special characters with meanings
...               |\S\w*                       # any sequence of word characters# 
... """            
>>> text="My weight is about 68 kg, +/- 10 grams."
>>> re.findall(pattern1, text)
[('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', '')]
>>> pattern2 = r"""(?x)                   # set flag to allow verbose regexps
...                       (?:[A-Z]\.)+           # abbreviations, e.g. U.S.A.
...                       |\d+(?:\.\d+)?%?       # numbers, incl. currency and percentages
...                       |\w+(?:[-']\w+)*       # words w/ optional internal hyphens/apostrophe
...                       |(?:[+/\-@&*])         # special characters with meanings
...                     """
>>> text="My weight is about 68 kg, +/- 10 grams."
>>> re.findall(pattern2, text)
['My', 'weight', 'is', 'about', '68', 'kg', '+', '/', '-', '10', 'grams']

Примечание. Изменение в том, как RegexpTokenizer NLTK компилирует регулярные выражения, сделает примеры на Токенизатор регулярных выражений NLTK также устарел.

person alvas    schedule 31.01.2016

Отбросьте конечный + или поместите его внутрь группы:

>>> text = 'That U.S.A. poster-print costs $12.40...'
>>> re.findall(r'([A-Z]\.)+', text)
['A.']              # wrong
>>> re.findall(r'([A-Z]\.)', text)
['U.', 'S.', 'A.']  # without '+'
>>> re.findall(r'((?:[A-Z]\.)+)', text)
['U.S.A.']          # with '+' inside the group
person Andrea Corbellini    schedule 31.01.2016

Первая часть текста, которой соответствует регулярное выражение, — «США». потому что ([A-Z]\.)+ соответствует первой группе (часть в скобках) три раза. Однако вы можете вернуть только одно совпадение для каждой группы, поэтому Python выбирает последнее совпадение для этой группы.

Если вместо этого вы измените регулярное выражение, включив в группу «+», то группа будет соответствовать только один раз, и будет возвращено полное совпадение. Например, (([A-Z]\.)+) или ((?:[A-Z]\.)+).

Если вместо этого вам нужны три отдельных результата, просто избавьтесь от знака «+» в регулярном выражении, и каждый раз он будет соответствовать только одной букве и одной точке.

person Jonas Berlin    schedule 31.01.2016

Проблема заключается в «группе захвата», также известной как круглые скобки, которые оказывают неожиданное влияние на результат findall(): когда группа захвата используется несколько раз в совпадении, механизм регулярных выражений теряет след, и происходят странные вещи. В частности: регулярное выражение правильно соответствует всему U.S.A., но findall отбрасывает его на пол и возвращает только последний захват группы.

Как говорится в в этом ответе, модуль re не поддерживает повторяющиеся группы захвата, но вы можете установить альтернативный regexp, который обрабатывает это правильно. (Однако это не поможет вам, если вы хотите передать свое регулярное выражение в nltk.tokenize.regexp.)

В любом случае, чтобы правильно сопоставить U.S.A., используйте это: r'(?:[A-Z]\.)+', text).

>>> re.findall(r'(?:[A-Z]\.)+', text)
['U.S.A.']

Вы можете применить одно и то же исправление ко всем повторяющимся шаблонам в регулярном выражении NLTK, и все будет работать правильно. Как предположил @alvas, NLTK использовал эту замену за кулисами, но эта функция недавно была удалена и заменена на предупреждение в документации токенизатора. Книга явно устарела; @alvas отправил отчет об ошибке еще в ноябре, но до сих пор подействовал еще...

person alexis    schedule 02.02.2016