PHP rocks! wünscht allen Mitgliedern einen guten Rutsch ins neue Jahr 2017 !!!
Hinweis: Das Forum zieht um! Um keine Datenverluste zu haben, schalten wir zwecks Übernahme der Daten das Forum am Sonntag, den 24.04.2016 um ca. 21:00 Uhr offline und passen anschliessend die DNS-Einträge an.
www.php-rocks.de wird euch dann nach den Aktualisierungen der DNS-Server wieder wie gewohnt uneingeschränkt zur Verfügung stehen.
Danke für euer Verständnis!

Themabewertung:
  • 0 Bewertung(en) - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Mehrzeiliges Matching mit RegEx
#1
Hallo,

Ich komme grad nicht drauf, wie ich bspw. aus folgendem Text
Code:
{{

   test foo:bar
   test foo:baz

}}
die Werte der beiden Zeilen mit test bekomme, also
Code:
foo:bar // Matching #1
foo:baz // Matching #2
Ich möchte, dass für jede Zeile ein Matching existiert.

Mein aktueller Stand von mehrfachen Tests sieht so aus:
Code:
/(?<=\{\{)\s+?test\s+([\w:]+)/ius
Der matcht aber nur den ersten ( ich verwende preg_match_all )

Die Regel soll lauten: Innerhalb der doppelt geschweiften Klammern alle Werte, die nach der Zeichenfolge "test" kommen, als separates Matching!

Ohne die geschweiften Klammern zu berücksichtigen bekomme ich das hin, aber ich möchte explizit vermeiden, dass eine Wertangabe zufällig im normalen Dokumentenbereich existiert und ebenfalls gematcht wird.

Wer kann mir einen Schubs in die Richtung geben?
Danke
Antworten
#2
Moin

Nachdem ich jetzt ne Weile auf regex101.com rumprobiert habe, meine Empfehlung: Nimm zwei Big Grin

Mit dem ersten RegEx schauste auf die Klammern und ziehst dir den Inhalt zwischen den Klammern und mit dem zweiten RegEx eben deine Key-Value Paare.



Zitat:
Code:
/(?<=\{\{)\s+?test\s+([\w:]+)/ius
Der matcht aber nur den ersten

Was daran liegt, dass dein RegEx vorschreibt, dass in jeder Zeile die zwei geschweiften Klammern exisiteren müssen. Das war auch mein Problem als ich rumprobiert hatte. Egal was ich versuchte: Jene Klammern im Zusammenhang mit den Zeilenumbrüchen haben dazu geführt hatten, dass entweder nur ein oder kein Match aufgetreten ist.

Es wäre super wenn mal ein RegEx Guru drüber schauen könnte, ob/wie man das unter Umständen besser lösen könnte.

Grüße
Antworten
#3
Hi,

Ich sehe, Dir gings wie mir... Wink
Ich habe habe auch auf regex101 rumgedoktort, kam aber ziemlich auf die selben Ergebnisse, wie Du scheint mir.

Zitat:Was daran liegt, dass dein RegEx vorschreibt, dass in jeder Zeile die zwei geschweiften Klammern exisiteren müssen.
In dem Fall ja, das war jetzt auch nur der aktuelle Stand, habe viele Variationen durchgetestet.

Ich werde mir mal überlegen, ob ich das auf zwei RegEx ausweite. Ist nicht meine favorisierte Lösung, aber sollte es nicht anders gehen, muß es wohl sein.
Danke für die Zeit, die Du Dir genommen hast.

Gruß Arne
Antworten
#4
Und, wie sieht Deine Lösung jetzt aus?
Antworten
#5
Hi Till,

Momentan verzichte ich einfach auf die mehrzeilige Schreibweise, um andere Komponenten zu priorisieren.
Sollte ich mich dem Problem wieder annehmen und eine passable Lösung finden, poste ich die gerne hier.

Gruß Arne
Antworten
#6
Reguläre Ausdrücke, die für ein Capturing-Pattern eine variable Anzahl an Werten liefern können, sind glaube ich per Design nicht möglich. Etwa ([a-z])* liefert in "abcdef" das letzte Match ("f"), nicht etwa ein Array mit 6 Einträgen.

Deshalb ist eine Lösung glaube ich nur mit Lookbehind-Assertions variabler Länge möglich. Die werden von PHPs Regex-Engine aber nicht unterstützt.

Die Idee ist, bei jeder Zeile, die mit "test" beginnt, zu schauen, ob sie sich in einem "{{…}}"-Block befindet.

Hier ein grobes Beispiel in Python (regex-Modul):

Code:
import regex

pattern = r"""
(?<=
 (?:\n|\r\n|\A)
 \{\{
 (?:.*?(?:\n|\r\n))*
)
\x20*test\x20*(.*?)(?:\n|\r\n)
(?=
 (?:.*?(?:\n|\r\n))*
 \}\}
 (?:\n|\r\n|\Z)
)
"""

input = r"""{{

  test foo:bar1
  test foo:baz1

}}

qix

{{
  test foo:bar2

  test foo:baz2
}}

qux

test bug

{{

  test foo:bar3
  test foo:baz3

}}"""

m = regex.findall(pattern, input, regex.VERBOSE)
print m

# ['foo:bar1', 'foo:baz1', 'foo:bar2', 'foo:baz2', 'bug', 'foo:bar3', 'foo:baz3']

Das Beispiel ist noch nicht ganz korrekt. Es müsste in den Assertions noch geprüft werden, dass keine der Zeilen hinter der vorherigen Zeile mit "{{" und vor der nachfolgenden Zeile mit "}}" das jeweils andere Klammernpaar ("}}", "{{") enthält. Sonst werden auch "test"-Zeilen, die zwischen zwei "{{…}}"-Blöcken stehen, gefunden ("bug" im Beispiel). Das habe ich aus Gründen der Lesbarkeit aber nicht hinzuzufügen versucht.

Als Fazit bleibt aber, dass das sicherlich kein Problem ist, das besonders sinnvoll mit einem einzigen regulären Ausdruck zu lösen ist.
Antworten
#7
Hi Marc,

Danke für den Anstoß und die Mühe.
Aber wie Du schon sagst, unterstützt die RegEx-Engine von PHP keine LookBehind Assertionen variabler Länge, was das ganze doch wieder zu einem Problem macht.

Ich überlege nach Deinem Beitrag allerdings, ob ich das Parsen an der Stelle auf Perl auslagere...
Egal, wie ich mich entscheide: Danke für den Ansatz.

Gruß Arne
Antworten


Gehe zu: