Laden...

Regex matcht zu viel trotz Verwendung des Layz-Operators

Erstellt von Hirnhamster vor 12 Jahren Letzter Beitrag vor 12 Jahren 1.725 Views
H
Hirnhamster Themenstarter:in
103 Beiträge seit 2010
vor 12 Jahren
Regex matcht zu viel trotz Verwendung des Layz-Operators

Hey,
ich entwickle grad eine kleine Applikation für diverse Analysen von HTML Quellcode. Dabei würde ich unter anderem gern feststellen, wie oft ein bestimmtes Wort in Fettschrift, Kursivschrift oder unterstrichen vorkommt.

Dazu verwende ich den folgenden Regex:


String keyword = "MatchMe";

String pattern = "<(b|strong|i|em|u)[^>]*?>.*?" + Regex.Escape(keyword) + ".*?</\\1\\s*>";

String text = "<strong>VielText</strong><em>MatchMe</em>VielText<em>MatchMe</em><strong>VielText</strong>";

Regex r = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
MatchCollection mc = r.Matches(text);

Das Problem: Es wird der komplette String gematcht, statt der beiden einzelnen "<em>MatchMe</em>". Gibt es irgendeine Möglichkeit, wie ich stattdessen sowas wie "shortest possible Match" sagen könnte, damit ich eben die beiden "<em>MatchMe</em>" matche?

Grüße
Hamster

PHP Tutorials zum PHP lernen

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo,

mit einem positiven Lookahead geht das. Für dein Beispiel (als Beispiel 😉:


<(b|strong|i|em|u)(?=>MatchMe)

Oder wenn in den Tags noch mehr zugelassen werden soll:


<(b|strong|i|em|u)(?=\s*[^>]*>MatchMe)

Aber ev. bist du mit XML-Methoden (wie Linq to XML) besser dran.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

5.742 Beiträge seit 2007
vor 12 Jahren

Hallo Hirnhamster,

Regex halte ich da für den völlig falschen Weg - verwende hier lieber das HTML Agility Pack.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo Hirnhamster,

ich behaupte - im Gegensatz zu gfoidl -, dass du einen negativen Lookahead verwenden muss, um zu verhindern, dass der Match nicht unerwünscht über Tag-Grenzen hinausgeht, siehe RegEx kürzester Match [und die Gefahren von .*?].

herbivore

6.911 Beiträge seit 2009
vor 12 Jahren

Hallo herbivore,

Edit: im Folgepost spezifiziert Hirnhamster die Aufgabe so dass die Frage mit dem negativen Lookahead geklärt ist.

es ist zwar oben ein (kleiner) Fehler drin - denn es würde auch "MatchMeToo" gematcht - und somit gehört das Pattern geändert zu


<(?<tag>b|strong|i|em|u)\s*[^>]*>MatchMe</\<tag>>

und das erfüllt meiner Ansicht nach die Aufgabe

wie oft ein bestimmtes Wort in Fettschrift, Kursivschrift oder unterstrichen vorkommt.

Die Anzahl der Matches wird korrekt zurückgegeben.

Wie ich hier einen negative Lookahead anwenden sollte weiß ich nicht, auch aus dem von dir geposteten Link erschließt sich mir das hier nicht. Die Gefahr von .*? habe ich erkannt und ja auch vermieden 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

H
Hirnhamster Themenstarter:in
103 Beiträge seit 2010
vor 12 Jahren

Hey, danke für die Antworten

<Siehe edit unten..>

@winSharp
Das Pack kommt mir bekannt vor. Brauche den Regex aber auch in nem anderen Zusammenhang, von daher wäre es ganz gut, wenn ich eine allgemeine Lösung finden würde , die nicht auf HTML beschränkt ist. 😃

@gfoidl
Ich glaub ich muss meine Anforderung nochmal genauer erklären:
Ich möchte nicht nur exakt <tag>MatchMe</tag> matchen, sondern auch solche Sachen wie <tag>Ein Satz über MatchMe, mit weiteren Wörtern</tag>. Es soll also reichen, wenn MatchMe irgendwo innerhalb der Tags auftritt. Ansonsten wäre deine zuletzt genannte Lösung richtig.

@herbivore
Könntest du mir ein Beispiel geben? Ich hab grad zum ersten Mal was von Lookarounds gelesen, aber mangels aktiver Anwendung fehlt mir noch das Verständnis dafür. Ich hatte es mit folgendem Regex versucht, den ich in Anlehnung an Matching Innermost HTML Elements erstellt hab:

<(b|strong|i|em|u)\b[^>]*?>(?:(?>[^<]+)|<(?!/\1>))*?MatchMe.*?</\1\s*>

Der Zielgedanke war wie folgt:
Matche <b|strong|i|em|u> (inklusive eventueller Attribute) mittels

<(b|strong|i|em|u)\b[^>]*?>

Matche "irgendetwas, aber NICHT den schließenden Tag" mittels

(?:(?>[^<]+)|<(?!/\1>))*?

Die Zeile soll konkret Folgendes machen: WENN [<]+ gematcht werden kann, dann matche auch [<]+, ansonten prüfe, ob dort NICHT der schließende Tag auftritt. Allerdings bin ich mir dabei überhaupt nicht sicher, ob das so richtig ist... Ich will halt wie gesagt "irgendetwas, aber NICHT den schließenden Tag" realisieren.

Naja und zum Schluss dann eben noch "MatchMe" und den schließenden Tag.

//edit

Folgender Regex tut das, was ich mir vorgestellt habe:

<(b|strong|i|em|u)\b[^>]*?>(?:(?>[^<])|<(?!/\1>))*?MatchMe.*?</\1\s*>

Unterschied zu dem oberen ist das "+" Zeichen, das macht da wohl keinen Sinn...
<(b|strong|i|em|u)\b[>]*?>(?:(?>[<]**:::

PHP Tutorials zum PHP lernen

5.742 Beiträge seit 2007
vor 12 Jahren

wenn ich eine allgemeine Lösung finden würde , die nicht auf HTML beschränkt ist.

Naja - "HTML parsen" ist nun einmal etwas spzielles.

Warum man es nicht mit Regex tun sollte, steht sehr eindrucksvoll auf Stackoverflow.
Was machst du z.B., wenn in der Seite ein "<b>" nicht ordnungsgemäß geschlossen wird?
Und Formatierung via CSS ist da noch überhaupt nicht berücksichtigt.

C
1.214 Beiträge seit 2006
vor 12 Jahren

HTML kann man grundsätzlich nicht mit Regex "parsen", weil Regex nur Chomsky 3 Grammatiken erkennnen kann.

49.485 Beiträge seit 2005
vor 12 Jahren

Hallo winSharp93, hallo Coder007,

beide Einwände sind korrekt, insbesondere wenn man den HTML-Code vollständig parsen will. Das ändert aber nichts daran, dass man mit Regex normalerweise schnell und einfach die gewünschte Information aus einem Text ziehen, auch wenn es sich um HTML-Code handelt.

Hallo Hirnhamster,

in verlinkten Thread habe ich doch einen Pattern angegeben, mit dem man auf zusammengehörigen Tags matchen kann, ohne dass dabei über Tag-Grenzen hinaus gemacht wird:

<ul>(((?!&lt;/?ul&gt;).)*){green}&lt;/ul&gt;

Da kannst du doch direkt die erlaubten Tagnamen einsetzen:

<(b|strong|i|em|u)>(((?!&lt;/?\1&gt;).)*){green}&lt;/\1&gt;

Jetzt willst du noch, dass ein bestimmtes Wort auftauchen muss, was aber auch kein Problem ist:

<(b|strong|i|em|u)>(((?!&lt;/?\1&gt;).)*){green}:::{style="color: orangered;"}MatchMe){orangered}:::{style="color: green;"}((?!&lt;/?\1&gt;).)*){green}&lt;/\1&gt;

herbivore

79 Beiträge seit 2007
vor 12 Jahren

Übrigens:
Für die Entwicklung und zum Test von regulären Ausdrücken gibt es nütliche Programme.
Eines ist der Regex Coach:
http://weitz.de/regex-coach

1.361 Beiträge seit 2007
vor 12 Jahren

Für die Entwicklung und zum Test von regulären Ausdrücken gibt es nütliche Programme. Eines ist der Regex Coach:
>

Ein anderes Programm (von herbivore) ist der On-the-fly Regex-Tester: Regex-Lab. Nur zu empfehlen!

beste Grüße
zommi