2012-03-28 1 views

Antwort

2

Natürlich ist dies möglich mit regex einige Look-Ahead-Behauptungen mit

$i =~ /^(?=.*(?:abc|bcd))(?!.*diy)/ 

(?=.*(?:abc|bcd)) wird wahr sein, wenn eines der Teile ist da drin Sie

wollen

(?!.*diy) fehl, wenn die Zeichenfolge, die Sie don‘ t wollen ist drin

Aber ich denke, Ihre Lösung ist klarer.

+0

wird in diesem Fall ein Regexp schneller als zwei Regexps? – madper

+1

@madper Sorry, aber ich denke, jede Antwort auf die Leistung wäre in diesem Fall eine wilde Vermutung. Wenn Sie dieses "wenn" sehr (sehr, sehr) oft machen, dann machen Sie einfach einen Benchmark. Wenn Sie dies von Zeit zu Zeit tun, verwenden Sie, was Sie besser mögen. – stema

1
$ echo 'now I know my abcs' | txr -c '@/.*(abc|bcd).*&~.*diy.*/' - 

$ echo 'no match' | txr -c '@/.*(abc|bcd).*&~.*diy.*/' - 
false 

$ echo 'Kaz wanted better regex, so he did the diy thing, starting from abc' | txr -c '@/.*(abc|bcd).*&~.*diy.*/' - 
false 

false wird von einem gescheiterten Beendigungsstatus begleitet und zeigt keine Übereinstimmung. Keine Ausgabe bedeutet Übereinstimmung.

Wir verwenden .*, weil die Übereinstimmung verankert ist. @/RE/ selbst in einer Zeile bedeutet, dass die Zeile mit der Regex übereinstimmen muss; wenn wir diese Linie von Anfang bis Ende der Regex-Maschine zuführen, ist die Maschine ein Akzeptanzzustand.

Wenn Sie solche Regexes haben, ist die Suche-Semantik in der Regex schlecht. I.e. wenn foo eigentlich .*(foo).* bedeutet (passen foo irgendwo im Text), funktioniert das nicht sehr gut für foo&~bar, weil .*(foo&~bar).* nicht dasselbe bedeutet wie . Ersteres bedeutet "Übereinstimmung mit einer beliebigen Zeichenfolge, die eine Teilzeichenfolge enthält, die foo&~bar entspricht." Aber das ist eine unmögliche Übereinstimmung; Kein String kann foo und bar gleichzeitig sein. Es ist eine leere Kreuzung. Letzteres bedeutet jedoch, dass "eine Zeichenkette, die foo enthält, irgendwo darin enthalten ist, aber bar nirgends enthält." Jetzt sind Sie im Geschäft.

+0

Ich denke, dass Sie 'Grep' bekommen können, was diese Art von Sache von AT & T Forschung tut. – Kaz

+0

Was ist 'txr' und woher kommt es? Es scheint nicht als Standard-Tool (Ports oder Packages) auf einem meiner FreeBSD oder Linux-Maschinen verfügbar zu sein. – ghoti

+0

Es ist neu (<3 Jahre). Der Tag, an dem es so verfügbar ist, ist nicht angekommen. Für den Moment bauen Leute aus Quellen: http://www.nongnu.org/txr. – Kaz

2

Das Negieren einer Folge von Zeichen ist normalerweise ein Schmerz in Regex. Sie müssten Look-Ahead-Assertions verwenden.

Wenn Sie für zusammenhängende Sequenzen testen, könnten Sie versuchen, es mit Look-Ahead-und Look-Behinds zu tun, etwa (?<!diy)(abc|bcd)(?!diy), aber das wird wahrscheinlich mehrdeutig sein.

würde ich den Test wie der Ihren verlassen, nur das Standard-Variable zu verlieren:

if /(abc|bcd)/ and not /diy/ 

klar und sauber. :)

+0

Vielen Dank ~ – madper

+1

Sie müssten Lookahead-Operatoren verwenden, wenn Sie nicht die Unterstützung in der Regex haben; aber Negieren gehört definitiv in den Bereich eines endlichen Automaten. Wenn Sie Tabellen erstellen würden, gäbe es viele Zustände, aber es ist möglich ohne; zum Beispiel durch Algebra. Anstatt den Pfeilen von Blase zu Blase zu folgen, können Sie Ableitungen von Regex-Formel zu Regex-Formel durchführen. – Kaz

3

Das in einer anderen Antwort vorgeschlagene Lookahead/Behind funktioniert bei diesem Problem nicht. Um Lookbehind zu verwenden, benötigen Sie ein Lookback mit variabler Länge, das von Perl nicht unterstützt wird.

/^(?!.*?diy).*?(?:abc|bcd)/s 

Ein anderer Ansatz:

/^(?:(?!diy).)*(?:abc|bcd(?!iy))(?:(?!diy).)*\z/s 

In jedem Fall die bc aus dem Factoring | kann eine effizientere Regex erzeugen.

Verwandte Themen