Mengapa ekspresi reguler saya berfungsi di X tetapi tidak di Y?

77

Saya menulis ekspresi reguler yang berfungsi dengan baik dalam program tertentu (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit, ...). Tetapi ketika saya menggunakannya dalam program yang berbeda (atau pada varian unix yang berbeda), itu berhenti cocok. Mengapa?

Gilles
sumber

Jawaban:

103

Sayangnya, karena alasan historis, alat yang berbeda memiliki sintaks ekspresi reguler yang sedikit berbeda, dan kadang-kadang beberapa implementasi memiliki ekstensi yang tidak didukung oleh alat lain. Meskipun ada kesamaan, sepertinya setiap alat penulis membuat beberapa pilihan berbeda.

Konsekuensinya adalah bahwa jika Anda memiliki ekspresi reguler yang berfungsi di satu alat, Anda mungkin perlu memodifikasinya agar berfungsi di alat lain. Perbedaan utama antara alat umum adalah:

  • apakah operator +?|(){}memerlukan backslash;
  • ekstensi apa yang didukung di luar dasar-dasar .[]*^$dan biasanya+?|()

Dalam jawaban ini, saya mencantumkan standar utama . Periksa dokumentasi alat yang Anda gunakan untuk detailnya.

Perbandingan Wikipedia dari mesin ekspresi reguler memiliki tabel yang mencantumkan fitur yang didukung oleh implementasi umum.

Ekspresi reguler dasar (BRE)

Ekspresi reguler dasar dikodifikasi oleh standar POSIX . Ini adalah sintaks yang digunakan oleh grep, seddan vi. Sintaks ini menyediakan fitur-fitur berikut:

  • ^dan $cocokkan hanya pada awal dan akhir garis.
  • . cocok dengan karakter apa pun (atau karakter apa pun kecuali baris baru).
  • […]cocok dengan satu karakter apa pun yang tercantum di dalam tanda kurung (set karakter). Jika karakter pertama setelah braket pembuka adalah a ^, karakter yang tidak terdaftar cocok sebagai gantinya. Untuk memasukkan a ], letakkan segera setelah pembukaan [(atau setelah [^jika set negatif). Jika -berada di antara dua karakter, ini menunjukkan kisaran; untuk memasukkan literal -, letakkan di tempat yang tidak dapat diuraikan sebagai rentang.
  • Garis miring terbalik sebelum ^$.*\[mengutip karakter berikutnya.
  • * cocok dengan karakter sebelumnya atau subekspresi 0, 1 atau lebih kali.
  • \(…\)adalah grup sintaksis, untuk digunakan dengan *operator atau referensi dan \DIGITpenggantian.
  • Referensi ulang \1,, \2... cocok dengan teks persis yang cocok dengan grup yang sesuai, misalnya \(fo*\)\(ba*\)\1cocok foobaafootetapi tidak foobaafo. Tidak ada cara standar untuk merujuk pada kelompok ke-10 dan seterusnya (arti standar \10adalah kelompok pertama diikuti oleh a 0).

Fitur-fitur berikut juga standar, tetapi hilang dari beberapa implementasi terbatas:

  • \{m,n\}cocok dengan karakter sebelumnya atau subekspresi antara m ke n kali; n atau m dapat dihilangkan, dan artinya persis m .\{m\}
  • Di dalam tanda kurung, kelas karakter dapat digunakan, misalnya [[:alpha:]]cocok dengan huruf apa pun. Implementasi modern dari ekspresi braket ) juga mencakup elemen penyusun seperti [.ll.]dan kelas kesetaraan suka [=a=].

Berikut ini adalah ekstensi umum (terutama di alat GNU), tetapi tidak ditemukan di semua implementasi. Periksa manual alat yang Anda gunakan.

  • \|untuk pergantian: foo\|barcocok fooatau bar.
  • \?(kependekan \{0,1\}) dan \+(kependekan \{1,\}) cocok dengan karakter atau subekspresi sebelumnya paling banyak 1 kali, atau setidaknya 1 kali masing-masing.
  • \ncocok dengan baris baru, \tcocok dengan tab, dll.
  • \wcocok dengan konstituen kata apa pun (kependekan dari [_[:alnum:]]tetapi dengan variasi ketika datang ke pelokalan) dan \Wcocok dengan karakter apa pun yang bukan konstituen kata.
  • \<dan \>cocokkan string kosong hanya di awal atau akhir kata masing-masing; \bcocok, dan \Bcocok di mana \btidak.

Perhatikan bahwa alat tanpa \|operator tidak memiliki kekuatan penuh ekspresi reguler. Referensi balik memungkinkan beberapa hal tambahan yang tidak dapat dilakukan dengan ekspresi reguler dalam arti matematika.

Ekspresi reguler yang diperluas (ERE)

Ekspresi reguler yang diperluas dikodifikasikan oleh standar POSIX . Keuntungan utama mereka dibandingkan BRE adalah keteraturan: semua operator standar adalah karakter tanda baca yang telanjang, garis miring terbalik sebelum karakter tanda baca selalu mengutipnya. Ini adalah sintaks yang digunakan oleh awk, grep -Eatau egrep, GNU sed -r, dan operator bash=~ . Sintaks ini menyediakan fitur-fitur berikut:

  • ^dan $cocokkan hanya pada awal dan akhir garis.
  • . cocok dengan karakter apa pun (atau karakter apa pun kecuali baris baru).
  • […]cocok dengan satu karakter apa pun yang tercantum di dalam tanda kurung (set karakter). Komplemenasi dengan inisial ^dan rentang berfungsi seperti di BRE (lihat di atas). Kelas karakter dapat digunakan tetapi hilang dari beberapa implementasi. Implementasi modern juga mendukung kelas kesetaraan dan elemen penyusun. Tanda garis miring terbalik di dalam tanda kurung mengutip karakter berikutnya dalam beberapa tetapi tidak semua implementasi; gunakan \\berarti backslash untuk portabilitas.
  • (…)adalah grup sintaksis, untuk digunakan dengan *atau \DIGITpenggantian.
  • |untuk pergantian: foo|barcocok fooatau bar.
  • *, +dan ?cocok dengan karakter atau subekspresi sebelumnya beberapa kali: 0 atau lebih untuk *, 1 atau lebih untuk +, 0 atau 1 untuk ?.
  • Backslash mengutip karakter berikutnya jika bukan alfanumerik.
  • {m,n}cocok dengan karakter sebelumnya atau subekspresi antara m dan n kali (hilang dari beberapa implementasi); n atau m dapat dihilangkan, dan artinya persis m .{m}
  • Beberapa ekstensi umum seperti dalam BRE: backreferences (terutama tidak ada dalam awk kecuali dalam implementasi busybox di mana Anda dapat menggunakan ); karakter khusus , , dll .; batas kata dan , konstituen kata dan , ...\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (ekspresi reguler yang kompatibel dengan Perl)

PCRE adalah ekstensi dari ERE, awalnya diperkenalkan oleh Perl dan diadopsi oleh GNU grep -Pdan banyak alat modern dan bahasa pemrograman , biasanya melalui perpustakaan PCRE . Lihat dokumentasi Perl untuk pemformatan yang bagus dengan contoh. Tidak semua fitur versi terbaru Perl didukung oleh PCRE (mis. Eksekusi kode Perl hanya didukung di Perl). Lihat manual PCRE untuk ringkasan fitur yang didukung. Tambahan utama untuk ERE adalah:

  • (?:…)adalah grup yang tidak menangkap: suka (…), tetapi tidak masuk dalam referensi.
  • (?=FOO)BAR(lookahead) cocok BAR, tetapi hanya jika ada juga yang cocok untuk FOOmemulai pada posisi yang sama. Ini paling berguna untuk melabuhkan kecocokan tanpa mencantumkan teks berikut dalam kecocokan: foo(?=bar)kecocokan footetapi hanya jika diikuti bar.
  • (?!FOO)BAR(lookahead negatif) cocok BAR, tetapi tidak ada juga kecocokan untuk FOOpada posisi yang sama. Misalnya (?!foo)[a-z]+cocok dengan kata kecil yang tidak dimulai dengan foo; [a-z]+(?![0-9)cocok dengan huruf kecil apa pun yang tidak diikuti oleh digit (jadi foo123, itu cocok fotetapi tidak foo).
  • (?<=FOO)BAR(lihat di belakang) cocok BAR, tetapi hanya jika segera didahului oleh pertandingan untuk FOO. FOOharus memiliki panjang yang diketahui (Anda tidak dapat menggunakan operator pengulangan seperti *). Ini paling berguna untuk mengaitkan kecocokan tanpa menyertakan teks sebelumnya dalam kecocokan: (?<=^| )fookecocokan footetapi hanya jika itu didahului oleh spasi atau awal string.
  • (?<!FOO)BAR(terlihat negatif di belakang) cocok BAR, tetapi hanya jika tidak segera didahului oleh pertandingan FOO. FOOharus memiliki panjang yang diketahui (Anda tidak dapat menggunakan operator pengulangan seperti *). Ini paling berguna untuk mengaitkan kecocokan tanpa menyertakan teks sebelumnya dalam kecocokan: (?<![a-z])fookecocokan footetapi hanya jika tidak diawali dengan huruf kecil.

Emacs

Sintaksis Emacs adalah antara BRE dan ERE. Selain Emacs, ini adalah sintaks default untuk -regexdi GNU find. Emacs menawarkan operator berikut:

  • ^, $, ., […], *, +, ?Seperti di ERE
  • \(…\), \|, \{…\}, Seperti di BRE\DIGIT
  • lebih banyak urutan huruf backslash ; \<dan \>untuk batas kata; dan lebih banyak lagi dalam versi terbaru Emacs, yang sering tidak didukung di mesin lain dengan sintaks mirip Emacs.

Gumpalan shell

Shell gumpalan (wildcard) melakukan pencocokan pola dengan sintaks yang sama sekali berbeda dari ekspresi reguler dan kurang kuat. Selain kerang, wildcard ini tersedia dengan alat lain seperti find -namedan rsync filter. Pola POSIX meliputi fitur-fitur berikut:

  • ? cocok dengan karakter apa pun.
  • […]adalah karakter yang diatur dalam sintaks ekspresi reguler biasa. Beberapa shell tidak mendukung kelas karakter. Beberapa shell membutuhkan !bukannya ^meniadakan set.
  • *cocok dengan urutan karakter apa pun (seringkali kecuali /saat mencocokkan jalur file; jika /dikecualikan dari *, maka **kadang-kadang termasuk /, tetapi periksa dokumentasi alat ini).
  • Backslash mengutip karakter selanjutnya.

Ksh menawarkan fitur tambahan yang memberikan polanya yang cocok dengan kekuatan penuh ekspresi reguler. Fitur-fitur ini juga tersedia dalam bash setelah dijalankan shopt -s extglob. Zsh memiliki sintaks yang berbeda tetapi juga dapat mendukung sintaks ksh setelahnya setopt ksh_glob.

Gilles
sumber
Lainnya kaya Res Anda mungkin ingin menyebutkan adalah vim's dan AT & T libast (seperti dalam ksh93) yang.
Stéphane Chazelas
@ StéphaneChazelas Selain vim, program apa yang menggunakan vim regexps? Selain ksh, program apa yang menggunakan libast?
Gilles
semua AT & T set alat menggunakan AT & T Res ( grep, tw, expr...). Kecuali ksh, toolset itu jarang ditemukan di luar AT&T.
Stéphane Chazelas
Menurut pemahaman saya (dan Wikipedia), istilah Anda "Kelas karakter" sebenarnya mengacu pada "kelas karakter POSIX" ... namun, regex(7)setuju dengan Anda dan menyebut [these]"ekspresi kurung" dan (dalam "kurung kurung") [:these:]"kelas karakter." Saya tidak yakin bagaimana cara terbaik untuk mengatasinya.
Adam Katz
Apa pun yang Anda sebut mereka, mereka mendukung rentang. Pasti patut dicatat yang -menentukan rentang dan harus diloloskan, pertama (setelah opsional ^), atau terakhir jika harus diambil secara harfiah. (Saya telah melihat banyak bug yang berasal dari misalnya [A-z]–catat perubahan dalam kasus-, yang cocok dengan karakter kode 65 hingga 122 dan secara tidak sengaja memasukkan masing-masing:. [\]^_`Saya juga melihat yang valid namun membingungkan [!-~]untuk mencocokkan semua karakter yang dapat dicetak dalam ANSI , yang saya lebih suka lihat sebagai [\x21-\x7e], yang paling tidak langsung dalam aksinya meskipun membingungkan dalam dimensi yang berbeda.)
Adam Katz