Bene, la risposta di Alan Moore è buona, ma vorrei modificarla un po 'per renderla più compatta. Per il compilatore regex:
"([^"\\]*(\\.)*)*"
Confronta con l'espressione di Alan Moore:
"[^"\\]*(\\.[^"\\]*)*"
La spiegazione è molto simile a Alan Moore di uno:
La prima parte "
corrisponde a un segno di virgolette.
La seconda parte [^"\\]*
corrisponde a zero o più caratteri diversi da virgolette o barre retroverse.
E l'ultima parte (\\.)*
corrisponde al backslash e qualunque sia il singolo carattere che lo segue. Presta attenzione al simbolo *, dicendo che questo gruppo è facoltativo.
Le parti descritte, insieme con la finale "
(cioè "[^"\\]*(\\.)*"
), corrisponderà: "Un testo" e "Ancora più Text \" "ma non corrisponderanno: 'Ancora più testo su \' questo testo \" ."
Per rendere possibile, abbiamo bisogno della parte:. [^"\\]*(\\.)*
ottiene ripetuto tante volte quanto necessario fino a quando un segno di virgolette senza caratteri di escape gira in su (o raggiunge la fine della stringa e il tentativo match fallisce) Così ho avvolto quella parte tra parentesi e aggiunge un asterisco. Ora corrisponde a: "Alcuni testo", "Ancora più testo \" "," Ancora testo su \ "questo testo \" "e" Ciao \\ ".
Nel codice C# che sarà del tipo:
var r = new Regex("\"([^\"\\\\]*(\\\\.)*)*\"");
BTW, l'ordine delle due parti principali: [^"\\]*
e (\\.)*
non importa. È possibile scrivere:
"([^"\\]*(\\.)*)*"
o
"((\\.)*[^"\\]*)*"
Il risultato sarà lo stesso.
Ora dobbiamo risolvere un altro problema: \"foo\"-"bar"
. L'espressione corrente corrisponderà a "foo\"-"
, ma vogliamo abbinarla a "bar"
. Non so
virgolette perché ci sarebbe sfuggito fuori di stringhe tra virgolette
ma possiamo implementare facilmente aggiungendo la seguente parte per l'inizio: (\G|[^\\])
. Dice che vogliamo che la partita inizi nel punto in cui è terminata la partita precedente o dopo qualsiasi carattere tranne il backslash. Perché abbiamo bisogno di \G
? Questo è per il seguente caso, ad esempio: "a""b"
.
Si noti che (\G|[^\\])"([^"\\]*(\\.)*)*"
corrisponde a -"bar"
in \"foo\"-"bar"
. Quindi, per ottenere solo "bar"
, dobbiamo specificare il gruppo e facoltativamente dargli un nome, ad esempio "MyGroup". Quindi il codice C# sarà simile a:
[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]);
}
Mi piace di più questa spiegazione. –
è stata una buona risposta – motevalizadeh
Navigare attraverso alcune delle risposte che ti hanno reso famoso ... Sconfiggere questo per fare una spiegazione così chiara della peggiore zuppa di backslash! :) – zx81