Nun, Alan Moores Antwort ist gut, aber ich würde sie etwas modifizieren, um sie kompakter zu machen. Für die Regex Compiler:
"([^"\\]*(\\.)*)*"
mit Alan Moores Ausdruck vergleichen:
"[^"\\]*(\\.[^"\\]*)*"
Die Erklärung ist sehr ähnlich wie Alan Moores ein:
Der erste Teil "
passt ein Anführungszeichen.
Der zweite Teil [^"\\]*
entspricht null oder mehr Zeichen außer Anführungszeichen oder Backslashes.
Und der letzte Teil (\\.)*
entspricht Backslash und was auch immer einzelnes Zeichen folgt. Achten Sie auf das *, dass diese Gruppe optional ist.
Die Teile beschrieben, zusammen mit der endgültigen "
(dh "[^"\\]*(\\.)*"
), wird übereinstimmen: "Einige Text" und "Noch mehr Text \" ", aber wird nicht übereinstimmen:" Noch mehr Text über \ "dieser Text \" ".
Um es zu ermöglichen, brauchen wir den Teil: [^"\\]*(\\.)*
wird so oft wie nötig wiederholt, bis ein unespaniertes Anführungszeichen auftaucht (oder es das Ende der Zeichenfolge erreicht und der Abgleichversuch fehlschlägt). So wickelte ich dieser Teil durch Klammern und fügte ein Sternchen hinzu. Jetzt stimmt es überein: "Etwas Text", "Noch mehr Text \" "," Noch mehr Text über \ "this text \" "und" Hello \\ ".
In C# -Code wird es wie folgt aussehen:
var r = new Regex("\"([^\"\\\\]*(\\\\.)*)*\"");
BTW, die Reihenfolge der zwei Hauptteilen: [^"\\]*
und (\\.)*
keine Rolle spielt. Sie können schreiben:
"([^"\\]*(\\.)*)*"
oder
"((\\.)*[^"\\]*)*"
Das Ergebnis wird das gleiche sein.
Nun müssen wir ein anderes Problem lösen: \"foo\"-"bar"
. Der aktuelle Ausdruck wird mit "foo\"-"
übereinstimmen, aber wir möchten ihn mit "bar"
vergleichen. Ich weiß nicht,
warum es
entgangen Zitate außerhalb von Strings in Anführungszeichen werden, aber wir können es leicht implementieren, indem Sie den folgenden Teil an den Anfang hinzu: (\G|[^\\])
. Es besagt, dass der Match-Start an dem Punkt erfolgen soll, an dem die vorherige Übereinstimmung beendet wurde oder nach einem beliebigen Zeichen außer Backslash. Warum brauchen wir \G
? Dies ist für den folgenden Fall, zum Beispiel: "a""b"
.
Beachten Sie, dass (\G|[^\\])"([^"\\]*(\\.)*)*"
mit -"bar"
in \"foo\"-"bar"
übereinstimmt. Um nur "bar"
zu erhalten, müssen wir die Gruppe angeben und ihr optional einen Namen geben, zum Beispiel "MyGroup". Dann wird C# -Code wie folgt aussehen:
[TestMethod]
public void RegExTest()
{
//Regex compiler: (?:\G|[^\\])(?<MyGroup>"(?:[^"\\]*(?:\.)*)*")
string pattern = "(?:\\G|[^\\\\])(?<MyGroup>\"(?:[^\"\\\\]*(?:\\\\.)*)*\")";
var r = new Regex(pattern, RegexOptions.IgnoreCase);
//Human readable form: "Some Text" and "Even more Text\"" "Even more text about \"this text\"" "Hello\\" \"foo\" - "bar" "a" "b" c "d"
string inputWithQuotedText = "\"Some Text\" and \"Even more Text\\\"\" \"Even more text about \\\"this text\\\"\" \"Hello\\\\\" \\\"foo\\\"-\"bar\" \"a\"\"b\"c\"d\"";
var quotedList = new List<string>();
for (Match m = r.Match(inputWithQuotedText); m.Success; m = m.NextMatch())
quotedList.Add(m.Groups["MyGroup"].Value);
Assert.AreEqual(8, quotedList.Count);
Assert.AreEqual("\"Some Text\"", quotedList[0]);
Assert.AreEqual("\"Even more Text\\\"\"", quotedList[1]);
Assert.AreEqual("\"Even more text about \\\"this text\\\"\"", quotedList[2]);
Assert.AreEqual("\"Hello\\\\\"", quotedList[3]);
Assert.AreEqual("\"bar\"", quotedList[4]);
Assert.AreEqual("\"a\"", quotedList[5]);
Assert.AreEqual("\"b\"", quotedList[6]);
Assert.AreEqual("\"d\"", quotedList[7]);
}
Ich mag diese Erklärung am besten. –
war eine gute Antwort – motevalizadeh
Cruising durch einige der Antworten, die dich berühmt gemacht haben ... Upvoting dieses für eine so klare Erklärung aus der schlimmsten Backslash-Suppe! :) – zx81