Apa perbedaan antara tanda kurung siku dan tanda kurung dalam ekspresi reguler?

101

Berikut adalah ekspresi reguler yang saya buat untuk digunakan dalam JavaScript:

var reg_num = /^(7|8|9)\d{9}$/

Ini satu lagi yang disarankan oleh anggota tim saya.

var reg_num = /^[7|8|9][\d]{9}$/

Aturannya adalah memvalidasi nomor telepon:

  • Seharusnya hanya sepuluh angka.
  • Angka pertama seharusnya salah satu dari 7, 8 atau 9.
Jayapal Chandran
sumber

Jawaban:

124

Ekspresi reguler ini setara (untuk tujuan pencocokan):

  • /^(7|8|9)\d{9}$/
  • /^[789]\d{9}$/
  • /^[7-9]\d{9}$/

Penjelasan:

  • (a|b|c)adalah regex "OR" dan berarti "a atau b atau c", meskipun kehadiran tanda kurung, yang diperlukan untuk OR, juga menangkap digit tersebut. Agar benar-benar setara, Anda akan (?:7|8|9)membuat kode untuk menjadikannya grup non- penangkap.

  • [abc]adalah "kelas karakter" yang berarti "karakter apa pun dari a, b, atau c" (kelas karakter dapat menggunakan rentang, misalnya [a-d]= [abcd])

Alasan persamaan ekspresi reguler ini adalah karena kelas karakter adalah singkatan dari "atau" (tetapi hanya untuk karakter tunggal). Sebagai gantinya, Anda juga dapat melakukan sesuatu seperti (abc|def)yang tidak diterjemahkan ke kelas karakter.

Bohemian
sumber
30
(7|8|9)dan [789]tidak setara, karena yang pertama menangkap, yang terakhir tidak. (?:7|8|9)akan menjadi setara di sisi lain (saya kira Anda tahu itu tentu saja ...).
hochl
Saya melihat regex ini: [<<|>>|\]\]|\[\[]. Karena konteksnya, saya tahu bahwa ekspresi reguler mencoba mencocokkan <<atau >>atau [[atau ]]. Tapi dari apa yang Anda katakan, itu harus cocok <atau >atau [atau ]. Jika Anda menggunakan |antara [], apakah tanda kurung berperilaku berbeda?
Daniel Kaplan
1
@DanielKaplan tidak digunakan |dalam kelas karakter [...], kecuali Anda ingin mencocokkan karakter pipa itu sendiri. Juga menduplikasi karakter dalam kelas karakter tidak akan berpengaruh - kelas karakter adalah daftar karakter dan akan cocok persis dengan salah satunya. Dugaan saya adalah Anda menginginkan grup , yang menggunakan tanda kurung bulat normal:(<<|>>|\]\]|\[\[)
Bohemian
57

Saran tim Anda hampir benar, kecuali kesalahan yang telah dibuat. Begitu Anda mengetahui alasannya, Anda tidak akan pernah melupakannya. Perhatikan kesalahan ini.

/^(7|8|9)\d{9}$/

Apa fungsinya:

  • ^dan $menunjukkan kecocokan berlabuh, yang menegaskan bahwa subpola di antara jangkar ini adalah kecocokan keseluruhan. String hanya akan cocok jika subpola cocok dengan keseluruhannya, bukan hanya satu bagian.
  • ()menunjukkan grup penangkap .
  • 7|8|9menunjukkan pencocokan baik dari 7, 8atau 9. Ini dilakukan dengan pergantian , yang dilakukan oleh operator pipa |- bergantian di antara pergantian. Ini backtrack antara pergantian: Jika pergantian pertama tidak cocok, mesin harus kembali sebelum lokasi penunjuk dipindahkan selama pertandingan pergantian, untuk terus mencocokkan pergantian berikutnya; Sedangkan kelas karakter bisa maju secara berurutan. Lihat pertandingan ini di mesin regex dengan pengoptimalan dinonaktifkan:
Pattern: (r|f)at
Match string: carat

pergantian

Pattern: [rf]at
Match string: carat

kelas

  • \d{9}cocok dengan sembilan digit. \dadalah karakter meta singkat, yang cocok dengan sembarang digit.
/^[7|8|9][\d]{9}$/

Lihat apa fungsinya:

  • ^dan $menunjukkan pertandingan berlabuh juga.
  • [7|8|9]adalah kelas karakter . Karakter dari daftar 7, |, 8, |, atau 9dapat dicocokkan, sehingga |ditambahkan di salah. Ini cocok tanpa mundur.
  • [\d]adalah kelas karakter yang mendiami metakarakter \d. Ngomong-ngomong, kombinasi penggunaan kelas karakter dan satu karakter meta adalah ide yang buruk, karena lapisan abstraksi dapat memperlambat pertandingan, tetapi ini hanya detail implementasi dan hanya berlaku untuk beberapa implementasi regex. JavaScript bukanlah satu, tetapi membuat subpola sedikit lebih panjang.
  • {9} menunjukkan konstruk tunggal sebelumnya diulang sembilan kali secara total.

Regex optimal adalah /^[789]\d{9}$/, karena /^(7|8|9)\d{9}$/menangkap secara tidak perlu yang menyebabkan penurunan performa pada sebagian besar implementasi regex (kebetulan menjadi satu, mengingat pertanyaannya menggunakan kata kunci vardalam kode, ini mungkin JavaScript). Penggunaanyang berjalan pada PCRE untuk pencocokan preg akan mengoptimalkan kekurangan backtracking, namun kami juga tidak menggunakan PHP, jadi menggunakan kelas []alih-alih pergantian |memberikan bonus kinerja karena pertandingan tidak mundur, dan karena itu keduanya cocok dan gagal lebih cepat daripada menggunakan Anda ekspresi reguler sebelumnya.

Unihedron
sumber
6
hanya karena tertarik, dari program apa tangkapan layar itu?
Tuan Tamu Misteri
12

2 contoh pertama bertindak sangat berbeda jika Anda MENGGANTInya dengan sesuatu. Jika Anda cocok dengan ini:

str = str.replace(/^(7|8|9)/ig,''); 

Anda akan mengganti 7 atau 8 atau 9 dengan string kosong.

Jika Anda cocok dengan ini

str = str.replace(/^[7|8|9]/ig,''); 

Anda akan mengganti 7atau 8atau 9ATAU THE VERTICAL BAR !!!! dengan string kosong.

Saya baru saja menemukan ini dengan cara yang sulit.

Sheila
sumber
6
Selamat datang di SO! Mengganti atau mencocokkan, itu salah. Banyak orang membuat kesalahan itu, dan mereka biasanya lolos begitu saja - selama bertahun-tahun, terkadang - karena string masukan mereka tidak pernah mengandung pipa ( |).
Alan Moore