2016-06-10 11 views
3

Ich brauche eine Regex, die eine bestimmte Erfassungsgruppe entspricht, die in einem mehrzeiligen Kommentar/* ... */fällt.Suchen Sie ein Wort im mehrzeiligen Kommentar mit einer Regex

Insbesondere muss ich PHP-Variablen-Definitionen innerhalb mehrzeilige Kommentare

zum Beispiel finden:

/* other code $var = value1 */ 
$var = value2 ; 

/* 
other code 
$var = value3 ; 
other code 
*/ 

müssen nur die beiden Vorkommen von ‚$ var =‘ passen in den Kommentaren, aber nicht den einen außerhalb der Kommentar.

für das obige Beispiel schrieb ich einen regulären Ausdruck, die uneingeschränkte Lookbehind verwendet, wie dies

(?<=[/][\*][^/]+)(\$var) | (?<=[/][\*][^\*]+)(\$var) 

aber diese regex versagt, falls es sowohl charachter findet * und/auch wenn sie voneinander entfernt ist voneinander zwischen der Kommentar öffnende Tag '/ *' und $ var, die nicht die gewünschte bahaviour ist:

es zum Beispiel in dem Fall versagt:

$var = .... ; 

/* 
other * code/
$var = .... ; 
other code 
*/ 

bacause es sowohl '*' findet eine nd '/', auch wenn es nicht das schließende Tag des Kommentars ist.

Der Schlüsselpunkt ist, dass ich ein Token, das eine Kombination aus zwei Zeichen ist, nicht negieren kann, sondern nur eins nach dem anderen negieren kann: [^ *] oder [^ /].

... außerdem kann ich nicht das Token [\ s \ S] anstelle von [^ /] und [^ *] verwenden, weil es $ var aus Kommentaren mit vorangestelltem Kommentarblock auswählen würde.

Irgendwelche Ideen? Ist es sogar möglich mit normalen Regex dies zu erreichen? Oder würde ich etwas anderes brauchen?

+1

Wie wäre es mit ['\ G' wie in dieser Demo bei regex101] (https://regex101.com/r/eO9fU4/1). –

+0

Vielen Dank! Dieser Regex beantwortet die Frage. Mit dem Meta-Zeichen \ G funktioniert gut! Das einzige Problem ist, dass es für Anfänger etwas schwer zu verstehen ist ... Ich verstehe, warum die Verwendung in diesem Fall, aber ich bin immer noch nicht genau mit der allgemeinen Bedeutung von (?! ^) – Obomar

+0

Großartig es hilft. Ich habe eine Antwort mit einer Erklärung gegeben. –

Antwort

1

Idee durch die Verwendung von \G to glue Spielen zu /*

(?:/\*|\G(?!^))(?:(?!\*/)[^$])*\K\$var\s*=\s*(?:(?!\*/)[^$;])* 

könnte schwierig sein, zu verstehen, wenn Sie nicht viel mit regulären Ausdrücken zu tun. See regex101 for demo.

\G kann als "Klebstoff" gesehen werden, es wird am Ende eines vorherigen Spiels fortgesetzt. Aber \G entspricht auch dem Anfang der Zeichenfolge. Deshalb wird das negative Lookahead \G(?!^) nur weiter verwendet.

  • /\*|\G(?!^) Dieser Teil ist der Beginn eines Spiels bei /* oder weiterhin passende zu finden.

  • (?:(?!\*/)[^$])* Spiel jede ammount von Zeichen, die nicht $ (negierte Klasse) sind zwar nicht den Kommentar (?!\*/) für Sachen endet vor/zwischen $var

  • \K\$var\K resets Beginn des berichtet Spiel vor $var auftritt. \K kann als Alternative zu einem Lookhind mit variabler Breite nützlich sein, das in pcre nicht verfügbar ist.

  • \s*=\s*(?:(?!\*/)[^$;])* mit dem Wert der Variablen übereinstimmen. Das ist alles andere als perfekt. Wäre Änderung erforderlich, wenn quoted values oder nicht bequem für Ihre Eingabe. Nach = entspricht es [^$;] Zeichen, die nicht Dollar oder Semikolon (?!\*/) sind, solange es keine */ voraus gibt.

Diese Regex überprüft nicht, ob es es (*SKIP)(*FAIL) wie in this demo nur eigentlich ein Kommentar-End */ ist bindet würde Einstimmungen /*
Eine weitere Idee, um Art von this trick mit Verben zu verwenden.

0

So etwas wie dies funktionieren könnte:

/\/\*.*?\$var\s*\=\s(.*?)(?=\s*;)/s 

Verbrauch:

$str = '$var = .... ; 
/* 
other code 
$var = ..... ; 
other code 
*/'; 
preg_match('/\/\*.*?\$var\s*\=\s(.*?)(?=\s*;)/s', $str, $matches); 

var_dump($matches); 

Will Ausgang:

array(2) { 
    [0]=> 
    string(26) "/* 
other code 
$var = ....." 
    [1]=> 
    string(5) "....." 
} 

Und die Zeichenfolge in $matches[1] gespeichert

Try it online

+0

Danke, aber leider stimmt Ihre Lösung auch mit Kommentaren überein und scheint keinen Fall von vorherigen Kommentaren zu berücksichtigen ... – Obomar

+1

@Obomar das Spiel wird in der Spielgruppe '1' nicht '0':' $ Übereinstimmungen [1 ] ' – andlrc

+0

richtig, Ihre Lösung funktioniert konzeptionell in dem Beispiel, das ich zur Verfügung gestellt habe, das aber nicht komplett ist (sorry dafür). Ich habe die Frage aktualisiert und das Beispiel in ein allgemeineres Szenario geändert, in dem es mehr als einen Block mehrzeiliger Kommentare gibt: Betrachte den/* */$ var/* -Code .. $ var ..code .. */würde es immer noch sein Arbeit? Es scheint, dass es die $ var außerhalb Kommentare auch übereinstimmen würde. – Obomar

1

Wie wäre:

$str = ' 
/* other code */ 
$var = "var1"; 

/* 
other code 
$var = "var2"; 
other code 
*/ 
/* other code */ 
$var = "var3"; 

/* 
other code/<-- a slash here 
$var = "var4"; 
other code 
*/'; 

preg_match_all('~/\*(?:(?!\*/).)+?(\$var = .+?;).*?\*/~s', $str, $m); 
print_r($m[1]); 

Ausgang:

Array 
(
    [0] => $var = "var2"; 
    [1] => $var = "var4"; 
) 
+0

Negative Lookahead ist nur '(?!', Nicht '(?! ='. Auch die einschließende Gruppe muss nur ein Zeichen zu einem Zeitpunkt verbrauchen. So funktioniert Ihre Regex nur zufällig. –

+0

@AlanMoore: Ja, Sie haben Recht, behoben. – Toto

+0

Ihre Lösung funktioniert mit dem von Ihnen bereitgestellten Beispiel, scheint aber in einem allgemeineren Szenario wie dem von @AlanMoore vorgeschlagenen zu versagen – Obomar

2

Dieses nur $var entspricht, und nur in einem mehrzeiligen Kommentar:

(?s)\$var(?=(?:(?!/\*|\*/).)*\*/) 

DEMO

(?:(?!/\*|\*/).)* ist ein Captive Lookahead (auch bekannt als Tempered Greedy Token - guter Name, aber zu viele Silben), und es ist, wie Sie eine Sequenz im Gegensatz zu einem einzelnen Zeichen ausschließen. Dieser entspricht keinem oder mehr beliebigen Zeichen (einschließlich Newline, wegen der (?s)), solange es nicht das erste Zeichen von /* oder */ ist.

Der einschließende Lookahead ist erfolgreich, wenn er */ findet, ohne zuerst auf /* zu stoßen. Das bedeutet, dass die aktuelle Position innerhalb eines Kommentars liegen muss (es ist nicht notwendig, die Öffnung /* anzupassen). Und da das Lookahead keine Zeichen enthält, können Sie bei Bedarf mehr als ein Element pro Kommentar zuordnen.

Eine Sache, die diese Regex täuschen kann, ist eine */, die nicht wirklich näher kommen. Also diese:

$var = "*/"; 

$var = ...; 
// */ 

... würde übereinstimmen, auch wenn sie nicht in einem Kommentar sind.

+0

Sie haben Recht, Ihre Regex funktioniert wie erwartet und abgesehen von den Fällen, die Sie erwähnt haben, und ich würde noch einen weiteren Fall hinzufügen: $ var in/* $ var/* code ... */ – Obomar

+0

... und ich bin mir sicher, dass wir weitere Möglichkeiten finden könnten, um zu scheitern.Wie @Toto an anderer Stelle sagte, um dies richtig zu machen, brauchen Sie einen Parser Ich dachte, du müsstest die Werte, die * zugewiesen sind, mit $ var vergleichen nicht einmal versucht haben. –

+0

Vielen Dank @AlanMoore für Ihre Antwort, aus meiner Sicht ist Ihre Regex die eleganteste und verständlichste, aber leider muss ich Fälle von passenden Strings zwischen möglichen öffnenden Trennzeichen verwalten, sonst würde ich Ihre Regex verwenden. Ich hätte es erwähnen sollen, Entschuldigung. Zum Glück für diese Frage brauche ich keinen Parser, um nach Anführungszeichen in Anführungszeichen zu suchen oder verschachtelte Kommentare zu verwalten (und sie als solche zu behandeln), weil in meinem Fall in Anführungszeichen gesetzte Trennzeichen nicht vorhanden oder sehr selten sind gemeinsames Verhalten von mehrzeiligen Kommentaren, die Sie in Programmier-Text-Editoren erfahren :) – Obomar