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