Python: Ekspresi Reguler tidak berfungsi dengan benar

Saya menggunakan regex berikut, seharusnya menemukan string 'U.S.A.', tetapi hanya mendapat 'A.', adakah yang tahu apa yang salah?

#INPUT
import re

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

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

#OUTPUT
['A.']

Hasil yang Diharapkan:

['U.S.A.']

Saya mengikuti Buku NLTK, Bab 3.7 di sini, itu memiliki satu set regex tetapi tidak berfungsi. Saya sudah mencobanya di Python 2.7 dan 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() berfungsi sama dengan re.findall(), saya rasa python saya di sini tidak mengenali regex seperti yang diharapkan. Regex yang tercantum di atas menampilkan ini:

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

person LingxB    schedule 31.01.2016    source sumber
comment
Karena Anda tidak menyebutkan polanya dan jika satu-satunya motif Anda adalah menemukan U.S.A. menggunakan (U.S.A.) sudah cukup.   -  person    schedule 31.01.2016
comment


Jawaban (4)


Mungkin, ini ada hubungannya dengan cara regex dikompilasi sebelumnya menggunakan nltk.internals.compile_regexp_to_noncapturing() yang dihapuskan di v3.1, lihat di sini)

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

Tapi itu tidak berfungsi di 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)
[('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', ''), ('', '', '')]

Dengan sedikit modifikasi pada cara Anda menentukan grup regex, Anda bisa mendapatkan pola yang sama untuk berfungsi di NLTK v3.1, menggunakan regex ini:

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
            """

Dalam kode:

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

Tanpa NLTK, dengan menggunakan modul re python, kita melihat bahwa pola regex lama tidak didukung secara asli:

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

Catatan: Perubahan dalam cara RegexpTokenizer NLTK mengkompilasi regex akan membuat contoh di Tokenizer Ekspresi Reguler NLTK juga sudah usang.

person alvas    schedule 31.01.2016

Hilangkan + di akhir, atau masukkan ke dalam grup:

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

Bagian pertama teks yang cocok dengan regexp adalah "U.S.A." karena ([A-Z]\.)+ cocok dengan grup pertama (bagian dalam tanda kurung) sebanyak tiga kali. Namun Anda hanya dapat mengembalikan satu pertandingan per grup, jadi Python memilih pertandingan terakhir untuk grup tersebut.

Jika Anda mengubah ekspresi reguler untuk menyertakan "+" dalam grup, maka grup tersebut hanya akan cocok satu kali dan kecocokan penuh akan dikembalikan. Misalnya (([A-Z]\.)+) atau ((?:[A-Z]\.)+).

Jika Anda menginginkan tiga hasil terpisah, hilangkan saja tanda "+" di ekspresi reguler dan itu hanya akan cocok dengan satu huruf dan satu titik untuk setiap kali.

person Jonas Berlin    schedule 31.01.2016

Masalahnya adalah "grup penangkap" alias tanda kurung, yang memiliki efek tidak terduga pada hasil findall(): Ketika grup penangkap digunakan beberapa kali dalam pertandingan, mesin regexp kehilangan jejak dan hal-hal aneh terjadi. Khususnya: regexp cocok dengan seluruh U.S.A., tetapi findall menjatuhkannya ke lantai dan hanya mengembalikan tangkapan grup terakhir.

Seperti yang dikatakan jawaban ini, modul re tidak mendukung pengambilan grup berulang, tetapi Anda dapat menginstal regexp yang menangani ini dengan benar. (Namun, ini tidak akan membantu Anda jika Anda ingin meneruskan regexp Anda ke nltk.tokenize.regexp.)

Pokoknya untuk mencocokkan U.S.A. dengan benar, gunakan ini: r'(?:[A-Z]\.)+', text).

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

Anda dapat menerapkan perbaikan yang sama ke semua pola berulang di regexp NLTK, dan semuanya akan berfungsi dengan benar. Seperti yang disarankan @alvas, NLTK biasa melakukan penggantian ini di belakang layar, namun fitur ini baru-baru ini dihapus dan diganti dengan peringatan dalam dokumentasi tokenizer. Buku ini jelas sudah ketinggalan zaman; @alvas mengajukan laporan bug tentang hal ini pada bulan November, namun belum ada belum ditindaklanjuti...

person alexis    schedule 02.02.2016