Kata pengantar
Baru -baru ini, saya menemukan bahwa kinerja keteraturan di JavaScript di beberapa tempat agak berbeda dari yang ada dalam bahasa atau alat lain, dan relatif alternatif. Meskipun hampir mustahil bagi Anda untuk menulisnya dan Anda hampir tidak dapat menggunakan aturan yang saya sebutkan di bawah, adalah baik untuk memahaminya.
Contoh kode dalam artikel ini dieksekusi dalam lingkungan JavaScript yang kompatibel dengan ES5. Dengan kata lain, kinerja dalam versi sebelum IE9, versi di sekitar FX4, dll. Cenderung berbeda dari apa yang saya sebutkan di bawah ini.
1. Kelas karakter kosong
Kelas karakter yang tidak mengandung [] apa pun disebut kelas empty char class kosong. Saya yakin Anda belum pernah mendengar orang lain menyebutnya karena dalam bahasa lain, metode penulisan ini ilegal, dan semua dokumen dan tutorial tidak berbicara tentang sintaksis ilegal. Izinkan saya menunjukkan bagaimana bahasa atau alat lain melaporkan kesalahan ini:
$ echo | grep '[]' grep: tak tertandingi [atau [^$ echo | SED '/[]/' SED: -E Ekspresi #1, Karakter 4: Alamat Tanpa Isi Reguler Ekspresi $ Echo | AWK '/[]/' AWK: CMD. Baris: 1: /[] /AWK: CMD. Baris: 1: ^ Regexpawk yang tidak diakhirinya: cmd. Baris: 1: Kesalahan: tidak tertandingi [atau [^:/[] // $ echo | Perl -Ne '/[]/' tidak tertandingi [di Regex; Ditandai oleh <-di sini di m/ [<-di sini]/ at -e baris 1. $ echo | Ruby -ne '/[]/' -E: 1: Kosong -kelas arang kosong:/[]/$ python -c 'Impor re; re.match ("[]", "")' Traceback (Panggilan Terbaru Terakhir): File "<string>", baris 1, dalam <Module> File "E: /python/lib/re.py", lini 137,. "E: /python/lib/re.py", baris 244, dalam _compile raise error, v # tidak valid ekspresionre_constants.error: akhir tak terduga dari ekspresi reguler Dalam JavaScript, kelas karakter kosong adalah komponen reguler yang sah, tetapi efeknya "tidak pernah cocok", yaitu, semuanya akan gagal. Itu setara dengan efek (empty negative lookahead)(?!) :
js> "apapun/n" .match (/[]/g) // kelas karakter null, tidak pernah cocok dengan nulljs> "apapun/n" .match (/(?!)/g) // null ke depan negatif melihat sekeliling, tidak pernah cocok dengan nol null
Jelas, hal semacam ini tidak berguna dalam JavaScript.
2. Meninggalkan kelas karakter kosong
Kelas karakter negatif yang tidak mengandung karakter apa pun disebut kelas arang kosong negatif atau kelas char negatif kosong, baik, karena kata benda ini "dibuat sendiri" dan mirip dengan kelas karakter kosong yang disebutkan di atas. Metode penulisan ini juga ilegal dalam bahasa lain:
$ echo | grep '[^]' grep: tak tertandingi [atau [^$ echo | Sed '/[^]/' sed: -e ekspresi #1, karakter 5: Alamat tanpa akhir ekspresi reguler $ echo | AWK '/[^]/' AWK: CMD. Baris: 1: /[^] /AWK: CMD. Baris: 1: ^ Regexpawk yang tidak diakhirinya: cmd. Baris: 1: Kesalahan: tidak tertandingi [atau [^:/[^] // $ echo | Perl -Ne '/[^]/' tidak tertandingi [di Regex; Ditandai oleh <-di sini di m/ [<-di sini ^]/ at -e baris 1. $ echo | Ruby -ne '/[^]/' -E: 1: Kosong Kosong:/[^]/$ python -c 'Impor re; re.match ("[^]", "")' Traceback (Panggilan Terbaru Terakhir): File "<string>", baris 1, dalam <Modul> File "E: /python/lib.py",... File "e: /python/lib/re.py", baris 244, di _compile raise error, v # tidak valid ekspresi_constants.error: akhir tak terduga dari ekspresi reguler $ Dalam JavaScript, meniadakan kelas karakter nol adalah komponen reguler yang sah. Efeknya hanyalah kebalikan dari efek kelas karakter nol. Ini dapat mencocokkan karakter apa pun, termasuk garis baru "/n" , yaitu setara dengan [/s/S] dan [/w/W] yang umum:
JS> "apapun/n" .match (/[^]/g) // kelas karakter neizontal, cocokkan karakter apa pun ["w", "h", "a", "t", "e", "v", "e", "r", "/n"] js> "n" "t", "e", "v", "e", "r", "/n"]
Perlu dicatat bahwa itu tidak dapat disebut "keteraturan pencocokan permanen", karena kelas karakter harus memiliki karakter yang cocok. Jika string target kosong atau telah dikonsumsi oleh keteraturan kiri, pertandingan akan gagal, misalnya:
JS> /Abc^........
Jika Anda ingin mengetahui "aturan pencocokan permanen" yang sebenarnya, Anda dapat memeriksa artikel yang saya terjemahkan sebelumnya: aturan "kosong"
3. []] dan [^]]
Ini relatif sederhana, yaitu: dalam ekspresi reguler Perl dan beberapa perintah Linux lainnya, jika kelas karakter [] berisi braket kotak kanan segera setelah []] kotak kiri, braket kotak kanan akan dianggap sebagai karakter normal, yaitu, hanya bisa cocok "]". Dalam JavaScript, keteraturan ini akan diakui sebagai kelas karakter kosong diikuti oleh braket kotak kanan, dan kelas karakter kosong tidak akan cocok dengan apa pun .[^]] serupa: Dalam JavaScript, cocok dengan karakter sewenang-wenang (kelas karakter nol negatif) diikuti oleh braket kotak kanan, seperti "a]","b]" , sedangkan dalam bahasa lain, ia cocok dengan karakter yang tidak ada.
$ perl -e 'print "]" = ~/[]]/' 1 $ js -e 'print (/[]]/. test ("]"))' false $ perl -e 'cetak "x" = ~/[^]]/' 1 $ js -e 'cetak (/[^]]/. Test ("x"))' FALSE $ JS -E '4. $ Anchor Point
Beberapa pemula berpikir bahwa $ cocok dengan karakter baru "/n" , yang merupakan kesalahan besar. $ adalah pernyataan nol-lebar, tidak mungkin mencocokkan karakter yang nyata, itu hanya bisa cocok dengan satu posisi. Perbedaan yang ingin saya bicarakan terjadi dalam mode non-line: Anda mungkin berpikir bahwa dalam mode non-lini, bukankah $ mencocokkan posisi setelah karakter terakhir? Sebenarnya itu tidak sesederhana itu. Dalam sebagian besar bahasa lain, jika karakter terakhir dalam string target adalah karakter baru "/n" , $ juga akan cocok dengan posisi sebelum garis baru, yaitu, cocok dengan dua posisi di sisi kiri dan kanan garis istirahat di akhir. Banyak bahasa memiliki dua notasi /z dan /z. Jika Anda tahu perbedaan di antara mereka, Anda harus memahami bahwa dalam bahasa lain (Perl, Python, PHP, Java, C#...), $ dalam mode non-multi-line setara dengan /z, sedangkan dalam JavaScript, $ dalam mode non-line adalah setara dengan /z (itu hanya akan cocok dengan posisi terakhir, terlepas dari apakah karakter non-multi-line). Ruby adalah kasus khusus karena default ke mode multi-line. $ dalam mode multi-line akan cocok dengan posisi sebelum setiap baris baru, dan tentu saja itu juga akan mencakup jeda garis yang mungkin muncul di akhir. Buku Yu Sheng "Pedoman Reguler" juga berbicara tentang poin -poin ini.
$ perl -e 'cetak "apapun/n" = ~ s/$/ganti karakter/rg' // Penggantian global Karakter apa pun // Posisi sebelum jeda garis digantikan oleh karakter pengganti // Posisi setelah jeda garis digantikan oleh cetak $ JS -E).
5. DOT METACHARACTOR "."
Dalam ekspresi reguler di JavaScript, DOT Metacharacter "." dapat mencocokkan semua karakter kecuali empat terminator baris ( /r-carriage return, /n-line newline, /u2028-line separator, /u2029-paragraph pemisah), sedangkan dalam bahasa umum lainnya, hanya baris baru /n yang akan dikecualikan.
6. Kutipan ke depan
Kita semua tahu bahwa ada referensi belakang secara teratur, yaitu, referensi angka backslash + ke string yang telah cocok dalam grup penangkapan sebelumnya. Tujuannya adalah untuk mencocokkan lagi atau sebagai hasil penggantian (/ menjadi $). Tetapi ada kasus khusus bahwa jika kelompok penangkapan yang dirujuk belum dimulai (braket kiri dibatasi), ia menggunakan referensi belakang, apa yang akan terjadi? Misalnya, reguler /(/2(a)){2}/ , (a) adalah grup penangkapan kedua, tetapi hasil pencocokannya digunakan di sisi kirinya. Kita tahu bahwa pertandingan reguler dari kiri ke kanan. Ini adalah asal usul referensi judul ke depan di bagian ini. Ini bukan konsep yang ketat. Jadi sekarang Anda memikirkannya, apa yang akan dikembalikan kode JavaScript berikut:
js>/(/2 (a)) {2}/. exec ("aaa") ???Sebelum menjawab pertanyaan ini, mari kita lihat kinerja dalam bahasa lain. Demikian pula, dalam bahasa lain, menulis dengan cara ini pada dasarnya tidak valid:
$ echo aaa | grep '(/2 (a)) {2}' grep: referensi punggung tidak valid $ echo aaa | Sed -r '/(/2 (a)) {2}/' sed: -e ekspresi #1, karakter 12: referensi punggung ilegal $ echo aaa | awk '/(/2 (a)) {2}/' $ echo aaa | Perl -ne 'print/(/2 (a)) {2}/' $ echo aaa | ruby -ne 'cetak $ _ = ~/(/2 (a)) {2}/' $ python -c 'import re; print re.match ("(/2 (a)) {2}", "aaa")' tidak adaTidak ada kesalahan dalam AWK karena AWK tidak mendukung refreferensi ini, dan /2 ditafsirkan sebagai karakter dengan ASCII Code 2. Namun, tidak ada kesalahan dalam Perl Ruby Python. Saya tidak tahu mengapa desain ini harus dipelajari oleh Perl, tetapi efeknya sama. Dalam hal ini, tidak mungkin untuk dicocokkan dengan sukses.
Dalam JavaScript, tidak hanya tidak melaporkan kesalahan, tetapi juga bisa cocok dengan itu dengan sukses. Mari kita lihat bahwa jawabannya sama dengan yang Anda pikirkan:
js> /(/2(a)) {2}/.exec("aaa").... "aa "," a "," a "] Untuk mencegah Anda melupakan apa hasilnya dikembalikan dengan metode exec , izinkan saya mengatakan. Elemen pertama adalah string pencocokan lengkap, yaitu, RegExp["$&"] , diikuti oleh konten masing -masing pencocokan grup penangkapan, yaitu, RegExp.$1 dan RegExp.$2. Mengapa pencocokan bisa berhasil? Apa proses pencocokannya? Pemahaman saya adalah:
Pertama, kami memasuki grup penangkapan pertama (braket kiri paling kiri), di mana pertandingan yang valid pertama adalah /2, tetapi pada saat ini grup penangkapan kedua (a) belum ada di putaran, jadi nilai RegExp.$2 masih undefined , jadi /2 cocok dengan karakter kosong di sebelah kiri A di string target, atau "posisi" sama seperti ^ dan ^ dan ^ dan lainnya ^ dan lainnya. Intinya adalah pertandingan berhasil. Lanjutkan untuk pergi, dan kemudian grup penangkapan kedua (a) cocok dengan A pertama di string target, dan nilai RegExp.$2 juga ditugaskan untuk "A", dan kemudian kelompok penangkapan pertama berakhir (braket kiri paling kanan paling kanan paling kanan), nilai RegExp.$1 juga "A". Lalu ada quantifier {2}, yaitu, setelah A pertama di string target, putaran baru pencocokan reguler (/2(a)) dimulai. Poin kuncinya adalah di sini: nilai RegExp.$2 adalah bahwa nilai /2 cocok atau apakah nilai yang ditetapkan pada akhir putaran pertama pencocokan "A". Jawabannya adalah: "Tidak", nilai -nilai RegExp.$1 dan RegExp.$2 akan dihapus sebagai undefined , dan /1 dan /2 akan sama dengan pertama kali, berhasil mencocokkan karakter kosong (setara dengan tidak ada efek, apakah itu ditulis atau tidak). A kedua dalam string target berhasil dicocokkan, dan nilai -nilai RegExp.$1 dan RegExp.$2 menjadi "A" lagi, nilai RegExp["$&"] menjadi string pencocokan lengkap, dua A pertama: "aa".
Dalam versi Firefox sebelumnya (3.6), pertandingan ulang kuantifikasi tidak akan menghapus nilai dari kelompok yang ditangkap yang ada, sehingga pada babak kedua pertandingan, /2 akan cocok dengan A kedua, dengan demikian: dengan demikian:
js> /(/2(a)) {2}/.exec("aaa") media'aaa "," a "]Selain itu, akhir dari kelompok penangkapan tergantung pada apakah braket penutup ditutup. Misalnya,/(a/1) {3}/. Meskipun kelompok penangkapan pertama sudah mulai cocok ketika /1 digunakan, itu belum berakhir. Ini juga merupakan referensi ke depan, sehingga kecocokan antara /1 masih kosong:
js> /(a/1) {3}/.exec("aaa") media"aaa "," a "]Contoh lain:
JS> /(?:(f)(o)(o)|(b)(a)(r))*/.exec("foobar") media"foobar ", tidak terdefinisi, tidak terdefinisi, tidak terdefinisi," B "," A "," R "] * adalah kuantifikasi. Setelah putaran pertama pencocokan: $ 1 adalah "F", $ 2 adalah "O", $ 3 adalah "O", $ 4 tidak ditentukan, $ 5 undefined , dan $ 6 undefined .
Pada awal babak kedua pertandingan: semua nilai yang ditangkap diatur ulang untuk undefined .
Setelah babak kedua pertandingan: $ 1 undefined , $ 2 undefined , $ 3 undefined , $ 4 adalah "B", $ 5 adalah "A", dan $ 6 adalah "R".
& ditugaskan sebagai "foobar", dan pertandingan berakhir.
Meringkaskan
Di atas adalah seluruh konten yang merangkum perbedaan antara keteraturan JavaScript dan bahasa lain. Saya berharap konten artikel ini akan membantu untuk belajar dan bekerja semua orang.