2009-03-31 8 views
5

Gibt es eine Möglichkeit anzugeben, dass zwei oder mehr Regex-Ausdrücke in beliebiger Reihenfolge vorkommen können? Zum Beispiel können XML-Attribute in beliebiger Reihenfolge geschrieben werden. Nehmen wir an, ich habe folgendes XML:Regex-Syntax variabler Reihenfolge

Wie würde ich eine Übereinstimmung schreiben, die die Klasse und den Titel überprüft und in beiden Fällen funktioniert? Ich suche hauptsächlich nach der Syntax, die es mir erlaubt, in beliebiger Reihenfolge zu checken, und nicht nur die Klasse und den Titel zu vergleichen, wie ich das tun kann. Gibt es einen anderen Weg, als nur beide Kombinationen einzubeziehen und sie mit einem '|' zu verbinden?

Bearbeiten: Meine Präferenz wäre es, es in einem einzigen Regex zu tun, wie ich es programmatisch und auch Unit-Test es erstellen.

+0

Ich mag @Josh Bushs Antwort unten viel, wie es ist, was für mich jetzt funktioniert, dass ich auf diese – Rick

+0

stieß Nein, Sie können es nicht tun. Das ist einer der Gründe, warum Sie keine regulären Ausdrücke verwenden, um HTML (oder XML) zu analysieren. Verwenden Sie ein richtiges HTML-Parsing-Modul. ** Sie können HTML nicht zuverlässig mit regulären Ausdrücken analysieren, und Sie werden mit Kummer und Frustration konfrontiert. Sobald sich der HTML-Code von Ihren Erwartungen ändert, wird Ihr Code nicht mehr funktionieren. Siehe http: // htmlparsing.com/php für Beispiele, wie man HTML mit PHP-Modulen, die bereits geschrieben, getestet und debuggt wurden, richtig analysiert. –

+0

Dies ist einer der vielen Gründe, warum Regexes nicht zum Parsen von XML oder HTML geeignet sind. –

Antwort

8

Nein, ich glaube, der beste Weg, es mit einem einzigen RE zu tun ist genau so, wie Sie es beschreiben. Leider wird es sehr unordentlich, wenn Ihr XML 5 verschiedene Attribute haben kann, die Ihnen eine große Anzahl von verschiedenen REs zu überprüfen geben.

Auf der anderen Seite würde ich dies mit einem RE überhaupt nicht machen, da sie nicht als Programmiersprachen gedacht sind. Was ist los mit dem altmodischen Ansatz der Verwendung einer XML-Verarbeitungsbibliothek?

Wenn Sie benötigt, um eine RE zu verwenden, wird diese Antwort wahrscheinlich nicht viel helfen, aber ich glaube an die richtigen Tools für den Job zu verwenden.

+2

Der meiste HTML ist ungültiges XML. Sie benötigen also eine HTML-Parsing-Bibliothek. Und je nachdem, warum Sie versuchen, diese Informationen herauszuziehen, ist es möglicherweise nicht erforderlich, eine Anwendung in einer Bibliothek zu schreiben. Vielleicht ist es nur eine einmalige Sache, wo Sie einige grobe Informationen erhalten möchten. – Kibbee

+0

Leider muss ich den Wert der Möglichkeit, ungültiges XML gegen eine lächerliche Anzahl von Permutationen zu analysieren, abwägen. Zu einem bestimmten Zeitpunkt wird die Regex nicht so trivial sein. Es ist nicht nur ein einmaliges Projekt, aber ich denke, dass ich am Ende eine Bibliothek verwenden muss. – VirtuosiMedia

+1

Ein paar Regexes sind vielleicht keine schlechte Idee, aber es ist am besten, nicht alles in einem zu machen. Verwenden Sie zuerst eine Regex, um Zeug in zu bekommen, dann verwenden Sie ein anderes, um Elemente und ähnliches zu extrahieren und sie entsprechend zu verarbeiten. Es ist viel lesbarer und einfacher zu schreiben. –

0

Der einfachste Weg wäre, eine Regex zu schreiben, die den <a .... > Teil aufnimmt und dann zwei weitere Regexes schreibt, um die Klasse und den Titel herauszuziehen. Obwohl Sie es wahrscheinlich mit einer einzigen Regex machen könnten, wäre es sehr kompliziert und wahrscheinlich viel fehleranfälliger.

Mit einem einzigen Regex Sie so etwas wie

<a[^>]*((class="([^"]*)")|(title="([^"]*)"))?((title="([^"]*)")|(class="([^"]*)"))?[^>]*> 

, die ohne Überprüfung nur eine erste Hand Vermutung ist, brauchen würden, um zu sehen, ob es noch gültig ist. Viel einfacher, das Problem einfach zu teilen und zu überwinden.

+0

Das Aufzählen aller Permutationen könnte für drei Attribute möglich sein, aber da die Anzahl der Permutationen exponentiell anwächst, wird diese Lösung sehr schnell ein riesiges Problem. –

0

Eine erste Ad-hoc-Lösung könnte das Folgende sein.

((class|title)="[^"]*?" *)+ 

Dies ist alles andere als perfekt, da jedes Attribut mehr als einmal auftreten kann. Ich könnte mir vorstellen, dass dies mit Behauptungen lösbar sein könnte. Aber wenn Sie nur die Attribute extrahieren möchten, könnte dies bereits ausreichend sein.

2

Sie könnten benannte Gruppen verwenden, um die Attribute aus dem Tag zu extrahieren. Führen Sie die Regex aus und führen Sie dann eine Schleife durch die Gruppen, die die von Ihnen benötigten Tests ausführen.

Etwas Ähnliches (nicht getestet, .net regex Syntax mit dem \ mit w für Wortzeichen und \ s für Leerzeichen):

<a ((?<key>\w+)\s?=\s?['"](?<value>\w+)['"])+ /> 
+0

Dies ist wahrscheinlich die sinnvollste Lösung, nur mit Regex (anstelle eines vorgefertigten css-Parsers) – Rick

-1

Wenn Sie eine Permutation eines Satzes von Elementen übereinstimmen soll, Sie könnte eine Kombination aus Rückverweisen und Null-Breite negative Forward-Matching verwenden.

Sagen Sie bitte eine dieser sechs Zeilen übereinstimmen soll:

123-abc-456-def-789-ghi-0AB 
123-abc-456-ghi-789-def-0AB 
123-def-456-abc-789-ghi-0AB 
123-def-456-ghi-789-abc-0AB 
123-ghi-456-abc-789-def-0AB 
123-ghi-456-def-789-abc-0AB 

Sie dies mit der folgenden regex tun können:

/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/ 

Die Referenzen zurück (\1, \2), können Sie beziehen sich auf Ihre vorherigen Übereinstimmungen, und die Null Breite vorwärts Matching ((?!...)) können Sie eine Positionsübereinstimmung negieren, sagen, stimmen nicht überein, wenn die enthalten an dieser Stelle übereinstimmt. Die Kombination der beiden stellt sicher, dass Ihr Match eine legale Permutation der gegebenen Elemente ist, wobei jede Möglichkeit nur einmal auftritt.

So zum Beispiel in Ruby:

input = <<LINES 
123-abc-456-abc-789-abc-0AB 
123-abc-456-abc-789-def-0AB 
123-abc-456-abc-789-ghi-0AB 
123-abc-456-def-789-abc-0AB 
123-abc-456-def-789-def-0AB 
123-abc-456-def-789-ghi-0AB 
123-abc-456-ghi-789-abc-0AB 
123-abc-456-ghi-789-def-0AB 
123-abc-456-ghi-789-ghi-0AB 
123-def-456-abc-789-abc-0AB 
123-def-456-abc-789-def-0AB 
123-def-456-abc-789-ghi-0AB 
123-def-456-def-789-abc-0AB 
123-def-456-def-789-def-0AB 
123-def-456-def-789-ghi-0AB 
123-def-456-ghi-789-abc-0AB 
123-def-456-ghi-789-def-0AB 
123-def-456-ghi-789-ghi-0AB 
123-ghi-456-abc-789-abc-0AB 
123-ghi-456-abc-789-def-0AB 
123-ghi-456-abc-789-ghi-0AB 
123-ghi-456-def-789-abc-0AB 
123-ghi-456-def-789-def-0AB 
123-ghi-456-def-789-ghi-0AB 
123-ghi-456-ghi-789-abc-0AB 
123-ghi-456-ghi-789-def-0AB 
123-ghi-456-ghi-789-ghi-0AB 
LINES 

# outputs only the permutations 
puts input.grep(/123-(abc|def|ghi)-456-(?!\1)(abc|def|ghi)-789-(?!\1|\2)(abc|def|ghi)-0AB/) 

Für eine Permutation von fünf Elementen, wäre es:

/1-(abc|def|ghi|jkl|mno)- 
2-(?!\1)(abc|def|ghi|jkl|mno)- 
3-(?!\1|\2)(abc|def|ghi|jkl|mno)- 
4-(?!\1|\2|\3)(abc|def|ghi|jkl|mno)- 
5-(?!\1|\2|\3|\4)(abc|def|ghi|jkl|mno)-6/x 

Für Ihr Beispiel würde die regex seiner

/<a href="home.php" (class="link"|title="Home") (?!\1)(class="link"|title="Home")>Home<\/a>/ 
3

Sie können für jedes der Attribute einen Lookahead erstellen und sie in eine Regex für das gesamte Tag einfügen. Zum Beispiel könnte die Regex für den Tag

<a\b[^<>]*> 

, wenn Sie diese auf XML verwenden werden Sie wahrscheinlich etwas aufwändigere benötigen. An sich wird diese Basisregex mit einem Tag mit null oder mehr Attributen übereinstimmen. Dann fügen Sie eine lookhead für jedes der Attribute, die Sie anpassen möchten:

(?=[^<>]*\s+class="link") 
(?=[^<>]*\s+title="Home") 

Die [^<>]* es für das Attribut -scannen können, aber es wird nicht über den Schließwinkel betrachten lassen. Die Übereinstimmung der führenden Whitespaces hier im Lookahead dient zwei Zwecken: Sie ist flexibler als die Übereinstimmung in der Basisregex und stellt sicher, dass wir einen vollständigen Attributnamen abgleichen. Kombinieren sie erhalten wir:

<a\b(?=[^<>]*\s+class="link")(?=[^<>]*\s+title="Home")[^<>]+>[^<>]+</a> 

Natürlich habe ich aus Gründen der Klarheit einige vereinfachende Annahmen gemacht. Ich habe keine Leerzeichen um die Gleichheitszeichen, einfache Anführungszeichen oder keine Anführungszeichen um die Attributwerte oder spitze Klammern in den Attributwerten berücksichtigt (was ich gehört habe, ist legal, aber ich habe es noch nie gesehen). Das Schließen dieser Lecks (falls erforderlich) wird die Regex hässlicher machen, erfordert aber keine Änderungen an der Grundstruktur.

5

Haben Sie xpath in Betracht gezogen? (Wo Attribut Reihenfolge spielt keine Rolle)

//a[@class and @title] 

beide <a> Knoten als gültige Übereinstimmungen auswählt. Der einzige Nachteil ist, dass die Eingabe xhtml (wohlgeformte XML) sein muss.

+0

Ich benutze jetzt ein bisschen xpath, guten Vorschlag. – VirtuosiMedia