Laden...

RegEx: Bestimmte Matches ausschließen

Erstellt von p!lle vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.470 Views
p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 5 Jahren
RegEx: Bestimmte Matches ausschließen

Moin,

ich versuche mich gerade ein bissel mit RegEx zu beschäftigen, dafür habe ich mir selbst ein paar Suchkriterien festgelegt.
Leider ergaben sich mir jetzt 2 Fragen dazu. =)

<Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<Button Grid.Row="0" Margin="Hallo" Property="Wert"
        Content="Start"
        Command="{Binding StartCommand}"
        Style="{StaticResource MeinTollerStyle}" />

Erstes Suchkriterium
Finde alle Vorkommnisse, die zwischen zwei aufeinanderfolgenden Gänsefüßchen stehen.
Hier ist darauf zu achten, dass er Hallo" Property="Wert dann natürlich nicht finden darf.

Gelöst habe ich es einfach mit: "([^"]*)"
Gibt es hier eine bessere Lösung oder ist das so in Ordnung?

Zweites Suchkriterium
Es soll das erste Suchkriterium angewandt werden, dabei sollen allerdings bestimmte Vorkommnisse nicht berücksichtigt werden.
In meinem Beispiel soll er "Auto" nicht matchen.
Hier komme ich gerade nicht weiter...

Ich habe es mit der Negierung versucht:
"(["]*(?!Auto))" liefert die gleichen Werte wie der Suchpattern aus dem 1. Beispiel.
"([
"]*(?<!Auto))" schließt zwar Auto aus, allerdings fängt dann die Suche beim " nach Auto an.

Leider komme ich gerade nicht darauf, wie ich bestimmte Vorkommnisse ausschließe - ist die Negierung hier überhaupt das Mittel der Wahl? Muss ich es ggf. an anderer Stelle einsetzen?

Über einen Denkanstoß oder ein Schlagwort würde ich mich freuen. =)

656 Beiträge seit 2008
vor 5 Jahren

Gibts einen Grund, warum du das XAML nicht einfach per XDocument liest, sondern versuchst es per Regex zu parsen? 😃

Zu Punkt 1: Der Ausschluss vom Zeichen ist ok, zumindest ist es die straight-forward Variante die ich dort (vermutlich) auch nutzen würde.
Als Alternative gäbe es noch den non-greedy modifier ? (bzw. auch das Non-Greedy flag vom Regex selber), was das Verhalten umdreht.
Standardmäßig ist ein Regex "greedy" (aka. es wird versucht, so viel wie möglich zu matchen). Mit dem Fragezeichen hinter dem Quantifier (zb. "(.*?)") dreht man dieses Verhalten um, damit so wenig wie nötig gematcht wird.

Zu Punkt 2: Das Stichwort hier ist "look-around", bzw. je nachdem ob du nach vorne/hinten auf Präsenz/Absenz prüfen willst ein "negative look-ahead" (bzw. in entsprechenden Kombinationen ein "positive look-ahead", "negative look-behind" bzw. "positive look-behind").
In deinem Fall willst du den look-ahead aber vermutlich zwischen dem Gänsefüßchen und dem [^"]* (weil du das Auto ja nicht am Anfang haben möchtest; dein jetziger Regex prüft es am Ende). Also so: "((?!Auto)[^"]*)"

p!lle Themenstarter:in
1.040 Beiträge seit 2007
vor 5 Jahren

Ja, gibt es. Es ist einfach nur ein Beispiel und Zufall, dass es eben XAML ist. Es geht rein ums Lernen und Verstehen von RegEx. 😉

Den Rest deiner Antwort schaue ich mir mal an und versuche damit mein "Problem" zu lösen. Danke! =)

49.485 Beiträge seit 2005
vor 5 Jahren

Hallo p!lle,

find ich gut, dass du dich mit Regex beschäftigst! Regex ist eine Technik, die ich - einmal gelernt - mein ganzes Leben lang immer und immer wieder gut gebrauchen konnte.

Grundsätzlich ist [^"]* schon ok und im Normalfall auch besser als .*?
Siehe dazu RegEx kürzester Match [und die Gefahren von .*?].

Allerdings ist es trotzdem nicht sicher. Zum einen kommst du aus dem Tritt, wenn in der Datei irgendwo auch nur ein falsches/unerwartetes Anführungszeichen steht. Zum anderen erkennt der Pattern keine escapten Anführungszeichen. Die gibt es zwar in XML wohl nicht, aber z.B. in CSV. Dafür würde es z.B. der folgende Pattern tun, der auch mit mehrfachen Backslashes klarkommt: ("(?:[^\"]|\.)*"

Um das aus dem Tritt kommen zu lösen, wäre es im konkreten Fall sinnvoll, Attribute als ganzes zu matchen, also z.B. ([a-z]+\s*=\s*"(?<value>[^"]*)"

Dann kommt du auch nicht mehr aus dem Tritt, wenn du mit negativen look-aheads bestimmte Attributwerte ausschließt. Wie BhaaL richtig gesagt hat, muss das (?!Auto) an den Anfang.

(Negative) Lookaheads(/-arounds) sind toll, aber man muss aufpassen, einen Pattern nicht zu überfrachten. Manchmal ist es einfacher, die unerwünschten Matches nachträglich auf C#-Ebene auszuschließen, z.B. mit if (m.Groups ["value"].Value != "Auto").

Das gilt insbesondere wenn du einen Attributwert ausschließen willst, der ein bestimmten String irgendwo enthält, z.B. "hallo". Die einfache Idee (?!.*hallo") funktioniert nicht (richtig), weil er den Match auch dann ausschließt, wenn das hallo hinter dem schließenden Anführungszeichen steht. Da hilft auch kein (?!.***?**hallo). Klar, das ließe sich wieder über (?![^"]*hallo) lösen. Aber das ist dann nicht mehr DRY, denn wenn man den eigentlichen Pattern z.B. auf escapte Anführungsstriche umstellt, muss man das im Lookahead auch machen und darf es nicht vergessen, weil beides sonst nicht zueinander passt. Es geht zwar auch DRY, aber das wird noch unleserlicher. Da ist ein if (!m.Groups ["value"].Value.Contains ("hallo") schon deutlich einfacher und übersichtlicher.

herbivore

PS: Siehe auch On-the-fly Regex-Tester: Regex-Lab