Pertanyaan Ekspresi reguler untuk mencocokkan garis yang tidak berisi kata?


Saya tahu itu mungkin untuk mencocokkan kata dan kemudian membalikkan kecocokan menggunakan alat lain (mis. grep -v). Namun, saya ingin tahu apakah mungkin untuk mencocokkan garis itu tidak mengandung kata tertentu (misalnya hede) menggunakan ekspresi reguler.

Memasukkan:

hoho
hihi
haha
hede

Kode:

grep "<Regex for 'doesn't contain hede'>" input

Output yang diinginkan:

hoho
hihi
haha

3559


asal


Jawaban:


Gagasan bahwa regex tidak mendukung pencocokan terbalik tidak sepenuhnya benar. Anda dapat meniru perilaku ini dengan menggunakan tampilan negatif:

^((?!hede).)*$

Rujukan di atas akan cocok dengan string apa pun, atau baris tanpa jeda baris, tidak mengandung (sub) string 'hede'. Seperti disebutkan, ini bukan sesuatu regex yang "bagus" di (atau harus dilakukan), tapi tetap saja, itu aku s mungkin.

Dan jika Anda perlu mencocokkan karakter garis putus juga, gunakan Pengubah DOT-ALL (Trailing s dalam pola berikut):

/^((?!hede).)*$/s

atau gunakan inline:

/(?s)^((?!hede).)*$/

(Dimana /.../ adalah pembatas regex, yaitu bukan bagian dari pola)

Jika pengubah DOT-ALL tidak tersedia, Anda dapat meniru perilaku yang sama dengan kelas karakter [\s\S]:

/^((?!hede)[\s\S])*$/

Penjelasan

String hanyalah daftar n karakter. Sebelum, dan setelah setiap karakter, ada string kosong. Jadi daftar n karakter akan memiliki n+1 string kosong. Pertimbangkan stringnya "ABhedeCD":

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

Dimana eadalah string kosong. Regex (?!hede). melihat ke depan untuk melihat apakah tidak ada substring "hede" harus dilihat, dan jika itu adalah kasus (jadi sesuatu yang lain terlihat), maka . (dot) akan cocok dengan karakter apa pun kecuali jeda baris. Look-around juga disebut penegasan nol-lebar karena mereka tidak mengkonsumsi karakter apa saja. Mereka hanya menegaskan / memvalidasi sesuatu.

Jadi, dalam contoh saya, setiap string kosong pertama kali divalidasi untuk melihat apakah tidak ada "hede" di depan, sebelum karakter dikonsumsi oleh . (dot). Regex (?!hede). hanya akan melakukannya sekali, jadi dibungkus dalam grup, dan diulangi nol atau beberapa kali: ((?!hede).)*. Akhirnya, awal dan akhir-dari-input berlabuh untuk memastikan seluruh input dikonsumsi: ^((?!hede).)*$

Seperti yang Anda lihat, masukan "ABhedeCD" akan gagal karena aktif e3, regex (?!hede) gagal (di sana aku s  "hede" di depan!).


4852



Perhatikan bahwa solusi untuk tidak dimulai dari "Hede":

^(?!hede).*$

umumnya jauh lebih efisien daripada solusi tidak berisi "Hede":

^((?!hede).)*$

Yang pertama memeriksa "hede" hanya pada posisi pertama string masukan, bukan di setiap posisi.


603



Jika Anda hanya menggunakannya untuk grep, yang dapat Anda gunakan grep -v hede untuk mendapatkan semua garis yang tidak mengandung hede.

ETA Oh, baca kembali pertanyaannya, grep -v mungkin apa yang Anda maksud dengan "opsi alat".


163



Menjawab:

^((?!hede).)*$

Penjelasan:

^awal string, ( kelompok dan ambil ke \ 1 (0 atau lebih kali (cocok dengan jumlah yang paling mungkin)),
(?! melihat ke depan untuk melihat jika tidak ada,

hedestring kamu,

) akhir melihat-depan, . karakter apa pun kecuali \ n,
)* akhir \ 1 (Catatan: karena Anda menggunakan pengukur pada penangkapan ini, hanya pengulangan TERAKHIR dari pola yang dipotret akan disimpan dalam \ 1)
$ sebelum \ n opsional, dan akhir dari string


121



Jawaban yang diberikan baik-baik saja, hanya sebuah poin akademis:

Ekspresi Reguler dalam arti ilmu komputer teoritis TIDAK MAMPU lakukan seperti ini. Bagi mereka itu harus terlihat seperti ini:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Ini hanya pertandingan FULL. Melakukannya untuk sub-pertandingan bahkan akan lebih canggung.


89



Jika Anda ingin menguji regex hanya gagal jika seluruh string cocok, berikut ini akan berfungsi:

^(?!hede$).*

misalnya - Jika Anda ingin mengizinkan semua nilai kecuali "foo" (yaitu "foofoo", "barfoo", dan "foobar" akan berlalu, tetapi "foo" akan gagal), gunakan: ^(?!foo$).*

Tentu saja, jika Anda memeriksa tepat kesetaraan, solusi umum yang lebih baik dalam hal ini adalah untuk memeriksa kesetaraan string, yaitu

myStr !== 'foo'

Anda bahkan bisa menempatkan negasi di luar tes jika Anda memerlukan fitur regex apa pun (di sini, ketidaksensitifan huruf dan kisaran pencocokan):

!/^[a-f]oo$/i.test(myStr)

Solusi regex di atas dapat membantu, namun, dalam situasi di mana tes regex positif diperlukan (mungkin oleh API).


48



Ini penjelasan yang bagus mengapa tidak mudah meniadakan regex yang sewenang-wenang. Saya harus setuju dengan jawaban yang lain, meskipun: jika ini adalah sesuatu selain pertanyaan hipotetis, maka regex bukanlah pilihan yang tepat di sini.


47



FWIW, karena bahasa reguler (bahasa rasional) ditutup karena saling melengkapi, selalu ada kemungkinan untuk menemukan ekspresi reguler (ekspresi rasional) yang meniadakan ekspresi lain. Tetapi tidak banyak alat yang mengimplementasikan ini.

Vcsn mendukung operator ini (yang ditunjukkannya {c}, postfix).

Anda terlebih dahulu menentukan jenis ekspresi Anda: label adalah huruf (lal_char) untuk memilih dari a untuk z misalnya (mendefinisikan alfabet ketika bekerja dengan komplementasi, tentu saja, sangat penting), dan "nilai" dihitung untuk setiap kata hanyalah sebuah Boolean: true kata itu diterima, false, ditolak.

Dengan Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹

lalu masukkan ekspresi Anda:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

ubah ekspresi ini menjadi otomat:

In [7]: a = e.automaton(); a

The corresponding automaton

akhirnya, ubah automaton ini kembali ke ekspresi sederhana.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

dimana + biasanya dilambangkan |, \e menunjukkan kata kosong, dan [^] biasanya ditulis . (karakter apa saja). Jadi, dengan sedikit penulisan ulang ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Anda dapat melihat contoh ini sini, dan coba Vcsn online sana.


43