Da hat sonst niemand eine direkte Antwort auf die gestellte Frage gegeben , werde ich es tun.
Die Antwort ist, dass es mit POSIX grep
unmöglich ist, diese Anfrage buchstäblich zu erfüllen:
grep "<Regex for 'doesn't contain hede'>" input
Der Grund dafür ist, dass POSIX grep
nur für die Arbeit mit regulären Basisausdrücken erforderlich ist , die dieser Aufgabe einfach nicht leistungsfähig genug sind (sie können reguläre Sprachen nicht analysieren, da es an Abwechslung und Klammern mangelt).
GNU grep
implementiert jedoch Erweiterungen, die dies ermöglichen. Insbesondere \|
ist der Wechseloperator in der Implementierung von BREs durch GNU und \(
und \)
sind die Klammern. Wenn Ihre Engine für reguläre Ausdrücke Alternation, negative Klammerausdrücke, Klammern und den Kleene-Stern unterstützt und in der Lage ist, am Anfang und Ende der Zeichenfolge zu verankern, ist dies alles, was Sie für diesen Ansatz benötigen. Beachten Sie jedoch, dass negative Mengen [^ ... ]
zusätzlich zu diesen sehr praktisch sind, da Sie sie ansonsten durch einen Ausdruck der Form ersetzen müssen (a|b|c| ... )
, in der alle Zeichen aufgelistet sind, die nicht in der Menge enthalten sind. Dies ist äußerst mühsam und zu lang, umso mehr, wenn Der gesamte Zeichensatz ist Unicode.
Mit GNU grep
wäre die Antwort ungefähr so:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
(gefunden mit Grail und einigen weiteren Optimierungen von Hand).
Sie können auch ein Tool verwenden, das erweiterte reguläre Ausdrücke implementiert , z. B. egrep
um die Backslashes zu entfernen:
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
Hier ist ein Skript zum Testen (beachten Sie, dass es eine Datei testinput.txt
im aktuellen Verzeichnis generiert ):
#!/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)
In meinem System wird gedruckt:
Files /dev/fd/63 and /dev/fd/62 are identical
wie erwartet.
Für diejenigen, die an den Details interessiert sind, besteht die angewandte Technik darin, den regulären Ausdruck, der dem Wort entspricht, in einen endlichen Automaten umzuwandeln, dann den Automaten umzukehren, indem jeder Akzeptanzzustand in Nichtakzeptanz geändert wird und umgekehrt, und dann die resultierende FA zurück in umzuwandeln ein regulärer Ausdruck.
Wie alle bemerkt haben, vereinfacht dies die Aufgabe erheblich, wenn Ihre Engine für reguläre Ausdrücke einen negativen Lookahead unterstützt. Zum Beispiel mit GNU grep:
grep -P '^((?!hede).)*$' input
Update: Ich habe kürzlich Kendall Hopkins 'exzellente FormalTheory- Bibliothek gefunden, die in PHP geschrieben wurde und eine ähnliche Funktionalität wie Grail bietet. Mit diesem und einem von mir selbst geschriebenen Vereinfacher konnte ich einen Online-Generator für negative reguläre Ausdrücke mit einer Eingabephrase schreiben (derzeit werden nur alphanumerische Zeichen und Leerzeichen unterstützt): http://www.formauri.es/personal/ pgimeno / misc / non-match-regex /
Dafür hede
gibt es aus:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
das ist äquivalent zu den oben genannten.
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
? Die Idee ist einfach. Passen Sie weiter an, bis Sie den Anfang der unerwünschten Zeichenfolge sehen, und stimmen Sie dann nur in den N-1-Fällen überein, in denen die Zeichenfolge nicht fertig ist (wobei N die Länge der Zeichenfolge ist). Diese N-1-Fälle sind "h gefolgt von Nicht-e", "Er gefolgt von Nicht-d" und "Hed gefolgt von Nicht-e". Wenn Sie es geschafft haben, diese N-1-Fälle zu bestehen, haben Sie die unerwünschte Zeichenfolge nicht erfolgreich gefunden, sodass Sie[^h]*
erneut nach ihr suchen können