Sorulan soruya başka hiç kimse doğrudan cevap vermediğinden, ben yapacağım.
Cevap POSIX ile grep
bu talebi tam anlamıyla karşılamak imkansızdır:
grep "<Regex for 'doesn't contain hede'>" input
Bunun nedeni, POSIX'in grep
yalnızca bu görevi yerine getirecek kadar güçlü olmayan Temel Düzenli İfadeler ile çalışması gerektiğidir (alternatif ve parantez eksikliği nedeniyle normal dilleri ayrıştıramazlar).
Ancak, GNU grep
buna izin veren uzantıları uygular. Özellikle, \|
GNU'nun BRE'leri uygulamasında alternatif operatör ve \(
ve \)
parantezler. Normal ifade motorunuz alternatifi, negatif parantez ifadelerini, parantezleri ve Kleene yıldızını destekliyorsa ve dizenin başına ve sonuna demirleyebiliyorsa, bu yaklaşım için ihtiyacınız olan tek şey budur. Bununla birlikte, negatif setlerin [^ ... ]
bunlara ek olarak çok uygun olduğuna dikkat edin , çünkü aksi takdirde, bunları (a|b|c| ... )
sette olmayan, son derece sıkıcı ve aşırı uzun olan her karakteri listeleyen formun bir ifadesiyle değiştirmeniz gerekir, tüm karakter kümesi Unicode'dur.
GNU ile grep
cevap şöyle olacaktır:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
( Grail ve elle yapılan bazı diğer optimizasyonlarla birlikte bulunur ).
Ayrıca uygular bir aracı kullanabilirsiniz Normal İfadeleri Extended gibi egrep
ters eğik kurtulmak için,:
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
İşte sınamak için bir komut dosyası ( testinput.txt
geçerli dizinde bir dosya oluşturduğunu unutmayın ):
#!/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)
Sistemimde:
Files /dev/fd/63 and /dev/fd/62 are identical
beklenildiği gibi.
Detaylarla ilgilenenler için kullanılan teknik, kelimeyle eşleşen normal ifadeyi sonlu bir otomasyona dönüştürmek, daha sonra her kabul durumunu kabul etmeme ve tersine çevirerek otomatı ters çevirmek ve daha sonra elde edilen FA'yı geri dönüştürmektir. düzenli bir ifade.
Son olarak, herkesin de belirttiği gibi, normal ifade motorunuz negatif ileriye bakmayı destekliyorsa, bu işi çok basitleştirir. Örneğin, GNU grep ile:
grep -P '^((?!hede).)*$' input
Güncelleme: Geçenlerde Kendall Hopkins'in Grail'e benzer bir işlev sağlayan PHP'de yazılmış mükemmel FormalTheory kütüphanesini buldum . Bunu ve kendim tarafından yazılmış bir sadeleştiriciyi kullanarak, bir giriş ifadesi (yalnızca şu anda alfasayısal ve boşluk karakterleri destekleniyor) verilen negatif düzenli ifadelerin çevrimiçi oluşturucusunu yazabildim: http://www.formauri.es/personal/ pgimeno / misc / olmayan maç-regex /
İçin hede
o çıkarır:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
bu yukarıdakine eşdeğerdir.
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
? Fikir basit. İstenmeyen dizenin başlangıcını görene kadar eşleştirmeye devam edin, ardından yalnızca dizenin bitmemiş olduğu N-1 örneklerinde eşleşmeye devam edin (burada N dizenin uzunluğudur). Bu N-1 vakaları "h ardından e olmayan", "o d olmayan" ve "hed ardından e olmayan" şeklindedir. Bu N-1 vaka geçmek başardı, başarıyla vermedi Aradığınız başlayabilirsiniz böylece istenmeyen dizeyle eşleşen[^h]*
tekrar