Ekspresi reguler adalah bahasa yang ampuh untuk mencocokkan pola teks. Halaman ini memberikan pengantar dasar ekspresi reguler yang cukup untuk latihan Python dan menunjukkan cara kerja ekspresi reguler di Python. Python "re" yang menyediakan dukungan ekspresi reguler.
Di Python, penelusuran ekspresi reguler biasanya ditulis sebagai:
match = re.search(pat, str)
Metode re.search() mengambil pola ekspresi reguler dan string, lalu mencari pola tersebut di dalam {i>string<i}. Jika penelusuran berhasil, search() akan menampilkan objek yang cocok atau Tidak Ada jika sebaliknya. Oleh karena itu, pencarian biasanya segera diikuti dengan pernyataan if untuk menguji apakah pencarian berhasil, seperti ditunjukkan dalam contoh berikut yang mencari pola 'word:' diikuti dengan kata 3 huruf (detail di bawah):
import re str = 'an example word:cat!!' match = re.search(r'word:\w\w\w', str) # If-statement after search() tests if it succeeded if match: print('found', match.group()) ## 'found word:cat' else: print('did not find')
Kode match = re.search(pat, str)
menyimpan hasil penelusuran dalam variabel bernama "match". Kemudian pernyataan if menguji kecocokan -- jika benar (true) maka penelusuran berhasil dan match.group() adalah teks yang cocok (misalnya, 'word:cat'). Sebaliknya, jika kecocokannya salah (Tidak ada agar lebih spesifik), penelusuran tidak berhasil, dan tidak ada teks yang cocok.
Huruf 'r' di awal, string pola menunjuk python "raw" string yang melalui garis miring terbalik tanpa perubahan yang sangat berguna untuk ekspresi reguler (Java sangat membutuhkan fitur ini). Saya sarankan agar Anda selalu menulis {i>string<i} pola dengan huruf 'r' hanya sebagai kebiasaan.
Pola Dasar
Kelebihan dari ekspresi reguler adalah bisa menentukan pola, bukan hanya karakter tetap. Berikut adalah pola paling dasar yang cocok dengan satu karakter:
- a, X, 9, < -- karakter biasa sama persis dengan karakternya. Meta-karakter yang tidak cocok dengan diri sendiri karena memiliki arti khusus adalah: . ^ $ * + ? { [ ] \ | ( ) (detail di bawah)
- . (titik) -- cocok dengan karakter tunggal apa pun kecuali baris baru '\n'
- \w -- (huruf kecil w) cocok dengan "kata" karakter: huruf atau angka atau garis bawah [a-zA-Z0-9_]. Perhatikan bahwa meskipun "kata" adalah mnemonik untuk ini, yang cocok hanya dengan satu karakter kata, bukan seluruh kata. \W (huruf besar W) cocok dengan karakter non-kata apa pun.
- \b -- batas antara kata dan non-kata
- \s -- (huruf kecil s) cocok dengan karakter spasi kosong tunggal -- spasi, baris baru, return, tab, bentuk [ \n \r\t\f]. \S (huruf besar S) cocok dengan karakter non-spasi kosong apa pun.
- \t, \n, \r -- tab, baris baru, kembali
- \d -- digit desimal [0-9] (beberapa utilitas ekspresi reguler lama tidak mendukung \d, tetapi semuanya mendukung \w dan \s)
- ^ = start, $ = end -- cocok dengan awal atau akhir {i>string<i}
- \ -- menghalangi "kekhususan" dari sebuah karakter. Jadi, misalnya, gunakan \. untuk mencocokkan titik atau \\ untuk mencocokkan dengan garis miring. Jika Anda tidak yakin apakah suatu karakter memiliki arti khusus, seperti '@', Anda dapat mencoba menempatkan garis miring di depannya, \@. Jika ini bukan urutan escape yang valid, seperti \c, program python Anda akan berhenti dengan error.
Contoh Dasar
Lelucon: apa sebutan babi dengan tiga mata? pai!
Aturan dasar penelusuran ekspresi reguler untuk pola dalam string adalah:
- Penelusuran dilanjutkan melalui {i>string<i} dari awal hingga akhir, berhenti pada kecocokan pertama yang ditemukan
- Semua pola harus cocok, tetapi tidak semua string
- Jika
match = re.search(pat, str)
berhasil, kecocokan bukan Tidak ada dan khususnya match.group() adalah teks yang cocok
## Search for pattern 'iii' in string 'piiig'. ## All of the pattern must match, but it may appear anywhere. ## On success, match.group() is matched text. match = re.search(r'iii', 'piiig') # found, match.group() == "iii" match = re.search(r'igs', 'piiig') # not found, match == None ## . = any char but \n match = re.search(r'..g', 'piiig') # found, match.group() == "iig" ## \d = digit char, \w = word char match = re.search(r'\d\d\d', 'p123g') # found, match.group() == "123" match = re.search(r'\w\w\w', '@@abcd!!') # found, match.group() == "abc"
Pengulangan
Hal-hal menjadi lebih menarik ketika Anda menggunakan + dan * untuk menentukan pengulangan pola
- + -- 1 atau beberapa kemunculan pola di sebelah kirinya, mis. "i+" = satu atau beberapa i
- * -- 0 atau lebih kemunculan pola di sebelah kirinya
- ? -- cocokkan 0 atau 1 kemunculan pola di sebelah kirinya
Yang paling kiri & Terbesar
Pertama, pencarian menemukan kecocokan paling kiri untuk pola, dan kedua mencoba menggunakan string sebanyak mungkin -- yaitu + dan * berjalan sejauh mungkin (+ dan * dikatakan "serakah").
Contoh Pengulangan
## i+ = one or more i's, as many as possible. match = re.search(r'pi+', 'piiig') # found, match.group() == "piii" ## Finds the first/leftmost solution, and within it drives the + ## as far as possible (aka 'leftmost and largest'). ## In this example, note that it does not get to the second set of i's. match = re.search(r'i+', 'piigiiii') # found, match.group() == "ii" ## \s* = zero or more whitespace chars ## Here look for 3 digits, possibly separated by whitespace. match = re.search(r'\d\s*\d\s*\d', 'xx1 2 3xx') # found, match.group() == "1 2 3" match = re.search(r'\d\s*\d\s*\d', 'xx12 3xx') # found, match.group() == "12 3" match = re.search(r'\d\s*\d\s*\d', 'xx123xx') # found, match.group() == "123" ## ^ = matches the start of string, so this fails: match = re.search(r'^b\w+', 'foobar') # not found, match == None ## but without the ^ it succeeds: match = re.search(r'b\w+', 'foobar') # found, match.group() == "bar"
Contoh Email
Misalnya Anda ingin menemukan alamat email di dalam string 'xyz alice-b@google.com Purple Monkey'. Kita akan menggunakan ini sebagai contoh untuk menunjukkan fitur ekspresi reguler yang lebih banyak. Berikut ini adalah upaya menggunakan pola r'\w+@\w+':
str = 'purple alice-b@google.com monkey dishwasher' match = re.search(r'\w+@\w+', str) if match: print(match.group()) ## 'b@google'
Dalam kasus ini, penelusuran tidak mendapatkan seluruh alamat email karena \w tidak cocok dengan '-' atau '.' di alamat. Kami akan memperbaikinya menggunakan fitur ekspresi reguler di bawah.
Tanda Kurung Siku
Tanda kurung siku dapat digunakan untuk menunjukkan sekumpulan karakter, sehingga [abc] cocok dengan 'a' atau 'b' atau 'c'. Kode \w, \s dll. juga berfungsi di dalam tanda kurung siku dengan satu pengecualian bahwa titik (.) hanya berarti titik literal. Untuk masalah email, tanda kurung siku adalah cara mudah untuk menambahkan '.' dan "-" ke kumpulan karakter yang dapat muncul di sekitar @ dengan pola r'[\w.-]+@[\w.-]+' untuk mendapatkan seluruh alamat email:
match = re.search(r'[\w.-]+@[\w.-]+', str) if match: print(match.group()) ## 'alice-b@google.com'
Ekstraksi Grup
"Grup" fitur ekspresi reguler memungkinkan Anda untuk memilih bagian dari teks yang cocok. Misalkan untuk masalah email yang ingin kita ekstrak nama pengguna dan host secara terpisah. Untuk melakukannya, tambahkan tanda kurung ( ) di sekitar nama pengguna dan host dalam pola, seperti ini: r'([\w.-]+)@([\w.-]+)'. Dalam hal ini, tanda kurung tidak mengubah apa yang akan cocok dengan pola, melainkan membuat "grup" yang logis di dalam teks yang sesuai. Pada penelusuran yang berhasil, match.group(1) adalah teks pencocokan yang sesuai dengan tanda kurung kiri pertama, dan match.group(2) adalah teks yang sesuai dengan tanda kurung kiri ke-2. Match.group() biasa masih merupakan seluruh teks yang cocok seperti biasa.
str = 'purple alice-b@google.com monkey dishwasher' match = re.search(r'([\w.-]+)@([\w.-]+)', str) if match: print(match.group()) ## 'alice-b@google.com' (the whole match) print(match.group(1)) ## 'alice-b' (the username, group 1) print(match.group(2)) ## 'google.com' (the host, group 2)
Alur kerja umum dengan ekspresi reguler adalah Anda menulis pola untuk hal yang Anda cari, menambahkan grup tanda kurung untuk mengekstrak bagian yang Anda inginkan.
cari semua
findall() mungkin merupakan satu-satunya fungsi yang paling ampuh dalam modul re. Di atas, kita menggunakan re.search() untuk menemukan kecocokan pertama untuk suatu pola. findall() menemukan *semua* kecocokan dan mengembalikannya sebagai daftar string, dengan setiap string mewakili satu kecocokan.## Suppose we have a text with many email addresses str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher' ## Here re.findall() returns a list of all the found email strings emails = re.findall(r'[\w\.-]+@[\w\.-]+', str) ## ['alice@google.com', 'bob@abc.com'] for email in emails: # do something with each found email string print(email)
findall Dengan File
Untuk file, Anda mungkin terbiasa menulis loop untuk melakukan iterasi pada baris-baris file, dan kemudian Anda dapat memanggil findall() di setiap baris. Alih-alih, biarkan findall() melakukan iterasi untuk Anda - jauh lebih baik! Cukup masukkan seluruh teks file ke findall() dan biarkan menampilkan daftar semua kecocokan dalam satu langkah (ingatlah bahwa f.read() mengembalikan seluruh teks file dalam satu string):
# Open file f = open('test.txt', encoding='utf-8') # Feed the file text into findall(); it returns a list of all the found strings strings = re.findall(r'some pattern', f.read())
findall dan Grup
Mekanisme grup tanda kurung ( ) dapat digabungkan dengan findall(). Jika polanya mencakup 2 atau lebih grup tanda kurung, maka alih-alih menampilkan daftar string, findall() akan mengembalikan daftar *tuple*. Setiap tuple mewakili satu kecocokan pola, dan di dalam tuple terdapat data group(1), group(2) .. data. Jadi jika 2 grup tanda kurung ditambahkan ke pola email, maka findall() mengembalikan daftar tupel, masing-masing panjang 2 berisi nama pengguna dan {i>host<i}, misalnya. ('alice', 'google.com').
str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher' tuples = re.findall(r'([\w\.-]+)@([\w\.-]+)', str) print(tuples) ## [('alice', 'google.com'), ('bob', 'abc.com')] for tuple in tuples: print(tuple[0]) ## username print(tuple[1]) ## host
Setelah memiliki daftar tuple, Anda dapat menggunakan loop untuk melakukan perhitungan pada setiap tuple. Jika polanya tidak menyertakan tanda kurung, maka findall() akan mengembalikan daftar string yang ditemukan seperti dalam contoh sebelumnya. Jika polanya menyertakan satu set tanda kurung, maka findall() akan mengembalikan daftar {i>string<i} yang sesuai dengan grup tunggal tersebut. (Fitur opsional yang tidak jelas: Terkadang Anda memiliki pengelompokan paren ( ) dalam pola, tetapi yang tidak ingin Anda ekstrak. Dalam hal ini, tulis tanda kurung dengan ?: di awal, misalnya (?: ) dan kurung kiri tersebut tidak akan dihitung sebagai hasil grup.)
Alur Kerja RE dan Debug
Pola ekspresi reguler mengemas banyak makna menjadi beberapa karakter saja , tetapi pola ini sangat padat, sehingga Anda bisa menghabiskan banyak waktu untuk men-debug pola. Siapkan runtime sehingga Anda dapat menjalankan pola dan mencetak pola yang cocok dengan mudah, misalnya dengan menjalankannya pada teks uji coba kecil dan mencetak hasil findall(). Jika pola tidak cocok, coba perlemahkan pola dan hapus sebagian pola agar Anda mendapatkan terlalu banyak kecocokan. Jika tidak ada yang cocok, Anda tidak dapat membuat kemajuan karena tidak ada hal konkret untuk dilihat. Setelah terlalu banyak kecocokan, Anda dapat mengencangkannya secara bertahap untuk mencapai apa yang Anda inginkan.
Opsi
Fungsi {i>re <i}mengambil opsi untuk mengubah perilaku kecocokan pola. Tanda opsi ditambahkan sebagai argumen tambahan ke search() atau findall() dll., misalnya re.search(pat, str, re.IGNORECASE).
- Abaikan -- abaikan perbedaan huruf besar/kecil untuk pencocokan, sehingga 'a' cocok dengan 'a' dan 'A'.
- DOTALL -- mengizinkan titik (.) untuk mencocokkan newline -- biasanya cocok dengan apa pun selain baris baru. Hal ini dapat membuat Anda tersandung - Anda pikir .* cocok dengan semuanya, tetapi secara default tidak melewati akhir baris. Perhatikan bahwa \s (spasi kosong) menyertakan baris baru, jadi jika Anda ingin mencocokkan sekumpulan spasi kosong yang mungkin menyertakan baris baru, Anda cukup menggunakan \s*
- MULTILINE -- Dalam string yang terdiri dari banyak baris, bolehkan ^ dan $ untuk mencocokkan awal dan akhir setiap baris. Biasanya ^/$ akan cocok dengan awal dan akhir dari seluruh string.
Serakah vs. Tidak Serakah (opsional)
Ini adalah bagian opsional yang menunjukkan teknik ekspresi reguler lebih canggih yang tidak diperlukan untuk latihan.
Misalkan Anda memiliki teks dengan tag di dalamnya: <b>foo</b> dan <i>selanjutnya</i>
Misalnya Anda mencoba mencocokkan setiap tag dengan pola '(<.*>)' -- apa yang cocok terlebih dahulu?
Hasilnya sedikit mengejutkan, tetapi aspek .* yang berlebihan membuatnya cocok dengan keseluruhan '<b>foo</b> dan <i>selanjutnya</i>' sebagai satu pertandingan besar. Masalahnya adalah .* berjalan sejauh mungkin, bukannya berhenti di awal > (alias "serakah").
Ada ekstensi untuk ekspresi reguler di mana Anda menambahkan ? di akhir, seperti .*? atau .+?, mengubahnya menjadi tidak serakah. Sekarang mereka berhenti sesegera mungkin. Jadi polanya '(<.*?>)' akan mendapatkan '<b>' sebagai kecocokan pertama, dan '</b>' sebagai kecocokan kedua, dan seterusnya untuk mendapatkan setiap <..> secara bergantian. Gayanya biasanya Anda menggunakan .*? segera diikuti oleh beberapa penanda konkret (> dalam hal ini) yang menjadi tujuan dari operasi harus diperpanjang.
Tanda *? berasal dari Perl, dan ekspresi reguler yang menyertakan ekstensi Perl dikenal sebagai Ekspresi Reguler yang Kompatibel dengan Perl -- pcre. Python menyertakan dukungan PCre. Banyak command line utils dll. memiliki flag di mana mereka menerima pola pcre.
Teknik lama yang banyak digunakan untuk mengodekan ide "semua karakter ini kecuali berhenti di X" menggunakan gaya {i>square-bracket<i}. Untuk hal di atas, Anda dapat menulis polanya, tetapi alih-alih menggunakan .* untuk mendapatkan semua karakter, gunakan [^>]* yang melewati semua karakter yang bukan > (awal ^ "membalikkan" kumpulan kurung siku, sehingga cocok dengan karakter apa pun yang tidak berada dalam tanda kurung).
Substitusi (opsional)
Fungsi re.sub(pat, replace, str) mencari semua instance pola dalam {i>string<i} tertentu, dan menggantikannya. String pengganti dapat mencakup '\1', '\2' yang mengacu pada teks dari grup(1), grup(2), dan seterusnya dari teks asli yang cocok.
Berikut ini contoh yang mencari semua alamat email, dan mengubahnya untuk mempertahankan pengguna (\1), tetapi memiliki yo-yo-dyne.com sebagai host.
str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher' ## re.sub(pat, replacement, str) -- returns new string with all replacements, ## \1 is group(1), \2 group(2) in the replacement print(re.sub(r'([\w\.-]+)@([\w\.-]+)', r'\1@yo-yo-dyne.com', str)) ## purple alice@yo-yo-dyne.com, blah monkey bob@yo-yo-dyne.com blah dishwasher
Latihan
Untuk mempraktikkan ekspresi reguler, lihat Latihan Nama Bayi.