Saya menemukan tutorial yang sangat baik ini pada ekspresi reguler dan sementara saya secara intuitif memahami apa yang "serakah", "segan" dan "posesif" lakukan, tampaknya ada lubang serius dalam pemahaman saya.
Secara khusus, dalam contoh berikut:
Enter your regex: .*foo // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.
Enter your regex: .*?foo // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.
Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.
Penjelasan menyebutkan memakan seluruh string input, surat telah dikonsumsi , pencocokan mundur , kejadian "foo" paling kanan telah dimuntahkan , dll.
Sayangnya, terlepas dari metafora yang bagus, saya masih tidak mengerti apa yang dimakan oleh siapa ... Apakah Anda tahu tutorial lain yang menjelaskan (ringkas) bagaimana mesin ekspresi reguler bekerja?
Atau, jika seseorang dapat menjelaskan dalam frasa yang agak berbeda pada paragraf berikut, itu akan sangat dihargai:
Contoh pertama menggunakan quantifier serakah. * Untuk menemukan "apa pun", nol atau lebih kali, diikuti dengan huruf "f" "o" "o". Karena quantifier itu serakah, bagian. * Dari ekspresi pertama-tama memakan seluruh string input. Pada titik ini, ekspresi keseluruhan tidak dapat berhasil, karena tiga huruf terakhir ("f" "o" "o") telah dikonsumsi ( oleh siapa? ). Jadi pencocokan perlahan-lahan mundur ( dari kanan ke kiri? ) Satu huruf pada satu waktu sampai kemunculan paling kanan dari "foo" telah dimuntahkan ( apa artinya ini? ), Pada titik mana pertandingan berhasil dan pencarian berakhir.
Contoh kedua, bagaimanapun, adalah enggan, jadi itu dimulai dengan mengkonsumsi pertama ( oleh siapa? ) "Tidak ada". Karena "foo" tidak muncul di awal string, terpaksa menelan ( siapa yang menelan?) Huruf pertama ("x"), yang memicu kecocokan pertama pada 0 dan 4. Uji harness kami melanjutkan proses sampai string input habis. Ia menemukan kecocokan lain pada 4 dan 13.
Contoh ketiga gagal menemukan kecocokan karena kuantifier bersifat posesif. Dalam kasus ini, seluruh string input dikonsumsi oleh. * +, ( Bagaimana? ) Tidak menyisakan apa pun untuk memuaskan "foo" di akhir ekspresi. Gunakan quantifier posesif untuk situasi di mana Anda ingin merebut semua sesuatu tanpa pernah mundur ( apa artinya mundur? ); itu akan mengungguli quantifier serakah yang setara dalam kasus di mana pertandingan tidak segera ditemukan.
sumber
*
,+
dan?
yang serakah. Minimal bilangan suka*?
,+?
dan??
yang malas. Posesif bilangan suka*+
,++
dan?+
yang lengket.Jawaban:
Saya akan mencobanya.
Sebuah serakah quantifier pertandingan pertama sebanyak mungkin. Jadi
.*
cocok dengan seluruh string. Kemudian pencocokan mencoba untuk mencocokkan yangf
berikut, tetapi tidak ada karakter yang tersisa. Jadi itu "backtracks", membuat quantifier serakah cocok dengan satu karakter kurang (meninggalkan "o" pada akhir string yang tak tertandingi). Itu masih belum cocok denganf
di regex, jadi backtracks satu langkah lagi, membuat quantifier serakah cocok dengan satu karakter lagi (meninggalkan "oo" pada akhir string tidak tertandingi). Itu masih tidak cocok denganf
di regex, sehingga backtracks satu langkah lagi (meninggalkan "foo" di akhir string tidak tertandingi). Sekarang, matcher akhirnya cocok denganf
di regex,o
o
cocok juga. Keberhasilan!Kuantitatif yang enggan atau "tidak rakus" pertama-tama cocok dengan sesedikit mungkin. Jadi
.*
tidak ada yang cocok pada awalnya, meninggalkan seluruh string yang tak tertandingi. Kemudian pencocokan mencoba untuk mencocokkan yangf
berikut ini, tetapi bagian yang tidak cocok dari string dimulai dengan "x" sehingga tidak berfungsi. Jadi backtracks matcher, membuat quantifier non-serakah mencocokkan satu karakter lagi (sekarang cocok dengan "x", meninggalkan "fooxxxxxxfoo" tak tertandingi). Kemudian mencoba untuk mencocokkanf
, yang berhasil,o
dan yang berikutnyao
dalam pertandingan regex juga. Keberhasilan!Dalam contoh Anda, ia kemudian memulai proses dengan bagian yang tersisa dari string, "xxxxxxfoo", mengikuti proses yang sama.
Sebuah posesif pembilang adalah seperti quantifier serakah, tapi tidak backtrack. Jadi itu dimulai dengan
.*
mencocokkan seluruh string, tanpa meninggalkan apa pun yang tak tertandingi. Maka tidak ada yang tersisa untuk itu agar sesuai denganf
di regex. Karena quantifier posesif tidak mundur, pertandingan gagal di sana.sumber
.*+
(perhatikan "+").*+
cocok dengan semuanya. Misalnya, jika Anda memiliki pola[xyz]*foo
, tidak mungkin melacak x, y, dan z yang cocok dengan[xyz]*
bit akan memungkinkanfoo
bit berikut cocok, sehingga Anda dapat mempercepatnya dengan membuatnya posesif.Hanya output latihan saya untuk memvisualisasikan adegan-
sumber
EXPRESSION .*?foo
(), bukankah[f] [o] [o]
persegi panjang harus berwarna kuning5th pass
?.*?foo
dan.*+foo
.Saya belum pernah mendengar istilah yang tepat 'memuntahkan' atau 'mundur' sebelumnya; frasa yang akan menggantikan ini adalah "backtracking", tetapi 'memuntahkan' sepertinya sama bagusnya dengan "konten yang telah diterima secara tentatif sebelum backtracking membuangnya lagi".
Hal penting untuk disadari tentang kebanyakan mesin regex adalah bahwa mereka mundur : mereka secara tentatif akan menerima kecocokan parsial yang potensial, sambil mencoba untuk mencocokkan seluruh isi regex. Jika regex tidak dapat sepenuhnya cocok pada upaya pertama, maka mesin regex akan mundur pada salah satu pertandingannya. Ini akan mencoba pencocokan
*
,+
,?
, pergantian, atau{n,m}
pengulangan berbeda, dan coba lagi. (Dan ya, proses ini bisa memakan waktu lama.)Tiga huruf terakhir,
f
,o
, dano
sudah dikonsumsi oleh awal.*
porsi aturan. Namun, elemen berikutnya dalam regex,,f
tidak memiliki apa pun yang tersisa di string input. Mesin akan dipaksa untuk mundur pada.*
pertandingan awal , dan mencoba mencocokkan karakter semua-tapi-yang terakhir. (Mungkin cerdas dan mundur ke all-but-the-last-three, karena memiliki tiga istilah literal, tapi saya tidak mengetahui detail implementasi pada level ini.)Ini berarti
foo
sudah tentatif termasuk saat pencocokan.*
. Karena upaya itu gagal, mesin regex mencoba menerima satu karakter lebih sedikit di.*
. Jika ada pertandingan sukses sebelum yang.*
dalam contoh ini, maka mesin akan mungkin mencoba memperpendek.*
pertandingan (dari kanan-ke-kiri, seperti yang Anda menunjukkan, karena merupakan kualifikasi serakah), dan jika tidak mampu pertandingan seluruh masukan, maka mungkin akan dipaksa untuk kembali mengevaluasi apa yang telah dicocokkan sebelum yang.*
dalam contoh hipotetis saya.Tidak ada awal yang dikonsumsi oleh
.?*
, yang akan mengkonsumsi jumlah sesingkat mungkin dari apa pun yang memungkinkan sisa regex untuk mencocokkan.Sekali lagi
.?*
mengkonsumsi karakter pertama, setelah mundur pada kegagalan awal untuk mencocokkan seluruh regex dengan pertandingan sesingkat mungkin. (Dalam hal ini, mesin regex memperpanjang pertandingan untuk.*?
dari kiri ke kanan, karena.*?
enggan.)A
.*+
akan mengkonsumsi sebanyak mungkin, dan tidak akan mundur untuk menemukan kecocokan baru ketika regex secara keseluruhan gagal menemukan kecocokan. Karena bentuk posesif tidak melakukan backtracking, Anda mungkin tidak akan melihat banyak kegunaan dengan.*+
, melainkan dengan kelas karakter atau pembatasan serupa:account: [[:digit:]]*+ phone: [[:digit:]]*+
.Ini secara drastis dapat mempercepat pencocokan regex, karena Anda memberi tahu mesin regex bahwa ia tidak boleh mundur dari pencocokan potensial jika input tidak cocok. (Jika Anda harus menulis semua kode yang cocok dengan tangan, ini akan sama dengan tidak pernah menggunakan
putc(3)
untuk 'mendorong kembali' karakter input. Ini akan sangat mirip dengan kode naif yang mungkin ditulis pada percobaan pertama. Kecuali mesin regex adalah jauh lebih baik daripada satu karakter push-back, mereka dapat memundurkan semua kembali ke nol dan coba lagi. :)Tetapi lebih dari potensi percepatan, ini juga dapat membuat Anda menulis regex yang cocok dengan apa yang Anda butuhkan untuk mencocokkan. Saya mengalami masalah dengan contoh yang mudah :) tetapi menulis regex menggunakan quantif vs serakah quantifiers dapat memberi Anda kecocokan yang berbeda, dan satu atau yang lain mungkin lebih tepat.
"Mundur" dalam konteks ini berarti "mundur" - membuang pertandingan parsial sementara untuk mencoba pertandingan parsial lain, yang mungkin berhasil atau tidak.
sumber
http://swtch.com/~rsc/regexp/regexp1.html
Saya tidak yakin itu penjelasan terbaik di internet, tetapi itu ditulis dengan cukup baik dan detail yang tepat, dan saya terus kembali ke sana. Anda mungkin ingin memeriksanya.
Jika Anda menginginkan tingkat yang lebih tinggi (penjelasan yang kurang rinci), untuk ekspresi reguler sederhana seperti yang Anda lihat, mesin ekspresi reguler berfungsi dengan menelusuri ulang. Pada dasarnya, ia memilih ("makan") bagian dari string dan mencoba untuk mencocokkan ekspresi reguler terhadap bagian itu. Jika cocok, bagus. Jika tidak, mesin mengubah pilihan bagian string dan mencoba mencocokkan regexp dengan bagian itu, dan seterusnya, hingga dicoba setiap pilihan yang memungkinkan.
Proses ini digunakan secara rekursif: dalam usahanya untuk mencocokkan string dengan ekspresi reguler yang diberikan, mesin akan membagi ekspresi reguler menjadi potongan-potongan dan menerapkan algoritma untuk setiap bagian secara individual.
Perbedaan antara quantif serakah, enggan, dan posesif masuk ketika mesin membuat pilihan bagian tali mana yang harus dicocokkan, dan bagaimana memodifikasi pilihan itu jika tidak bekerja pertama kali. Aturannya adalah sebagai berikut:
Kuantitatif serakah memberi tahu mesin untuk memulai dengan seluruh string (atau setidaknya, semua yang belum cocok dengan bagian sebelumnya dari ekspresi reguler) dan periksa apakah cocok dengan regexp. Jika demikian, bagus; mesin dapat melanjutkan sisa regexp. Jika tidak, ia mencoba lagi, tetapi memangkas satu karakter (yang terakhir) dari bagian string yang akan diperiksa. Jika itu tidak berhasil, itu memangkas karakter lain, dll. Jadi quantifier rakus memeriksa kemungkinan yang cocok dalam urutan dari terpanjang ke terpendek.
Pengukur enggan memberi tahu mesin untuk memulai dengan seutas tali yang sesingkat mungkin. Jika cocok, mesin dapat melanjutkan; jika tidak, ia menambahkan satu karakter ke bagian string yang sedang diperiksa dan mencobanya, dan seterusnya sampai menemukan kecocokan atau seluruh string telah digunakan. Jadi quantifier yang enggan memeriksa kemungkinan kecocokan secara berurutan dari yang terpendek ke yang terpanjang.
Sebuah quantifier posesif adalah seperti quantifier serakah pada upaya pertama: itu memberitahu mesin untuk memulai dengan memeriksa seluruh string. Perbedaannya adalah bahwa jika tidak berhasil, quantifier posesif melaporkan bahwa kecocokan gagal saat itu juga. Mesin tidak mengubah bagian dari string yang sedang dilihat, dan tidak membuat upaya lagi.
Inilah sebabnya mengapa kecocokan kuantifier gagal dalam contoh Anda:
.*+
dicentang terhadap seluruh string, yang cocok, tetapi kemudian mesin melanjutkan untuk mencari karakter tambahanfoo
setelah itu - tetapi tentu saja tidak menemukannya, karena Anda Sudah ada di akhir string. Jika itu adalah pengukur serakah, itu akan mundur dan mencoba membuat.*
satu - satunya yang cocok hingga karakter berikutnya-terakhir, kemudian hingga karakter ketiga hingga terakhir, kemudian hingga karakter keempat hingga terakhir, yang berhasil karena baru kemudian adafoo
kiri setelah.*
"makan" bagian awal dari string.sumber
Inilah yang saya ambil menggunakan posisi Sel dan Indeks (Lihat diagram di sini untuk membedakan Sel dari Indeks).
Serakah - Cocokkan sebanyak mungkin dengan quantifier serakah dan seluruh regex. Jika tidak ada kecocokan, mundur pada quantifier serakah.
Input String: xfooxxxxxxfoo
Regex:. * Foo
Regex di atas memiliki dua bagian:
(i) '. *' Dan
(ii) 'foo'
Setiap langkah di bawah ini akan menganalisis dua bagian. Komentar tambahan untuk kecocokan dengan 'Lulus' atau 'Gagal' dijelaskan dalam kurung kurawal.
Langkah 1:
(i). * = Xfooxxxxxxfoo - PASS ('. *' Adalah quantifier serakah dan akan menggunakan seluruh String Input)
(ii) foo = Tidak ada karakter yang tersisa untuk dicocokkan setelah indeks 13 - FAIL
Match gagal.
Langkah 2:
(i). * = Xfooxxxxxxfo - PASS (Mundur dari pengukur serakah '. *')
(Ii) foo = o - GAGAL
Pertandingan gagal.
Langkah 3:
(i). * = Xfooxxxxxxf - PASS (Mundur dari pengukur serakah '. *')
(Ii) foo = oo - GAGAL
Pertandingan gagal.
Langkah 4:
(i). * = Xfooxxxxxx - PASS (Mundur dari pengukur serakah '. *')
(Ii) foo = foo -
Laporan LULUS PERTANDINGAN
Hasil: 1 match (es)
Saya menemukan teks "xfooxxxxxxfoo" mulai dari indeks 0 dan berakhir pada indeks 13.
Enggan - Cocokkan sesedikit mungkin dengan kuantifier enggan dan cocokkan seluruh regex. jika tidak ada kecocokan, tambahkan karakter ke quantifier yang enggan.
Input String: xfooxxxxxxfoo
Regex:. *? Foo
Regex di atas memiliki dua bagian:
(i) '. *?' dan
(ii) 'foo'
Langkah 1:.
*? = '' (kosong) - LULUS (Cocokkan sesedikit mungkin dengan kuantifier enggan '. *?'. Indeks 0 memiliki '' adalah kecocokan.)
foo = xfo - FAIL (Sel 0,1,2 - yaitu indeks antara 0 dan 3)
Pertandingan gagal.
Langkah 2:.
*? = x - LULUS (Tambahkan karakter ke quantifier enggan '. *?'. Sel 0 memiliki 'x' adalah kecocokan.)
foo = foo - LULUS
Laporan MATCH
Langkah 3:.
*? = '' (kosong) - LULUS (Cocokkan sesedikit mungkin dengan kuantifier enggan '. *?'. Indeks 4 memiliki '' adalah kecocokan.)
foo = xxx - GAGAL (Sel 4,5,6 - yaitu indeks antara 4 dan 7)
Pertandingan gagal.
Langkah 4:.
*? = x - LULUS (Tambahkan karakter ke kuantifier enggan '. *?'. Sel 4.)
foo = xxx - FAIL (Sel 5,6,7 - yaitu indeks antara 5 dan 8)
Pencocokan gagal.
Langkah 5:.
*? = xx - LULUS (Tambahkan karakter ke kuantifier enggan '. *?'. Sel 4 sampai 5.)
foo = xxx - GAGAL (Sel 6,7,8 - yaitu indeks antara 6 dan 9)
Pencocokan gagal.
Langkah 6:.
*? = xxx - LULUS (Tambahkan karakter ke kuantifier enggan '. *?'. Sel 4 sampai 6.)
foo = xxx - GAGAL (Sel 7,8,9 - yaitu indeks antara 7 dan 10)
Pencocokan gagal.
Langkah 7:.
*? = xxxx - LULUS (Tambahkan karakter ke kuantifier enggan '. *?'. Sel 4 sampai 7.)
foo = xxf - FAIL (Sel 8,9,10 - yaitu indeks antara 8 dan 11)
Pencocokan gagal.
Langkah 8:.
*? = xxxxx - LULUS (Tambahkan karakter ke kuantifier enggan '. *?'. Sel 4 sampai 8.)
foo = xfo - FAIL (Sel 9,10,11 - yaitu indeks antara 9 dan 12)
Pencocokan gagal.
Langkah 9:.
*? = xxxxxx - LULUS (Tambahkan karakter ke kuantifier enggan '. *?'. Sel 4 sampai 9.)
foo = foo - LULUS (Sel 10,11,12 - yaitu indeks antara 10 dan 13)
Laporkan MATCH
Langkah 10:.
*? = '' (kosong) - LULUS (Cocokkan sesedikit mungkin dengan quantifier yang enggan '. *?'. Indeks 13 kosong.)
foo = Tidak ada karakter yang tersisa untuk dicocokkan - GAGAL (Tidak ada apa pun setelah indeks 13 yang cocok)
Cocok gagal.
Hasil: 2 match (es)
Saya menemukan teks "xfoo" mulai dari indeks 0 dan berakhir pada indeks 4.
Saya menemukan teks "xxxxxxfoo" mulai dari indeks 4 dan berakhir pada indeks 13.
Posesif - Cocokkan sebanyak mungkin dengan kuantifer posesif dan cocokkan dengan seluruh regex. JANGAN mundur.
Input String: xfooxxxxxxfoo
Regex:. * + Foo
Regex di atas memiliki dua bagian: '. * +' Dan 'foo'.
Langkah 1:.
* + = Xfooxxxxxxfoo - PASS (Cocokkan sebanyak mungkin dengan quantifier posesif '. *')
Foo = Tidak ada karakter yang tersisa untuk dicocokkan - GAGAL (Tidak ada yang cocok dengan indeks 13)
Pencocokan gagal.
Catatan: Mundur tidak diizinkan.
Hasil: 0 pertandingan
sumber
Serakah: "cocok dengan urutan karakter terpanjang yang mungkin"
Enggan: "cocok dengan urutan karakter yang sesingkat mungkin"
Posesif: Ini agak aneh karena TIDAK (berbeda dengan serakah dan enggan) mencoba menemukan kecocokan untuk seluruh regex.
Omong-omong: Tidak ada implementasi pencocokan pola regex yang akan menggunakan backtracking. Semua pencocokan pola kehidupan nyata sangat cepat - hampir tidak tergantung pada kompleksitas ekspresi reguler!
sumber
Kuantifikasi Serakah melibatkan pencocokan pola menggunakan semua karakter string yang tidak divalidasi yang tersisa selama iterasi. Karakter yang tidak divalidasi mulai dalam urutan aktif . Setiap kali pertandingan tidak terjadi, karakter di akhir dikarantina dan cek dilakukan lagi.
Ketika hanya kondisi utama dari pola regex dipenuhi oleh urutan aktif, upaya dilakukan untuk memvalidasi kondisi yang tersisa terhadap karantina. Jika validasi ini berhasil, karakter yang cocok dalam karantina divalidasi dan sisa karakter yang tidak cocok tetap tidak divalidasi dan akan digunakan ketika proses dimulai lagi di iterasi berikutnya.
Alur karakter dari urutan aktif ke karantina. Perilaku yang dihasilkan adalah sebanyak mungkin urutan asli termasuk dalam kecocokan.
Kuantitatif enggan sebagian besar sama dengan kualifikasi serakah kecuali aliran karakter adalah sebaliknya - yaitu, mereka mulai di karantina dan mengalir ke urutan aktif . Perilaku yang dihasilkan adalah bahwa sesedikit mungkin urutan asli termasuk dalam kecocokan.
Kuantifikasi Posesif tidak memiliki karantina dan memasukkan semuanya dalam urutan aktif yang tetap .
sumber