Pola ekstrak Python cocok

129

Python 2.7.1 Saya mencoba menggunakan ekspresi reguler python untuk mengekstrak kata-kata di dalam pola

Saya memiliki beberapa string yang terlihat seperti ini

someline abc
someother line
name my_user_name is valid
some more lines

Saya ingin mengekstrak kata "my_user_name". Saya melakukan sesuatu seperti

import re
s = #that big string
p = re.compile("name .* is valid", re.flags)
p.match(s) #this gives me <_sre.SRE_Match object at 0x026B6838>

Bagaimana cara mengekstrak my_user_name sekarang?

Kannan Ekanath
sumber

Jawaban:

159

Anda perlu mengambil dari regex. searchuntuk polanya, jika ditemukan, ambil stringnya menggunakan group(index). Dengan asumsi pemeriksaan yang valid dilakukan:

>>> p = re.compile("name (.*) is valid")
>>> result = p.search(s)
>>> result
<_sre.SRE_Match object at 0x10555e738>
>>> result.group(1)     # group(1) will return the 1st capture.
                        # group(0) will returned the entire matched text.
'my_user_name'
UltraInstinct
sumber
26
Apakah Anda yakin itu bukan group(0)untuk pertandingan pertama?
sharshofski
33
Agak terlambat, tapi ya dan tidak. group(0)mengembalikan teks yang cocok, bukan grup tangkapan pertama. Kode komentar benar, sementara Anda tampaknya membingungkan menangkap kelompok dan pertandingan. group(1)mengembalikan grup tangkapan pertama.
andrewgu
1
Saya mendapatkanNameError: name '_' is not defined
Ian G
Baris kedua Anda, saya pikir harus dibaca _ = p.search(s). Saya melihatnya menyebutkan pengaturan hasil _tetapi kode tidak mencerminkan itu. Saya berubah menjadi _ = p.search(s)untuk baris kedua itu dan berhasil.
Ian G
2
@IanG Maaf, saya akan memperbarui jawaban saya. BTW, dengan REPL python standar, hasil terakhir disimpan dalam variabel khusus yang disebut _. Ini tidak berlaku di luar tempat lain.
UltraInstinct
57

Anda dapat menggunakan grup yang cocok:

p = re.compile('name (.*) is valid')

misalnya

>>> import re
>>> p = re.compile('name (.*) is valid')
>>> s = """
... someline abc
... someother line
... name my_user_name is valid
... some more lines"""
>>> p.findall(s)
['my_user_name']

Di sini saya menggunakan re.findalldaripada re.searchmendapatkan semua contoh my_user_name. Menggunakan re.search, Anda perlu mendapatkan data dari grup pada objek yang cocok:

>>> p.search(s)   #gives a match object or None if no match is found
<_sre.SRE_Match object at 0xf5c60>
>>> p.search(s).group() #entire string that matched
'name my_user_name is valid'
>>> p.search(s).group(1) #first group that match in the string that matched
'my_user_name'

Seperti yang disebutkan di komentar, Anda mungkin ingin membuat ekspresi reguler Anda tidak serakah:

p = re.compile('name (.*?) is valid')

untuk hanya mengambil hal-hal antara 'name 'dan berikutnya ' is valid'(daripada membiarkan ekspresi reguler Anda mengambil yang lain ' is valid'di grup Anda.

mgilson.dll
sumber
2
Mungkin saja diperlukan kecocokan non-serakah ... (kecuali nama pengguna dapat terdiri dari beberapa kata ...)
Jon Clements
@ JonClements - Maksud Anda (.*?)? Ya, itu mungkin, meskipun tidak perlu kecuali OP kami menggunakanre.DOTALL
mgilson
ya - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')mungkin tidak akan memberikan hasil yang diinginkan ...
Jon Clements
Ini tidak bekerja untuk Python 2.7.1? Ini hanya mencetak objek pola?
Kannan Ekanath
@CalmStorm - Bagian mana yang tidak berfungsi (saya uji di python2.7.3)? Bagian yang saya gunakan .grouppersis sama dengan jawaban yang Anda terima ...
mgilson
16

Anda bisa menggunakan sesuatu seperti ini:

import re
s = #that big string
# the parenthesis create a group with what was matched
# and '\w' matches only alphanumeric charactes
p = re.compile("name +(\w+) +is valid", re.flags)
# use search(), so the match doesn't have to happen 
# at the beginning of "big string"
m = p.search(s)
# search() returns a Match object with information about what was matched
if m:
    name = m.group(1)
else:
    raise Exception('name not found')
Apalala
sumber
10

Mungkin itu sedikit lebih pendek dan lebih mudah dipahami:

import re
text = '... someline abc... someother line... name my_user_name is valid.. some more lines'
>>> re.search('name (.*) is valid', text).group(1)
'my_user_name'
John
sumber
9

Anda ingin grup tangkap .

p = re.compile("name (.*) is valid", re.flags) # parentheses for capture groups
print p.match(s).groups() # This gives you a tuple of your matches.
Henry Keiter
sumber
9

Anda dapat menggunakan grup (ditunjukkan dengan '('dan ')') untuk menangkap bagian dari string. Metode objek pencocokan group()kemudian memberi Anda konten grup:

>>> import re
>>> s = 'name my_user_name is valid'
>>> match = re.search('name (.*) is valid', s)
>>> match.group(0)  # the entire match
'name my_user_name is valid'
>>> match.group(1)  # the first parenthesized subgroup
'my_user_name'

Di Python 3.6+ Anda juga dapat mengindeks objek yang cocok alih-alih menggunakan group():

>>> match[0]  # the entire match 
'name my_user_name is valid'
>>> match[1]  # the first parenthesized subgroup
'my_user_name'
Eugene Yarmash
sumber
6

Berikut cara melakukannya tanpa menggunakan grup (Python 3.6 atau lebih tinggi):

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'
serigala
sumber
1
Ini membahas Python Regex, tetapi tidak menjawab pertanyaan spesifik OP.
Aleister Tanek Javas Mraz
Selain itu, ini pada dasarnya tidak menambahkan sesuatu yang baru ke jawaban yang ada yang menyebutkan sintaks pengindeksan 3.6+.
Eugene Yarmash
3

Anda juga dapat menggunakan grup penangkapan (?P<user>pattern)dan mengakses grup seperti kamus match['user'].

string = '''someline abc\n
            someother line\n
            name my_user_name is valid\n
            some more lines\n'''

pattern = r'name (?P<user>.*) is valid'
matches = re.search(pattern, str(string), re.DOTALL)
print(matches['user'])

# my_user_name
Ryan Stefan
sumber
1

Sepertinya Anda benar-benar mencoba mengekstrak nama wakil hanya mencari kecocokan. Jika ini masalahnya, memiliki indeks span untuk kecocokan Anda sangat membantu dan saya merekomendasikan penggunaan re.finditer. Sebagai jalan pintas, Anda tahu namebagian regex Anda adalah panjang 5 dan is validpanjangnya 9, sehingga Anda dapat memotong teks yang cocok untuk mengekstrak namanya.

Catatan - Dalam contoh Anda, sepertinya sadalah string dengan jeda baris, jadi itulah yang diasumsikan di bawah ini.

## covert s to list of strings separated by line:
s2 = s.splitlines()

## find matches by line: 
for i, j in enumerate(s2):
    matches = re.finditer("name (.*) is valid", j)
    ## ignore lines without a match
    if matches:
        ## loop through match group elements
        for k in matches:
            ## get text
            match_txt = k.group(0)
            ## get line span
            match_span = k.span(0)
            ## extract username
            my_user_name = match_txt[5:-9]
            ## compare with original text
            print(f'Extracted Username: {my_user_name} - found on line {i}')
            print('Match Text:', match_txt)
chiceman
sumber