2010-07-20 9 views
5

Ich bin in Ordnung mit grundlegenden regulären Ausdrücken, aber ich bin ein bisschen verloren verloren pos/neg Look ahead/hinterher.mit Regex, um alle Zeichen vorzuspringen, bis eine bestimmte Folge von Buchstaben mit negativen Lookahead

Ich versuche, die ID # daraus zu ziehen:

[keyword stuff=otherstuff id=123 morestuff=stuff]

Es unbegrenzte Mengen an „Stoff“ vor oder nach sein könnte. Ich war die Regex Coach mit Debug zu helfen, was ich versucht habe, aber ich bin nicht in Bewegung nach vorne mehr ...

Bisher habe ich dies:

\[keyword (?:id=([0-9]+))?[^\]]*\] 

Welche Pflege braucht der irgendwelche zusätzlichen Attribute nach der ID, aber ich kann nicht herausfinden, wie alles zwischen Schlüsselwort und ID zu ignorieren ist. Ich weiß, ich kann nicht gehen [^id]* Ich glaube, ich muss ein negatives Lookahead wie dieses verwenden, aber ich denke, da es null Breite ist, bewegt es sich nicht von dort weiter. Das funktioniert nicht, entweder:

\[keyword[A-z0-9 =]*(?!id)(?:id=([0-9]+))?[^\]]*\] 

Ich habe überall nach Beispielen suchen, haben aber keine gefunden. Oder vielleicht habe ich es getan, aber sie sind so weit über meinen Kopf gegangen, dass ich nicht einmal wusste, was sie waren.

Hilfe! Danke.

EDIT: Es muss auch [Schlüsselwort stuff = otherstuff] übereinstimmen, wobei id = überhaupt nicht existiert, also muss ich eine 1 oder 0 in der ID # -Gruppe haben. Es gibt auch andere [otherkeywords id = 32], die ich nicht abgleichen möchte. Das Dokument muss mehrere [Keyword-ID = 3] in allen Dokumenten mit preg_match_all übereinstimmen.

+0

Die angebotenen Lösungen funktionieren großartig und wahrscheinlich Benchmark schneller als mit jeder Art von Lookahead, ich werde es definitiv so machen. Aber für meine eigene Neugier, und vielleicht jemand, der diese Frage mit Google in der fernen Zukunft trifft, ist die Methode, die ich versuchte, um zu bekommen? Das heißt, können Lookaheads verwendet werden, um einige Dinge zu überspringen, bis ein bestimmtes Wort getroffen wird? – phazei

Antwort

2

No Look-Ahead/hinten erforderlich:

/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/ 

Hinzugefügt wurde die Endung '[^]] *]' für einen echten Tag Ende zu überprüfen, nicht notwendig sein könnte.

Edit: nach vorne schauen/hinter hinzugefügt, um den \ b-ID wie sonst könnte es [keyword you-dont-want-this-guid=123123-132123-123 id=123]

$ php -r 'preg_match_all("/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/","[keyword stuff=otherstuff morestuff=stuff]",$matches);var_dump($matches);' 
array(2) { 
    [0]=> 
    array(1) { 
    [0]=> 
    string(42) "[keyword stuff=otherstuff morestuff=stuff]" 
    } 
    [1]=> 
    array(1) { 
    [0]=> 
    string(0) "" 
    } 
} 
$ php -r 'var_dump(preg_match_all("/\[keyword(?:[^\]]*?\bid=([0-9]+))?[^\]]*?\]/","[keyword stuff=otherstuff id=123 morestuff=stuff]",$matches),$matches);' 
int(1) 
array(2) { 
    [0]=> 
    array(1) { 
    [0]=> 
    string(49) "[keyword stuff=otherstuff id=123 morestuff=stuff]" 
    } 
    [1]=> 
    array(1) { 
    [0]=> 
    string(3) "123" 
    } 
} 
+0

Ich dachte, dass das funktionierte, aber nachdem ich es getestet habe, scheint es, dass ID nicht optional ist und es sein muss. – phazei

+0

Oh, hat das nicht bekommen, wird behoben, – Wrikken

+0

Fixed (in einem nicht-Capturing-Untermuster) – Wrikken

2

Sie müssen nicht übereinstimmen.

Da die Frage mit PHP markiert ist, verwenden Sie preg_match_all() und speichern Sie das Match in $ matches.

Hier ist, wie:

<?php 

    // Store the string. I single quote, in case there are backslashes I 
    // didn't see. 
$string = 'blah blah[keyword stuff=otherstuff id=123 morestuff=stuff] 
      blah blah[otherkeyword stuff=otherstuff id=555 morestuff=stuff] 
      blah blah[keyword stuff=otherstuff id=444 morestuff=stuff]'; 

    // The pattern is '[keyword' followed by not ']' a space and id 
    // The space before id is important, so you don't catch 'guid', etc. 
    // If '[keyword' is always at the beginning of a line, you can use 
    // '^\[keyword' 
$pattern = '/\[keyword[^\]]* id=([0-9]+)/'; 

    // Find every single $pattern in $string and store it in $matches 
preg_match_all($pattern, $string, $matches); 

    // The only tricky part you have to know is that each entire match is stored in 
    // $matches[0][x], and the part of the match in the parentheses, which is what 
    // you want is stored in $matches[1][x]. The brackets are optional, since it's 
    // only one line. 
foreach($matches[1] as $value) 
{  
    echo $value . "<br/>"; 
} 
?> 

Ausgang:

123 
444 

(555 übersprungen wird, wie es sein sollte)

PS

Sie auch \b verwenden können, statt eines literalen Raumes, wenn die Re könnte stattdessen eine Registerkarte sein. \b represents a word boundary ... in diesem Fall der Anfang eines Wortes.

$pattern = '/\[keyword[^\]]*\bid=([0-9]+)/'; 
+0

Das wird nicht funktionieren, weil ich preg_match_all für ein großes Dokument verwende, das [otherkeyword id = 324] haben könnte, das ich nicht zuordnen kann. Außerdem muss ich [keyword stuff = otherstuff] entsprechen, wo es keine ID gibt. – phazei

+0

@ phazei Bearbeitete meine Antwort, um mehrere Antworten anzuzeigen und andere Schlüsselworte zu ignorieren. –

+0

Kühl. Du hast alles nach der ID übersprungen, obwohl ich das behalten muss, da ich es benutze, um den gesamten Abschnitt [keyword x = x] zu ersetzen, aber das ist kein Problem für mich, mich zu ändern. Ich sehe, dass Sie das größte Problem behoben haben, das ich mit Wrikkens [^]] * nach dem Keyword hatte. Warum funktioniert das und verursacht nicht alles bis zum letzten "]"? – phazei

0

Ich denke, das ist das, was auf Sie bekommen:

\[keyword(?:\s+(?!id\b)[A-Za-z]+=[^\]\s]+)*(?:\s+id=([0-9]+))?[^\]]*\] 

(Ich gehe davon aus Attributnamen nur ASCII-Buchstaben enthalten, während die Werte jedes Nicht-Leerzeichen außer ] enthalten .)

(?:\s+(?!id\b)[A-Za-z]+=[^\]\s]+)* passt zu einer beliebigen Anzahl von attribute=value Paaren (und dem vorangestellten Leerzeichen), solange der Attributname nicht id ist. Die \b (Wortgrenze) ist dort nur für den Fall, dass es Attributnamen gibt, die starten mit id, wie idiocy. Sie müssen \bnicht vor den Attributnamen dieses Mal setzen, denn Sie wissen, dass jedem Namen, mit dem es übereinstimmt, ein Leerzeichen vorangestellt wird. Wie Sie jedoch wissen, ist der Lookahead-Ansatz in diesem Fall übertrieben.

nun dazu:

[A-z0-9 =] 

Die A-z ist entweder ein Tippfehler oder ein Fehler. Wenn Sie erwarten, dass alle Groß- und Kleinbuchstaben übereinstimmen, tut es das auch. Aber es passt auch

'[', ']', '^', '_', '`` and '\' 

... weil ihre Codepunkte zwischen denen der Großbuchstaben und der Kleinbuchstaben liegen. ASCII-Buchstaben, das heißt.

Verwandte Themen