PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Reguläre Expression


rotalever
2008-10-06, 19:27:07
Ich habe folgende regex in PHP, mit dem ich den Inhalt aus Klammern rausholen möchte:

preg_match_all("/(?:\(|\[)(.*?)(?:\)|\])/", $str, $matches, PREG_PATTERN_ORDER);

Wenn ich jetzt $str auf etwas wie

$str = "abc (abc1) [abc2] (abc3) [abc4]";

setze, dann kommt so ein Array raus:

Array
(
[0] => Array
(
[0] => (abc1)
[1] => [abc2]
[2] => (abc3)
[3] => [abc4]
)
[1] => Array
(
[0] => abc1
[1] => abc2
[2] => abc3
[3] => abc4
)
)

Die Elemente aus dem zweiten Teilarray benötige ich jeweils, soweit so gut.

Ich möchte aber, dass bei einem String wo nach den Klammern wieder normaler Text kommt, überhaupt nichts gemachted wird. Also bei

$str = "abc (abc1) [abc2] (abc3) [abc4] abc";

soll nichts gemachted werden. Geht das irgendwie?
Ich habe schon sowas probiert:
/^(?:[^\(\)\[\]]*?)((?:\(|\[)(.*?)(?:\)|\]))+$/
Aber das matched nicht richtig.

Gertz
2008-10-06, 21:47:56
sry, ist zwar ot, aber:

entweder regular expression oder aber regulaerer Ausdruck. ;)

Pinoccio
2008-10-06, 21:59:42
Ohne dir konkret bei diesem Problem weiterhelfen zu können, poste ich einfach mal einen Link (http://www.ultrapico.com/expresso.htm).
Bei meinen bisher sehr kurzen Ausflügen ins reguläre Land hat mir das Tool geholfen.

mfg

gereggter Gast
2008-10-06, 22:58:54
ICh bin mir nicht ganz sicher, ob ich dein programmatisches Anliegen verstehe.

Du möchtest, dass ein String wie "abc (abc1) [abc2] (abc3) [abc4]" in die matchenden Einzelteile zerlegt wird. Außerdem möchtest du verhindern, dass überhaupt etwas gesplittet wird, wenn zusätzlich hinter dem letzten matchenden Part etwas kommt, was nicht mehr matcht. So wie hier: "abc (abc1) [abc2] (abc3) [abc4] abc"?

Sofern ich dich richtig verstanden habe, worüber ich mir nicht sicher bin, wirst du das nicht mit regulären Ausdrücken lösen können. Da musst du schon eine If Anweisung draus machen. Wenn der String deinen Ansprüchen matcht (egal, wie diese auch sein mögen), kann der String gesplittet werden. Ansonsten halt nicht.

Monger
2008-10-07, 00:32:24
Jetzt mal langsam. Was GENAU ist denn deine Forderung?

Mein erster Ansatz wäre ja (in dem Fall in der Regex Geschmacksrichtung von .NET, keine Ahnung wie es da in PHP aussieht):

\(\w+\)|\[\w+\]

Den kann man jetzt natürlich noch verbessern. Damit man nicht noch von Hand wieder die Klammern rausrechnen muss, kann man diese z.B. als Lookaheads bzw. Lookbehinds realisieren:

(?<=\()\w+(?=\))|(?<=\[)\w+(?=\])

Was ansonsten vorne oder hintendran passiert, ist diesem Ausdruck dann völlig egal. Wenn dir das nicht reicht, musst du uns noch ein paar Details geben was konkret deine Anforderungen sind.

Edit:
Tippfehler auf Hinweis von samm berichtigt

samm
2008-10-07, 01:53:12
Monger: So wirklich hübsch wird der Ausdruck mit den Lookaheads jetzt auch nicht und das zweite "w" ist nicht escaped ;)

Rotalever:
/^(?:[^\(\)\[\]]*?)((?:\(|\[)(.*?)(?:\)|\]))+$/

Ich weiss jetzt nicht, was *? bedeutet, deswegen ist folgende Deutung evt. inkorrekt:
Würde der zitierte Ausdruck bedeuten: am Zeilenanfang steht keine Klammer auf oder zu, und zwar eine beliebige Anzahl davon, gefolgt von mehreren ( bla_bla ) bis zum Zeilenende? Wenn ja, wieso brauchst du den Teil, der den Zeilenanfang beschreibt? mit /((?:\(|\[)(.*)(?:\)|\]))+$/ müsstest du schon glücklich werden. Damit wird nur gematcht, wenn es bis zum Zeilenende Klammerungen hat und keine andern Zeichen.

Was zu beachten ist, wäre noch, dass nicht auf korrekte Klammerung geachtet wird. Es wird also auch [bla) gematcht. Wenn das eine Rolle spielt, müsstest du für jede Klammervariante eine Option verwenden, wie im ersten Beispiel von Monger.

Monger
2008-10-07, 11:18:50
Monger: So wirklich hübsch wird der Ausdruck mit den Lookaheads jetzt auch nicht und das zweite "w" ist nicht escaped ;)

Argh! Ich verbesser es gleich mal! :ugly:
Etwas, aber nicht viel hübscher wird der Ausdruck, wenn man mit benamsten Capturing Groups arbeitet. Das kann nur nicht jede Implementierung. Oder halt ne Bedingung, aber die sind auch nicht gerade schön.


Ich weiss jetzt nicht, was *? bedeutet, deswegen ist folgende Deutung evt. inkorrekt:

*? ist "lazy". Es werden also grundsätzlich mal alle Zeichen geschluckt - aber nur wenn sie nicht durch andere Kombinationsmöglichkeiten zuerst konsumiert wurden.

rotalever
2008-10-07, 14:05:31
Da anscheinend nicht so klar war, was ich eigentlich wollte, versuche ich es noch mal zu beschreiben.
Ich habe einen String, der Möglicherweise eingeklammerte Teilstrings enthält, zum Beispiel "bla bla bla (blub blub)", "blub blub" wäre dann der (in diesem Fall einzige) eingeklammerte Teilstring.
Ich möchte alle diese Teilstrings extrahieren und danach aus dem String rauslöschen, sodass z.B. nur noch "bla bla bla" übrig bleibt. Das funktioniert auch mit dem von mir zu erst genannten regulären Ausdruck.

Nun möchte ich aber gerne eine weitere Bedingung festlegen, und zwar sollen eingeklammerte Teilstrings nur extrahiert und später entfernt werden, wenn nach diesen kein normaler Text mehr folgt. Also bei "bla bla bla (blub blub) bla" soll nichts extrahiert oder gelöscht werden.

@gereggter Gast: Das mit der IF-Anweisung ist eine gute Idee. Ich müsste dann einen regulären Ausdruck nehmen, der feststellt ob es ein String der Form "bla bla bla (blub blub)" oder der Form "bla bla bla (blub blub) bla" etc ist und kann dann entscheiden ob ich meinen anderen reg. Ausdruck anwende um die eingeklammerten Strings zu extrahieren.

Monger
2008-10-07, 14:19:31
Ahh! Okay, dann sieht die Sache schon wieder ganz anders aus! :D

Ich dachte, dich interessiert explizit der Inhalt der Klammern.
Würde dir dann vielleicht ein Anchor helfen? \Z garantiert, dass der Ausdruck nur am Ende des Strings matchen kann.

Um also mein Beispiel von oben aufzugreifen:
(?:\(.+\)|\[.+\])\Z

Als Match bekommst du dann entweder einen Leerstring, oder eben dieses Klammernkonstrukt. Das kannst du dann mit einer String Operation à la Remove aus dem Originalstring entfernen.

rotalever
2008-10-07, 15:57:55
Also

preg_match_all("/(?:\(.*\)|\[.*\])\Z/",..);

ergibt das:

Array
(
[0] => Array
(
[0] => [abc2] (abc3) [abc4]
)

)

Da fehlt das "(abc1)", aufteilen könnte ich natürlich noch in einem zweiten Schritt.

Monger
2008-10-07, 16:15:01
Ach, so ne Shice...
Meine Konzentration ist echt am Arsch. Warum escape ich denn bitteschön die ganzen Punkte? :ugly:

Ja klar, er sucht jetzt gerade natürlich den größten zu klammernden Ausdruck, ist ja schließlich "Greedy". Und das ist in diesem Fall die beiden äußeren eckigen Klammern. Entweder wir ignorieren jetzt schlicht, dass jede Klammer auch wieder mit der selben Klammernsorte geschlossen werden muss:

[([].+[)\]]\Z

Oder wir bauen den bestehenden Ausdruck so aus, dass er beliebige Klammernpaare (mit eventuellen Leerzeichen dazwischen) akzeptiert:

(?:\(.+\)|\[.+\]\s*)+\Z

rotalever
2008-10-07, 17:40:33
[([].+[)\]]\Z

Ja, das ist gut. Dann kann ich mit einem IF entscheiden, ob der String weiterzerlegt wird, oder nicht der gwünschten Form entspricht.