Karena tidak ada orang lain yang memberikan jawaban langsung untuk pertanyaan yang diajukan , saya akan melakukannya.
Jawabannya adalah bahwa dengan POSIX grep
, mustahil untuk benar-benar memenuhi permintaan ini:
grep "<Regex for 'doesn't contain hede'>" input
Alasannya adalah bahwa POSIX grep
hanya diperlukan untuk bekerja dengan Ekspresi Reguler Dasar , yang sama sekali tidak cukup kuat untuk menyelesaikan tugas itu (mereka tidak mampu menguraikan bahasa biasa, karena kurangnya pergantian dan tanda kurung).
Namun, GNU grep
mengimplementasikan ekstensi yang memungkinkannya. Secara khusus, \|
adalah operator pergantian dalam implementasi BRE GNU, dan \(
dan \)
adalah tanda kurung. Jika mesin ekspresi reguler Anda mendukung pergantian, ekspresi braket negatif, tanda kurung, dan bintang Kleene, dan mampu berlabuh ke awal dan akhir string, hanya itu yang Anda butuhkan untuk pendekatan ini. Namun perlu dicatat bahwa set negatif [^ ... ]
sangat nyaman selain itu, karena jika tidak, Anda perlu menggantinya dengan ekspresi dari formulir (a|b|c| ... )
yang mencantumkan setiap karakter yang tidak ada di set, yang sangat membosankan dan terlalu lama, terlebih lagi jika set karakter keseluruhan adalah Unicode.
Dengan GNU grep
, jawabannya akan seperti:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
(ditemukan dengan Grail dan beberapa optimasi lebih lanjut dibuat dengan tangan).
Anda juga dapat menggunakan alat yang mengimplementasikan Ekspresi Reguler Diperpanjang , seperti egrep
, untuk menghilangkan garis miring terbalik:
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
Berikut ini skrip untuk mengujinya (perhatikan ini menghasilkan file testinput.txt
di direktori saat ini):
#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"
# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede
h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)
Dalam sistem saya itu mencetak:
Files /dev/fd/63 and /dev/fd/62 are identical
seperti yang diharapkan.
Bagi mereka yang tertarik dengan perincian, teknik yang digunakan adalah mengubah ekspresi reguler yang cocok dengan kata menjadi otomat terbatas, kemudian membalikkan automaton dengan mengubah setiap keadaan penerimaan menjadi non-penerimaan dan sebaliknya, dan kemudian mengubah FA yang dihasilkan kembali ke ekspresi reguler.
Akhirnya, seperti yang dicatat oleh semua orang, jika mesin ekspresi reguler Anda mendukung tampilan negatif, itu menyederhanakan banyak tugas. Misalnya, dengan GNU grep:
grep -P '^((?!hede).)*$' input
Pembaruan: Saya baru-baru ini menemukan pustaka FormalTheory Kendall Hopkins yang sangat baik , ditulis dalam PHP, yang menyediakan fungsionalitas yang mirip dengan Grail. Dengan menggunakannya, dan penyederhanaan yang ditulis sendiri, saya dapat menulis generator online ekspresi reguler negatif yang diberi frasa input (hanya karakter alfanumerik dan spasi yang saat ini didukung): http://www.formauri.es/personal/ pgimeno / misc / non-match-regex /
Untuk hede
itu output:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
yang setara dengan di atas.
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
:? Idenya sederhana. Terus mencocokkan sampai Anda melihat awal string yang tidak diinginkan, maka hanya cocok dalam kasus N-1 di mana string belum selesai (di mana N adalah panjang string). Kasus N-1 ini adalah "h diikuti oleh non-e", "ia diikuti oleh non-d", dan "hed diikuti oleh non-e". Jika Anda berhasil meneruskan kasus N-1 ini, Anda berhasil tidak cocok dengan string yang tidak diinginkan sehingga Anda dapat mulai mencari[^h]*
lagi