Хорошо, ответ Алана Мура хорош, но я бы немного его изменил, чтобы сделать его более компактным. Для компилятора регулярных выражений:
"([^"\\]*(\\.)*)*"
Сравните с выражением лица Алана Мура:
"[^"\\]*(\\.[^"\\]*)*"
Объяснение очень похоже на объяснение Алана Мура:
Первая часть "
соответствует кавычке.
Вторая часть [^"\\]*
соответствует нулю или более любых символов, кроме кавычек или обратной косой черты.
И последняя часть (\\.)*
соответствует обратной косой черте и любому последующему символу. Обратите внимание на *, говоря, что эта группа необязательна.
Описанные части, вместе с окончательным "
(то есть "[^"\\]*(\\.)*"
), будут соответствовать: «Некоторый текст» и «Еще больше текста \» », но не будут совпадать:« Еще больше текста об этом » текст \ "".
Чтобы сделать это возможным, нам нужна часть: [^"\\]*(\\.)*
повторяется столько раз, сколько необходимо, пока не появится неэкранированная кавычка (или она не достигнет конца строки и неудачная попытка сопоставления). Я обернул эту часть скобками и добавил звездочку. Теперь он соответствует: «Некоторый текст», «Еще больше текста», «Еще больше текста об этом тексте» и «Привет».
В коде C # это будет выглядеть так:
var r = new Regex("\"([^\"\\\\]*(\\\\.)*)*\"");
Кстати, порядок двух основных частей: [^"\\]*
и (\\.)*
не имеет значения. Вы можете написать:
"([^"\\]*(\\.)*)*"
или
"((\\.)*[^"\\]*)*"
Результат будет таким же.
Теперь нам нужно решить еще одну проблему: \"foo\"-"bar"
. Текущее выражение будет соответствовать "foo\"-"
, но мы хотим сопоставить его с "bar"
. Я не знаю
почему бы избежать экранированных кавычек за пределами цитируемых строк
но мы можем легко это реализовать, добавив в начало следующую часть: (\G|[^\\])
. Это говорит о том, что мы хотим, чтобы матч начинался в том месте, где закончился предыдущий матч, или после любого символа, кроме обратной косой черты. Зачем нам нужно \G
? Это для следующего случая, например: "a""b"
.
Обратите внимание, что (\G|[^\\])"([^"\\]*(\\.)*)*"
соответствует -"bar"
в \"foo\"-"bar"
. Итак, чтобы получить только "bar"
, нам нужно указать группу и при необходимости дать ей имя, например «MyGroup». Тогда код C # будет выглядеть так:
[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]);
}