Ekspresi reguler untuk mencocokkan garis yang tidak mengandung kata

4294

Saya tahu adalah mungkin untuk mencocokkan sebuah kata dan kemudian membalikkan kecocokan menggunakan alat lain (misalnya grep -v). Namun, apakah mungkin untuk mencocokkan baris yang 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
knaser
sumber
85
Mungkin terlambat beberapa tahun, tapi ada apa dengan ([^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
stevendesu
323
@stevendesu: coba ini untuk 'kata yang sangat-sangat-sangat-panjang' atau lebih baik setengah kalimat. Selamat mengetik. BTW, hampir tidak terbaca. Tidak tahu tentang dampak kinerja.
Peter Schuetze
13
@PeterSchuetze: Tentu itu tidak cantik untuk kata-kata yang sangat panjang, tetapi itu adalah solusi yang layak dan benar. Meskipun saya belum menjalankan tes pada kinerja, saya tidak akan membayangkan itu terlalu lambat karena sebagian besar aturan yang terakhir diabaikan sampai Anda melihat h (atau huruf pertama dari kata, kalimat, dll.). Dan Anda dapat dengan mudah menghasilkan string regex untuk string panjang menggunakan penggabungan iteratif. Jika berfungsi dan dapat dihasilkan dengan cepat, apakah keterbacaan penting? Itulah gunanya komentar.
stevendesu
57
@stevendesu: saya bahkan lebih baru, tetapi jawaban itu hampir sepenuhnya salah. untuk satu hal, itu mengharuskan subjek mengandung "h" yang seharusnya tidak perlu, mengingat tugasnya adalah "mencocokkan garis yang tidak mengandung kata tertentu". mari kita asumsikan bahwa Anda bermaksud menjadikan kelompok dalam opsional, dan bahwa polanya berlabuh: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ ini gagal ketika contoh "hede" didahului dengan contoh parsial "hede" seperti dalam "hhede".
jaytea
8
Pertanyaan ini telah ditambahkan ke FAQ Ekspresi Reguler Overflow Overflow , di bawah "Advanced Regex-Fu".
aliteralmind

Jawaban:

5895

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

^((?!hede).)*$

Regex di atas akan cocok dengan string apa pun, atau garis tanpa jeda baris, tidak mengandung (sub) string 'hede'. Seperti disebutkan, ini bukan sesuatu regex adalah "baik" di (atau harus dilakukan), tapi masih, itu adalah mungkin.

Dan jika Anda juga harus mencocokkan baris break karakter, gunakan pengubah DOT-ALL (trailing sdalam pola berikut):

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

atau gunakan sebaris:

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

(di mana /.../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 nkarakter. Sebelum, dan setelah setiap karakter, ada string kosong. Jadi daftar nkarakter akan memiliki n+1string kosong. Pertimbangkan string "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 e's adalah string kosong. Regex (?!hede).melihat ke depan untuk melihat apakah tidak ada substring "hede"untuk dilihat, dan jika itu yang terjadi (jadi sesuatu yang lain terlihat), maka .(titik) akan cocok dengan karakter apa pun kecuali jeda baris. Look-arounds juga disebut pernyataan nol-lebar- karena mereka tidak mengkonsumsi karakter apa pun. Mereka hanya menegaskan / memvalidasi sesuatu.

Jadi, dalam contoh saya, setiap string kosong terlebih dahulu divalidasi untuk melihat apakah tidak ada "hede"di depan, sebelum karakter dikonsumsi oleh .(titik). Regex (?!hede).akan melakukannya hanya sekali, sehingga dibungkus dalam kelompok, dan mengulangi nol atau lebih kali: ((?!hede).)*. Akhirnya, input awal dan akhir berlabuh untuk memastikan seluruh input dikonsumsi:^((?!hede).)*$

Seperti yang Anda lihat, masukan "ABhedeCD"akan gagal karena pada e3, regex (?!hede)gagal (ada yang "hede" di depan!).

Bart Kiers
sumber
26
Saya tidak akan mengatakan bahwa ini adalah sesuatu yang buruk pada regex. Kenyamanan solusi ini cukup jelas dan kinerja yang baik dibandingkan dengan pencarian terprogram sering tidak penting.
Archimaredes
29
Bicara negatif yang terus-menerus membuat Anda ekspresi reguler tidak-teratur.
Peter K
55
@ PeterK, tentu, tapi ini SO, bukan MathOverflow atau CS-Stackexchange. Orang yang mengajukan pertanyaan di sini umumnya mencari jawaban praktis. Sebagian besar pustaka atau alat (seperti grep, yang OP sebutkan) dengan dukungan regex semuanya memiliki fitur yang membuatnya tidak teratur dalam arti teoritis.
Bart Kiers
19
@ Bart Kiers, jangan tersinggung jawaban Anda, hanya penyalahgunaan terminologi ini sedikit mengganggu saya. Bagian yang benar-benar membingungkan di sini adalah bahwa ekspresi reguler dalam arti yang ketat dapat sangat banyak melakukan apa yang diinginkan OP, tetapi bahasa umum untuk menulisnya tidak memungkinkan, yang mengarah pada penyelesaian (secara matematis jelek) seperti lampu sorot. Silakan lihat jawaban ini di bawah ini dan komentar saya di sana untuk cara yang tepat secara teoritis melakukannya. Tidak perlu dikatakan itu bekerja lebih cepat pada input besar.
Peter K
17
Jika Anda pernah bertanya-tanya bagaimana melakukan ini dalam vim:^\(\(hede\)\@!.\)*$
botak
739

Perhatikan bahwa solusi untuk tidak memulai dengan "hede" :

^(?!hede).*$

umumnya jauh lebih efisien daripada solusi untuk tidak mengandung "hede" :

^((?!hede).)*$

Mantan memeriksa "hede" hanya di posisi pertama string input, daripada di setiap posisi.

FireCoding
sumber
5
Terima kasih, saya menggunakannya untuk memvalidasi bahwa string tidak mengandung squence digit ^ ((?! \ D {5,}).) *
Samih A
2
Halo! Saya tidak bisa menulis tidak berakhir dengan regex "hede" . Bisakah Anda membantu?
Aleks Ya
1
@AleksYa: cukup gunakan versi "berisi", dan sertakan jangkar akhir ke dalam string pencarian: ubah string menjadi "tidak cocok" dari "hede" menjadi "hede $"
Nyerguds
2
@AleksYa: tidak berakhir versi dapat dilakukan dengan menggunakan lookbehind negatif: (.*)(?<!hede)$. Versi @Nyerguds akan bekerja dengan baik tetapi benar-benar melewatkan poin kinerja yang disebutkan jawabannya.
thisismydesign
5
Mengapa begitu banyak jawaban yang dikatakan ^((?!hede).)*$? Apakah tidak lebih efisien untuk digunakan ^(?!.*hede).*$? Itu melakukan hal yang sama tetapi dalam langkah yang lebih sedikit
JackPRead
208

Jika Anda hanya menggunakannya untuk grep, Anda bisa menggunakan grep -v hedeuntuk mendapatkan semua baris yang tidak mengandung hede.

ETA Oh, membaca ulang pertanyaan, grep -vmungkin apa yang Anda maksud dengan "opsi alat".

Athena
sumber
22
Kiat: untuk secara progresif menyaring apa yang tidak Anda inginkan: grep -v "hede" | grep -v "hihi" | ... dll.
Olivier Lalonde
51
Atau hanya menggunakan satu prosesgrep -v -e hede -e hihi -e ...
Olaf Dietsche
15
Atau hanya grep -v "hede\|hihi":)
Putnik
2
Jika Anda memiliki banyak pola yang ingin Anda saring, masukkan ke dalam file dan gunakangrep -vf pattern_file file
codeforester
4
Atau sekadar egrepatau grep -Ev "hede|hihi|etc"untuk menghindari canggung melarikan diri.
Amit Naidu
161

Menjawab:

^((?!hede).)*$

Penjelasan:

^awal string, (grup dan tangkap ke \ 1 (0 kali atau lebih (cocok dengan jumlah sebanyak mungkin)),
(?!lihat ke depan untuk melihat apakah tidak ada,

hede string kamu,

)akhir pandangan ke depan, .karakter apa pun kecuali \ n,
)*akhir \ 1 (Catatan: karena Anda menggunakan kuantifier pada tangkapan ini, hanya pengulangan TERAKHIR dari pola yang ditangkap akan disimpan di \ 1)
$sebelum opsional \ n, dan akhir string

Jessica
sumber
14
luar biasa yang bekerja untuk saya dalam teks luhur 2 menggunakan banyak kata ' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'
Damodar Bashyal
3
@DamodarBashyal Saya tahu saya cukup terlambat di sini, tetapi Anda benar-benar dapat menghapus jabatan kedua di sana dan Anda akan mendapatkan hasil yang sama persis
forresthopkinsa
99

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

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

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

Ini hanya cocok dengan LENGKAP. Melakukannya untuk sub-pertandingan bahkan akan lebih canggung.

Hades32
sumber
1
Penting untuk dicatat ini hanya menggunakan ekspresi reguler dasar POSIX.2 dan karenanya sementara lebih portabel untuk saat PCRE tidak tersedia.
Steve-o
5
Saya setuju. Banyak jika tidak kebanyakan ekspresi reguler bukan bahasa biasa dan tidak dapat dikenali oleh automata terbatas.
ThomasMcLeod
@ThomasMcLeod, Hades32: Apakah itu dalam ranah bahasa reguler yang memungkinkan untuk dapat mengatakan ' tidak ' dan ' dan ' serta ' atau ' dari ekspresi seperti ' (hede|Hihi)'? (Ini mungkin pertanyaan untuk CS.)
James Haigh
7
@ JohnAllen: AKU !!! ... Yah, bukan regex yang sebenarnya tetapi referensi akademis, yang juga berkaitan erat dengan kompleksitas komputasi; PCRE pada dasarnya tidak dapat menjamin efisiensi yang sama dengan ekspresi reguler POSIX.
James Haigh
4
Maaf - jawaban ini tidak berfungsi, itu akan cocok dengan hehe dan bahkan cocok dengan hehe sebagian (babak kedua)
Falco
60

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

^(?!hede$).*

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

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

myStr !== 'foo'

Anda bahkan bisa meletakkan negasi di luar tes jika Anda memerlukan fitur regex (di sini, tidak sensitif huruf dan pencocokan kisaran):

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

Solusi regex di bagian atas jawaban ini mungkin membantu, dalam situasi di mana tes regex positif diperlukan (mungkin oleh API).

Roy Tinker
sumber
bagaimana dengan jejak spasi putih? Misalnya, jika saya ingin pengujian gagal dengan string " hede "?
eagor
@ eagor the \sdirective cocok dengan karakter spasi putih tunggal
Roy Tinker
terima kasih, tapi saya tidak berhasil memperbarui regex untuk membuat ini berfungsi.
eagor
2
@eagor:^(?!\s*hede\s*$).*
Roy Tinker
52

FWIW, karena bahasa reguler (alias bahasa rasional) ditutup dengan komplemen, selalu memungkinkan untuk menemukan ekspresi reguler (alias ekspresi rasional) yang meniadakan ekspresi lain. Tetapi tidak banyak alat yang mengimplementasikan ini.

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

Pertama-tama Anda menentukan jenis ekspresi Anda: label adalah huruf ( lal_char) untuk memilih dari ake zmisalnya (mendefinisikan alfabet ketika bekerja dengan komplementasi, tentu saja, sangat penting), dan "nilai" yang dihitung untuk setiap kata hanyalah Boolean : truekata 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 Anda memasukkan 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

Otomat yang sesuai

akhirnya, ubah otomat ini kembali ke ekspresi sederhana.

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

di mana +biasanya dilambangkan |, \emenunjukkan 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 di sini , dan coba Vcsn online di sana .

akim
sumber
6
Benar, tapi jelek, dan hanya bisa dilakukan untuk set karakter kecil. Anda tidak ingin melakukan ini dengan string Unicode :-)
reinierpost
Ada lebih banyak alat yang memungkinkannya, salah satu yang paling mengesankan adalah Ragel . Di sana itu akan ditulis sebagai (any * - ('hehe' any *)) untuk pertandingan yang diselaraskan atau (any * - ('hehe' any *)) untuk yang tidak selaras.
Peter K
1
@reinierpost: mengapa jelek dan apa masalahnya dengan unicode? Saya tidak bisa menyetujui keduanya. (Saya tidak punya pengalaman dengan vcsn, tetapi miliki dengan DFA).
Peter K
3
@PedroGimeno Ketika Anda berlabuh, Anda memastikan untuk menempatkan regex ini di parens pertama? Kalau tidak, prioritas di antara jangkar dan |tidak akan bermain dengan baik. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'.
akim
1
Saya pikir itu layak untuk mengatakan bahwa metode ini adalah untuk mencocokkan baris yang bukan kata 'hede', daripada baris daripada tidak mengandung kata 'hede' yang merupakan apa yang diminta OP. Lihat jawaban saya untuk yang terakhir.
Pedro Gimeno
51

Berikut adalah penjelasan yang baik tentang mengapa tidak mudah untuk meniadakan regex sewenang-wenang Tetapi saya harus setuju dengan jawaban yang lain: jika ini bukan pertanyaan hipotetis, maka regex bukanlah pilihan yang tepat di sini.

Josh Lee
sumber
10
Beberapa alat, dan khususnya mysqldumpslow, hanya menawarkan cara ini untuk menyaring data, jadi dalam kasus seperti itu, menemukan regex untuk melakukan ini adalah solusi terbaik selain dari menulis ulang alat (berbagai tambalan untuk ini belum dimasukkan oleh MySQL AB / Sun / Oracle
FGM
1
Analagous persis dengan situasi saya. Mesin templat kecepatan menggunakan ekspresi reguler untuk memutuskan kapan menerapkan transformasi (escape html) dan saya ingin agar selalu berfungsi KECUALI dalam satu situasi.
Henno Vermeulen
1
Apa alternatif yang ada? Ive tidak pernah menemukan sesuatu yang bisa melakukan pencocokan string yang tepat selain regex. Jika OP menggunakan bahasa pemrograman, mungkin ada alat lain yang tersedia, tetapi jika dia menggunakan tidak menulis kode, mungkin tidak ada pilihan lain.
kingfrito_5005
2
Salah satu dari banyak skenario non-hipotetis di mana sebuah regex adalah pilihan terbaik yang tersedia: Saya berada di IDE (Android Studio) yang menunjukkan output log, dan satu-satunya alat penyaringan yang disediakan adalah: string sederhana, dan regex. Mencoba melakukan ini dengan string sederhana akan gagal total.
LarsH
48

Dengan lookahead negatif, ekspresi reguler dapat mencocokkan sesuatu yang tidak mengandung pola tertentu. Ini dijawab dan dijelaskan oleh Bart Kiers. Penjelasan hebat!

Namun, dengan jawaban Bart Kiers, bagian lookahead akan menguji 1 hingga 4 karakter di depan sambil mencocokkan setiap karakter tunggal. Kita dapat menghindari ini dan membiarkan bagian lookahead memeriksa seluruh teks, memastikan tidak ada 'hede', dan kemudian bagian normal (. *) Dapat memakan seluruh teks sekaligus.

Berikut adalah regex yang ditingkatkan:

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

Perhatikan quantifier lazy (*?) Di bagian lookahead negatif adalah opsional, Anda dapat menggunakan (*) quantifier greedy sebagai gantinya, tergantung pada data Anda: jika 'hede' ada dan di bagian awal teks, lazy quantifier dapat lebih cepat; jika tidak, quantifier rakus menjadi lebih cepat. Namun jika 'hede' tidak ada, keduanya akan sama lambat.

Ini kode demo .

Untuk informasi lebih lanjut tentang lookahead, silakan lihat artikel hebat: Menguasai Lookahead dan Lookbehind .

Juga, silakan periksa RegexGen.js , Generator Ekspresi Reguler JavaScript yang membantu membangun ekspresi reguler yang kompleks. Dengan RegexGen.js, Anda bisa membuat regex dengan cara yang lebih mudah dibaca:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);
amobiz
sumber
3
jadi untuk memeriksa apakah string yang diberikan tidak mengandung str1 dan str2:^(?!.*(str1|str2)).*$
S.Serpooshan
1
Ya, atau Anda dapat menggunakan quanty quantifier:, ^(?!.*?(?:str1|str2)).*$tergantung pada data Anda. Menambahkan ?:karena kita tidak perlu menangkapnya.
amobiz
Sejauh ini, ini adalah jawaban terbaik dengan faktor 10xms. Jika Anda menambahkan kode jsfiddle Anda dan hasilnya ke jawaban orang mungkin memperhatikannya. Saya bertanya-tanya mengapa versi malas lebih cepat daripada versi serakah ketika tidak ada hede. Bukankah seharusnya mereka mengambil jumlah waktu yang sama?
user5389726598465
Ya, mereka mengambil jumlah waktu yang sama karena mereka berdua menguji seluruh teks.
amobiz
41

Tolak ukur

Saya memutuskan untuk mengevaluasi beberapa Opsi yang disajikan dan membandingkan kinerjanya, serta menggunakan beberapa Fitur baru. Benchmarking pada .NET Regex Engine: http://regexhero.net/tester/

Teks Benchmark:

7 baris pertama tidak boleh cocok, karena mengandung Ekspresi yang dicari, sedangkan 7 baris bawah harus cocok!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

Hasil:

Hasilnya adalah Iterasi per detik sebagai median 3 run - Bigger Number = Better

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

Sejak .NET tidak mendukung action Verbs (* FAIL, dll.) Saya tidak bisa menguji solusi P1 dan P2.

Ringkasan:

Saya mencoba menguji sebagian besar solusi yang diajukan, beberapa Optimalisasi dimungkinkan untuk kata-kata tertentu. Sebagai Contoh jika dua huruf pertama dari string pencarian bukan Sama, jawaban 03 dapat diperluas untuk ^(?>[^R]+|R+(?!egex Hero))*$menghasilkan peningkatan kinerja yang kecil.

Tetapi solusi tercepat yang paling mudah dibaca dan berdasarkan kinerja tampaknya 05 menggunakan pernyataan bersyarat atau 04 dengan kuantifier positif. Saya pikir solusi Perl harus lebih cepat dan lebih mudah dibaca.

Falco
sumber
5
Kamu harus waktu ^(?!.*hede)juga. /// Selain itu, mungkin lebih baik untuk memberi peringkat ekspresi untuk korpus yang cocok dan korpus yang tidak cocok secara terpisah karena biasanya merupakan kasus yang sebagian besar baris cocok atau sebagian besar baris tidak.
ikegami
32

Bukan regex, tapi saya merasa logis dan berguna menggunakan serial greps dengan pipa untuk menghilangkan noise.

misalnya. cari file konfigurasi apache tanpa semua komentar-

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

dan

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

Logika serial grep adalah (bukan komentar) dan (cocok dengan dir)

kiwalk
sumber
2
Saya pikir dia meminta versi regex darigrep -v
Angel.King.47
9
Ini berbahaya. Juga ketinggalan garis-garis sepertigood_stuff #comment_stuff
Xavi Montero
29

dengan ini, Anda menghindari untuk menguji lookahead di setiap posisi:

/^(?:[^h]+|h++(?!ede))*+$/

setara dengan (untuk .net):

^(?>(?:[^h]+|h+(?!ede))*)$

Jawaban lama:

/^(?>[^h]+|h+(?!ede))*$/
Casimir et Hippolyte
sumber
7
Poin bagus; Saya terkejut tidak ada yang menyebutkan pendekatan ini sebelumnya. Namun, regex khusus itu cenderung mengalami backtracking katastropik ketika diterapkan pada teks yang tidak cocok. Inilah cara saya akan melakukannya:/^[^h]*(?:h+(?!ede)[^h]*)*$/
Alan Moore
... atau Anda bisa membuat semua bilangan kuantitatif menjadi posesif. ;)
Alan Moore
@ Alan Moore - Saya terkejut juga. Saya melihat komentar Anda (dan regex terbaik di tumpukan) di sini hanya setelah memposting pola yang sama dalam jawaban di bawah ini.
ridgerunner
@ Derununner, tidak harus menjadi yang terbaik. Saya telah melihat tolok ukur di mana jawaban teratas berkinerja lebih baik. (Saya terkejut tentang hal itu.)
Qtax
23

Aforemention (?:(?!hede).)*sangat bagus karena dapat ditambatkan.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

Tetapi yang berikut ini sudah cukup dalam hal ini:

^(?!.*hede)                    # A line without hede

Penyederhanaan ini siap ditambahkan klausa "DAN":

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same
ikegami
sumber
20

Begini cara saya melakukannya:

^[^h]*(h(?!ede)[^h]*)*$

Akurat dan lebih efisien daripada jawaban lainnya. Ini menerapkan teknik efisiensi "membuka gulungan-the-loop" Friedl dan membutuhkan jauh lebih sedikit mundur.

penunggang kuda
sumber
17

Jika Anda ingin mencocokkan karakter untuk meniadakan kata yang mirip dengan meniadakan kelas karakter:

Misalnya, string:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

Jangan gunakan:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

Menggunakan:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

Perhatikan "(?!bbb)."bukan melihat di belakang atau melihat ke depan, itu terlihat saat ini, misalnya:

"(?=abc)abcde", "(?!abc)abcde"
diyism
sumber
3
Tidak ada "lookcurrent" di perl regexp's. Ini benar-benar tampilan negatif (awalan (?!). Awalan lookahead positif adalah (?=sementara awalan lookhead yang sesuai akan (?<!dan (?<=masing - masing. Sebuah lookahead berarti Anda membaca karakter berikutnya (karenanya "maju") tanpa mengkonsumsinya. Melihat ke belakang berarti Anda memeriksa karakter yang sudah digunakan.
Didier L
14

An, menurut pendapat saya, varian jawaban teratas yang lebih mudah dibaca:

^(?!.*hede)

Pada dasarnya, "cocokkan di awal baris jika dan hanya jika tidak ada 'hede' di dalamnya" - sehingga persyaratan diterjemahkan hampir secara langsung ke dalam regex.

Tentu saja, ada beberapa persyaratan kegagalan:

^(?!.*(hede|hodo|hada))

Detail: Jangkar ^ memastikan mesin regex tidak mencoba ulang kecocokan di setiap lokasi dalam string, yang akan cocok dengan setiap string.

Jangkar pada awalnya dimaksudkan untuk mewakili awal dari garis. Alat grep mencocokkan setiap baris satu per satu, dalam konteks di mana Anda bekerja dengan string multiline, Anda dapat menggunakan bendera "m":

/^(?!.*hede)/m # JavaScript syntax

atau

(?m)^(?!.*hede) # Inline flag
Dannie P
sumber
Contoh luar biasa dengan banyak negasi.
Peter Parada
Satu perbedaan dari jawaban teratas adalah bahwa ini tidak cocok dengan apa pun, dan itu cocok dengan seluruh baris jika tanpa "hede"
Z. Khullah
13

OP tidak menentukan atau Tagposting yang mengindikasikan konteks (bahasa pemrograman, editor, alat) yang akan digunakan Regex.

Bagi saya, kadang-kadang saya perlu melakukan ini saat mengedit file menggunakan Textpad.

Textpad mendukung beberapa Regex, tetapi tidak mendukung lookahead atau lookhhind, sehingga dibutuhkan beberapa langkah.

Jika saya ingin mempertahankan semua baris yang TIDAK mengandung string hede, saya akan melakukannya seperti ini:

1. Cari / ganti seluruh file untuk menambahkan "Tag" unik ke awal setiap baris yang mengandung teks apa pun.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Hapus semua baris yang berisi string hede(string pengganti kosong):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3. Pada titik ini, semua baris yang tersisa TIDAK mengandung string hede. Hapus "Tag" unik dari semua baris (string pengganti kosong):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

Sekarang Anda memiliki teks asli dengan semua baris yang berisi string hededihapus.


Jika saya mencari untuk melakukan sesuatu yang lain untuk hanya baris yang TIDAK mengandung string hede, saya akan melakukannya seperti ini:

1. Cari / ganti seluruh file untuk menambahkan "Tag" unik ke awal setiap baris yang mengandung teks apa pun.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Untuk semua baris yang berisi string hede, hapus "Tag" unik:

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3. Pada titik ini, semua baris yang dimulai dengan "Tag" unik, JANGAN berisi string hede. Sekarang saya dapat melakukan Sesuatu Saya yang lain untuk hanya garis-garis itu.

4. Setelah saya selesai, saya menghapus "Tag" unik dari semua baris (string pengganti kosong):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  
Kevin Fegan
sumber
12

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 grephanya 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 grepmengimplementasikan 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.txtdi 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 hedeitu output:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

yang setara dengan di atas.

Pedro Gimeno
sumber
11

Sejak diperkenalkannya ruby-2.4.1, kita dapat menggunakan Operator Absen yang baru dalam Ekspresi Reguler Ruby

dari dokumen resmi

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

Dengan demikian, dalam kasus Anda ^(?~hede)$melakukan pekerjaan untuk Anda

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]
aoror
sumber
9

Melalui kata kerja PCRE (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

Ini akan sepenuhnya melompati garis yang berisi string yang tepat hededan cocok dengan semua baris yang tersisa.

DEMO

Eksekusi bagian:

Mari kita perhatikan regex di atas dengan membaginya menjadi dua bagian.

  1. Bagian sebelum |simbol. Bagian tidak harus dicocokkan .

    ^hede$(*SKIP)(*F)
  2. Bagian setelah |simbol. Bagian harus dicocokkan .

    ^.*$

BAGIAN 1

Mesin Regex akan memulai eksekusi dari bagian pertama.

^hede$(*SKIP)(*F)

Penjelasan:

  • ^ Menegaskan bahwa kami berada di awal.
  • hede Cocok dengan string hede
  • $ Menyatakan bahwa kita berada di garis akhir.

Jadi garis yang berisi string hedeakan cocok. Setelah mesin regex melihat kata kerja berikut (*SKIP)(*F)( Catatan: Anda bisa menulis (*F)sebagai(*FAIL) ), itu melompat dan membuat pertandingan gagal. |disebut alteration atau logis ATAU operator ditambahkan di sebelah kata kerja PCRE yang inturn cocok dengan semua batas yang ada antara masing-masing dan setiap karakter pada semua baris kecuali baris berisi string yang tepat hede. Lihat demo di sini . Artinya, ia mencoba untuk mencocokkan karakter dari string yang tersisa. Sekarang regex di bagian kedua akan dieksekusi.

BAGIAN 2

^.*$

Penjelasan:

  • ^ Menegaskan bahwa kami berada di awal. yaitu, cocok dengan semua baris yang dimulai kecuali yang ada di hedebaris. Lihat demo di sini .
  • .*Dalam mode Multiline, .akan cocok dengan karakter apa pun kecuali baris baru atau karakter carriage return. Dan *akan mengulangi karakter sebelumnya nol atau lebih banyak kali. Jadi .*akan cocok dengan seluruh lini. Lihat demo di sini .

    Hei mengapa Anda menambahkan. * Bukan. +?

    Karena .*akan cocok dengan garis kosong tetapi .+tidak akan cocok dengan garis kosong. Kami ingin mencocokkan semua baris kecuali hede, mungkin ada kemungkinan baris kosong juga di input. sehingga Anda harus menggunakan .*bukan .+. .+akan mengulangi karakter sebelumnya satu kali atau lebih. Lihat .*cocok dengan garis kosong di sini .

  • $ Akhir jangkar baris tidak diperlukan di sini.

Avinash Raj
sumber
7

Mungkin lebih mudah dikelola untuk dua regex dalam kode Anda, satu untuk melakukan pencocokan pertama, dan kemudian jika itu cocok jalankan regex kedua untuk memeriksa kasus-kasus outlier yang ingin Anda blokir misalnya ^.*(hede).*maka miliki logika yang sesuai dalam kode Anda.

OK, saya akui ini bukan jawaban untuk pertanyaan yang diposting dan mungkin juga menggunakan sedikit lebih banyak pemrosesan daripada satu regex. Tetapi untuk pengembang yang datang ke sini mencari perbaikan darurat yang cepat untuk kasus outlier maka solusi ini tidak boleh diabaikan.

andrew pate
sumber
6

Opsi lain adalah menambahkan pandangan positif ke depan dan memeriksa apakah heheada di baris input, maka kita akan meniadakannya, dengan ekspresi yang mirip dengan:

^(?!(?=.*\bhede\b)).*$

dengan batas kata.


Ekspresi dijelaskan pada panel kanan atas regex101.com , jika Anda ingin menjelajahi / menyederhanakan / memodifikasinya, dan di tautan ini , Anda dapat menonton bagaimana itu akan cocok dengan beberapa input sampel, jika Anda mau.


Sirkuit RegEx

jex.im memvisualisasikan ekspresi reguler:

masukkan deskripsi gambar di sini

Emma
sumber
5

The TXR Bahasa mendukung negasi regex.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

Contoh yang lebih rumit: cocokkan semua baris yang dimulai dengan adan diakhiri dengan z, tetapi tidak mengandung substring hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

Negasi Regex tidak terlalu berguna dengan sendirinya tetapi ketika Anda juga memiliki persimpangan, hal-hal menjadi menarik, karena Anda memiliki set lengkap operasi set boolean: Anda dapat mengekspresikan "set yang cocok dengan ini, kecuali untuk hal-hal yang cocok dengan itu".

Kaz
sumber
Perhatikan bahwa ini juga merupakan solusi untuk regex berbasis ElasticSearch Lucene.
Wiktor Stribiżew
4

Fungsi di bawah ini akan membantu Anda mendapatkan hasil yang diinginkan

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>
Daniel Nyamasyo
sumber
2

^ ((?! hede).) * $ adalah solusi yang elegan, kecuali karena menghabiskan karakter Anda tidak akan dapat menggabungkannya dengan kriteria lain. Misalnya, Anda ingin memeriksa tidak adanya "hede" dan "haha". Solusi ini akan berfungsi karena tidak akan menggunakan karakter:

^ (?!. \ bhede \ b) (? =. \ bhaha \ b)

cloudhopperpilot
sumber
1

Cara menggunakan kata kerja kontrol mundur PCRE untuk mencocokkan baris yang tidak mengandung kata

Inilah metode yang belum pernah saya lihat digunakan sebelumnya:

/.*hede(*COMMIT)^|/

Bagaimana itu bekerja

Pertama, ia mencoba untuk menemukan "hede" di suatu tempat di barisan. Jika berhasil, pada titik ini, (*COMMIT)memberitahu mesin untuk, tidak hanya tidak mundur jika terjadi kegagalan, tetapi juga tidak mencoba pencocokan lebih lanjut dalam kasus itu. Kemudian, kami mencoba mencocokkan sesuatu yang tidak mungkin cocok (dalam hal ini, ^).

Jika sebuah baris tidak mengandung "hede" maka alternatif kedua, sebuah subpattern kosong, berhasil cocok dengan string subjek.

Metode ini tidak lebih efisien daripada lookahead negatif, tapi saya pikir saya hanya akan membuangnya di sini kalau-kalau ada orang yang menemukan itu bagus dan menemukan menggunakannya untuk aplikasi lain yang lebih menarik.

jaytea
sumber
0

Solusi yang lebih sederhana adalah dengan menggunakan bukan operator !

Pernyataan if Anda harus cocok dengan "mengandung" dan tidak cocok dengan "dikecualikan".

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

Saya percaya para desainer RegEx mengantisipasi penggunaan bukan operator.

user1691651-John
sumber
0

Mungkin Anda akan menemukan ini di Google ketika mencoba menulis regex yang dapat mencocokkan segmen garis (sebagai lawan dari seluruh baris) yang tidak mengandung substring. Butuh beberapa saat untuk mencari tahu, jadi saya akan membagikan:

Diberikan string: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

Saya ingin mencocokkan <span>tag yang tidak mengandung "buruk" substring.

/<span(?:(?!bad).)*?>akan cocok <span class=\"good\">dan <span class=\"ugly\">.

Perhatikan bahwa ada dua set (lapisan) tanda kurung:

  • Yang paling dalam adalah untuk lookahead negatif (ini bukan grup tangkap)
  • Yang paling luar ditafsirkan oleh Ruby sebagai kelompok tangkap tetapi kami tidak ingin itu menjadi kelompok tangkap, jadi saya menambahkan?: Di awal dan tidak lagi ditafsirkan sebagai kelompok tangkap.

Demo di Ruby:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]
BrunoFacca
sumber
0

Dengan ConyEdit , Anda dapat menggunakan baris perintah cc.gl !/hede/untuk mendapatkan baris yang tidak mengandung pencocokan regex, atau menggunakan baris perintah cc.dl /hede/untuk menghapus baris yang berisi pencocokan regex. Mereka memiliki hasil yang sama.

Donald
sumber
0

Saya ingin menambahkan contoh lain untuk jika Anda mencoba untuk mencocokkan seluruh baris yang berisi string X , tetapi tidak juga mengandung string Y .

Sebagai contoh, katakanlah kita ingin memeriksa apakah URL / string kita mengandung " suguhan lezat ", asalkan tidak juga mengandung " cokelat " di mana saja.

Pola regex ini akan berfungsi (berfungsi dalam JavaScript juga)

^(?=.*?tasty-treats)((?!chocolate).)*$

(global, bendera multiline misalnya)

Contoh Interaktif: https://regexr.com/53gv4

Cocok

(URL ini mengandung "suguhan lezat" dan juga tidak mengandung "cokelat")

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

Tidak cocok

(URL ini mengandung "cokelat" di suatu tempat - sehingga tidak akan cocok meskipun mengandung "suguhan lezat")

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts
Matthew Rideout
sumber