То, что у вас есть, является примером техники "развернутого цикла" Фридла, но вы, кажется, немного путаетесь в том, как выразить ее в виде строкового литерала. Вот как это должно выглядеть для компилятора регулярных выражений:
"[^"\\]*(?:\\.[^"\\]*)*"
Начальный "[^"\\]* соответствует кавычке, за которой следует ноль или более любых символов, кроме кавычек или обратной косой черты. Только эта часть вместе с последним " будет соответствовать простой строке в кавычках без встроенных escape-последовательностей, таких как "this" или "".
Если он обнаруживает обратную косую черту, \\. использует обратную косую черту и все, что следует за ней, а [^"\\]* (опять же) использует все до следующей обратной косой черты или кавычки. Эта часть повторяется столько раз, сколько необходимо, пока не появится неэкранированная кавычка (или она не достигнет конца строки и попытка сопоставления не удастся).
Обратите внимание, что это будет соответствовать "foo\"- в \"foo\"-"bar". Это может показаться недостатком в регулярном выражении, но это не так; это ввод недействителен. Цель состояла в том, чтобы сопоставить строки в кавычках, необязательно содержащие экранированные кавычки с обратной косой чертой, встроенные в другой текст - почему должны быть экранированные кавычки за пределами строк в кавычках? Если вам действительно нужно это поддерживать, у вас гораздо более сложная проблема, требующая совсем другого подхода.
Как я уже сказал, это то, как регулярное выражение должно выглядеть для компилятора регулярных выражений. Но вы пишете его в виде строкового литерала, а они, как правило, специально обрабатывают определенные символы, т. е. обратную косую черту и кавычки. К счастью, дословные строки C# избавляют вас от хлопот, связанных с двойным экранированием обратной косой черты; вам просто нужно экранировать каждую кавычку другой кавычкой:
Regex r = new Regex(@"""[^""\\]*(?:\\.[^""\\]*)*""");
Таким образом, правило заключается в двойных кавычках для компилятора C# и двойных обратных косых чертах для компилятора регулярных выражений - красиво и просто. Это конкретное регулярное выражение может выглядеть немного неуклюжим, с тремя кавычками на каждом конце, но рассмотрите альтернативу:
Regex r = new Regex("\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"");
В Java вы всегда должны писать их именно так. :-(
Ответ 2 · рейтинг 12 · Ricardo Nolde
Регулярное выражение для захвата строк (с \ для экранирования символов), для движка .NET:
(?> | especify nonbacktracking
(?(STR) | if (STRING MODE) then
(?(ESC) | if (ESCAPE MODE) then
.(?<-ESC>) | match any char and exits escape mode (pop ESC)
| | else
\\(?<ESC>) | match '\' and enters escape mode (push ESC)
) | endif
| | else
(?!) | do nothing (NOP)
) | endif
| | -- OR
(?(STR) | if (STRING MODE) then
"(?<-STR>) | match '"' and exits string mode (pop STR)
| | else
"(?<STR>) | match '"' and enters string mode (push STR)
) | endif
| | -- OR
(?(STR) | if (STRING MODE) then
. | matches any character
| | else
(?!) | do nothing (NOP)
) | endif
)+ | REPEATS FOR EVERY CHARACTER
Чтобы поэкспериментировать с регулярными выражениями, я рекомендую Rad Software Regular Expression Designer, который имеет хороший "Язык Вкладка "Элементы" с быстрым доступом к некоторым основным инструкциям. Он основан на механизме регулярных выражений .NET.
Ответ 3 · рейтинг 4 · Tim Pietzcker
"(\\"|\\\\|[^"\\])*"
должно сработать. Соответствует экранированной кавычке, экранированной обратной косой черте или любому другому символу, кроме кавычки или обратной косой черты. Повторение.
In C#:
StringCollection resultList = new StringCollection();
Regex regexObj = new Regex(@"""(\\""|\\\\|[^""\\])*""");
Match matchResult = regexObj.Match(subjectString);
while (matchResult.Success) {
resultList.Add(matchResult.Value);
matchResult = matchResult.NextMatch();
}
Изменить: в список добавлена экранированная обратная косая черта для правильной обработки "This is a test\\".
Объяснение:
Сначала найдите символ кавычки.
Затем альтернативы оцениваются слева направо. Движок сначала пытается сопоставить экранированную цитату. Если это не совпадает, он пытается использовать экранированную обратную косую черту. Таким образом, он может различать "Hello \" string continues" и "String ends here \\".
Если какой-либо из них не совпадает, то допускается все остальное, кроме кавычек или символов обратной косой черты. Затем повторите.
Наконец, сопоставьте закрывающую цитату.
Ответ 4 · рейтинг 3 · Jason
Я рекомендую получить RegexBuddy. Это позволяет вам играть с ним, пока вы не убедитесь, что все в вашем тестовом наборе совпадает.
Что касается вашей проблемы, я бы попробовал четыре / вместо двух:
\"[^\"\\\\]*(?:\\.[^\"\\\\]*)*\"
Ответ 5 · рейтинг 2 · Kamarey
Регулярное выражение
(?<!\\)".*?(?<!\\)"
также будет обрабатывать текст, начинающийся с экранированной кавычки:
\"Some Text\" Some Text "Some Text", and "Some more Text" an""d "Even more text about \"this text\""
Ответ 6 · рейтинг 2 · Alex
Что ж, ответ Алана Мура хорош, но я бы немного изменил его, чтобы сделать его более компактным. Для компилятора регулярных выражений:
"([^"\\]*(\\.)*)*"
Сравните с выражением Алана Мура:
"[^"\\]*(\\.[^"\\]*)*"
Объяснение очень похоже на объяснение Алана Мура:
Первая часть " соответствует кавычкам.
Вторая часть [^"\\]* соответствует нулю или более любых символов, кроме кавычек или обратной косой черты.
И последняя часть (\\.)* соответствует обратной косой черте и любому отдельному символу, следующему за ней. Обратите внимание на *, говорящий о том, что эта группа необязательна.
Описанные части вместе с конечным " (т.е. "[^"\\]*(\\.)*") будут соответствовать: "Некоторый текст" и "Еще больше текста\"", но не будут соответствовать: "Еще больше текста об \"этом тексте\"".
Чтобы сделать это возможным, нам нужна часть: [^"\\]*(\\.)* повторяется столько раз, сколько необходимо, пока не появится неэкранированная кавычка (или она не достигнет конца строки и попытка сопоставления не удастся). Поэтому я заключил эту часть в скобки и добавил звездочку. Теперь он соответствует: "Некоторый текст", "Еще больше текста\"", "Еще больше текста об \"этом тексте\"" и "Здравствуйте\\".
В коде 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]);
}
Ответ 7 · рейтинг 1 · Krill
Я знаю, что это не самый чистый метод, но в вашем примере я бы проверил символ перед ", чтобы увидеть, является ли он \. Если это так, я бы проигнорировал цитату.
Ответ 8 · рейтинг 1 · Emre
Подобно RegexBuddy, опубликованному @Blankasaurus, RegexMagic тоже помогает.
Ответ 9 · рейтинг 1 · Piotr Zierhoffer
Простой ответ без использования ?:
"([^\\"]*(\\")*)*\"
или, как дословная строка
@"^""([^\\""]*(\\"")*(\\[^""])*)*"""
Это просто означает:
найди первую "
найти любое количество символов, которые не являются \ или "
найти любое количество экранированных кавычек \"
найти любое количество экранированных символов, которые не являются кавычками
повторяйте последние три команды, пока не найдете "
Я считаю, что это работает так же хорошо, как ответ @Alan Moore, но для меня его легче понять. Он также принимает несбалансированные ("несбалансированные") котировки.
Ответ 10 · Fried Hoeben
Любой шанс, который вам нужно сделать: \"[^\"\\\\]*(?:\\.[^\"\\\\]*)*\"
Ответ 11 · Babu James
Если вы можете определить начало и конец, должно работать следующее: