Регулярное выражение для строки в кавычках с экранирующими кавычками

122

Как получить подстроку " It's big \"problem "с помощью регулярного выражения?

s = ' function(){  return " It\'s big \"problem  ";  }';     
Дэвид
источник
1
Как найти «Это» в строке, содержащей только «Есть»? Я бы исправил это для вас, но я не знаю, какие соглашения об одинарных кавычках / escape-кодах применяются на используемом вами языке.
Джонатан Леффлер
2
Собственно, глядя на даты, я вижу, что второй вопрос дублирует этот. В любом случае, обязательно ознакомьтесь с моим ответом .
ridgerunner 08
@ridgerunner: Я голосую за то, чтобы закрыть это, как вы предложили. Верно, что другой вопрос более свежий, но он также намного лучше (в основном благодаря вашему ответу).
Алан Мур

Ответы:

160
/"(?:[^"\\]|\\.)*"/

Работает в Regex Coach и PCRE Workbench.

Пример теста на JavaScript:

    var s = ' function(){ return " Is big \\"problem\\", \\no? "; }';
    var m = s.match(/"(?:[^"\\]|\\.)*"/);
    if (m != null)
        alert(m);

PhiLho
источник
24
Имеет смысл. Обычный английский: две кавычки, окружающие ноль или более «любого символа, кроме кавычек или обратной косой черты» или «обратной косой черты, за которой следует любой символ». Не могу поверить, что я не думал об этом ...
Ajedi32
7
Я сам отвечу. =) (?:...)- это пассивная или не захватывающая группа. Это означает, что на него нельзя ссылаться позже.
magras
после долгих поисков и тестов это реальное и единственное решение, которое я нашел для этой распространенной проблемы. Спасибо!
Cancebero
10
Спасибо за это. Я также хотел сопоставить одинарные кавычки, поэтому в итоге я адаптировал его к этому:/(["'])(?:[^\1\\]|\\.)*?\1/
leo
При var s = ' my \\"new\\" string and \"this should be matched\"';этом такой подход приведет к неожиданным результатам.
Wiktor Stribiew
32

Это происходит из nanorc.sample, доступного во многих дистрибутивах Linux. Используется для подсветки синтаксиса строк в стиле C

\"(\\.|[^\"])*\"

источник
При var s = ' my \\"new\\" string and \"this should be matched\"';этом такой подход приведет к неожиданным результатам.
Wiktor Stribiew
1
c.nanorc был первым местом, куда я пошел. Не удалось заставить его работать как часть строкового литерала C до тех пор, пока он дважды не экранировал все, как это" \"(\\\\.|[^\\\"])*\" "
Hellork
Это работает с функциями egrep и re_comp / re_exec из libc.
fk0
19

Как сообщает ePharaoh, ответ:

/"([^"\\]*(\\.[^"\\]*)*)"/

Чтобы вышеуказанное применялось к строкам в одинарных или двойных кавычках, используйте

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/
Гай Бедфорд
источник
2
Это единственный набор, который у меня работал с одной большой строкой в ​​кавычках размером 1,5 КБ, содержащей 99 экранирований. Все остальные выражения на этой странице в моем текстовом редакторе ломались с ошибкой переполнения. Хотя большинство из них работает в браузере, о чем следует помнить. Fiddle: jsfiddle.net/aow20y0L
Beejor,
3
См. Ответ @ MarcAndrePoulin ниже для объяснения.
shaunc
10

В большинстве представленных здесь решений используются альтернативные пути повторения, например (A | B) *.

Вы можете столкнуться с переполнением стека при больших входных данных, поскольку какой-то компилятор шаблонов реализует это с помощью рекурсии.

Например, Java: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

Что-то вроде этого: "(?:[^"\\]*(?:\\.)?)*"или тот, который предоставил Гай Бедфорд, уменьшит количество шагов синтаксического анализа, избегая большинства переполнений стека.

Марк-Андре Пулен
источник
9
"(?:\\"|.)*?"

Чередование \"и .пропускает экранированные кавычки, в то время как ленивый квантификатор *?гарантирует, что вы не перейдете за конец строки в кавычках. Работает с классами .NET Framework RE

Тош Афанасьев
источник
Но терпит неудачу"\\"
Ян
/"(?:(?:\\"|[^"])*)"/gэто должно исправить
Дэйв
7
/"(?:[^"\\]++|\\.)*+"/

Взято прямо из man perlreсистемы Linux с установленным Perl 5.22.0. В качестве оптимизации это регулярное выражение использует «потенциальную» форму обоих +и *для предотвращения обратного отслеживания, поскольку заранее известно, что строка без закрывающей кавычки не будет совпадать в любом случае.

извед
источник
4
/(["\']).*?(?<!\\)(\\\\)*\1/is

должен работать с любой строкой в ​​кавычках


источник
1
Красиво, но слишком гибко для запроса (будет соответствовать одинарным кавычкам ...). И может быть упрощен до /".*?(?<!\)"/, если я что-то не пропущу. Да, и некоторые языки (например, JavaScript), увы, не понимают выражения отрицательного просмотра назад.
PhiLho
1
@PhiLho, простое использование одного (? <! \\) приведет к сбою при экранировании обратной косой черты в конце строки. Однако верно в отношении ретроспективного анализа в JavaScript.
Маркус Джардерот 01
4

Он отлично работает на PCRE и не подходит для StackOverflow.

"(.*?[^\\])??((\\\\)+)?+"

Объяснение:

  1. Каждая строка в кавычках начинается с Char: ";
  2. Он может содержать любое количество любых символов: .*?{Ленивое совпадение}; оканчивается не escape-символом [^\\];
  3. Оператор (2) является ленивым (!) Необязательным, поскольку строка может быть пустой (""). Так:(.*?[^\\])??
  4. Наконец, каждая строка в кавычках заканчивается Char ( "), но ей может предшествовать четное количество пар знаков escape (\\\\)+; Жадный (!) необязательный: ((\\\\)+)?+{Жадное сопоставление}, так как строка может быть пустой или без конечных пар!
Вадим Сайфи
источник
Это не самый эффективный паттерн в мире, но идея интересная. Обратите внимание, что вы можете сократить его так:"(.*?[^\\])?(\\\\)*"
Casimir et Hippolyte
2

вот тот, который работает с обоими "и", и вы легко добавляете другие в начале.

( "|") (: \\\ 1 | [^ \ 1])? * \ 1

он использует обратную ссылку (\ 1) в точном соответствии с тем, что находится в первой группе ("или").

http://www.regular-expressions.info/backref.html

Матиас Хансен
источник
это очень хорошее решение, но его [^\1]следует заменить на, .потому что не существует такой вещи, как анти-обратная ссылка, и это в любом случае не имеет значения. первое условие всегда будет соответствовать, прежде чем что-нибудь плохое может случиться.
Seph Reed
@SephReed - замена [^\1]с .бы эффективно изменить это регулярное выражение для ("|').*?\1а затем он будет соответствовать "foo\"в "foo \" bar". Тем не менее, приступить [^\1]к работе тяжело. @ Mathiashansen - Вам лучше использовать громоздкие и дорогие (?!\1).(так что все регулярное выражение с некоторой очисткой эффективности будет (["'])(?:\\.|(?!\1).)*+\1. +Необязательно, если ваш движок не поддерживает его.
Адам Кац,
2

Вариант, который ранее не затрагивался:

  1. Переверните строку.
  2. Выполните сопоставление перевернутой строки.
  3. Переверните совпавшие струны.

Это дает дополнительный бонус в виде возможности правильно сопоставить закрытые открытые теги.

Допустим, у вас есть следующая строка; String \"this "should" NOT match\" and "this \"should\" match" Здесь \"this "should" NOT match\"не должно совпадать, а "should"должно быть. Вдобавок this \"should\" matchдолжно совпадать, а \"should\"не должно.

Сначала пример.

// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';

// The RegExp.
const regExp = new RegExp(
    // Match close
    '([\'"])(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))' +
    '((?:' +
        // Match escaped close quote
        '(?:\\1(?=(?:[\\\\]{2})*[\\\\](?![\\\\])))|' +
        // Match everything thats not the close quote
        '(?:(?!\\1).)' +
    '){0,})' +
    // Match open
    '(\\1)(?!(?:[\\\\]{2})*[\\\\](?![\\\\]))',
    'g'
);

// Reverse the matched strings.
matches = myString
    // Reverse the string.
    .split('').reverse().join('')
    // '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'

    // Match the quoted
    .match(regExp)
    // ['"hctam "\dluohs"\ siht"', '"dluohs"']

    // Reverse the matches
    .map(x => x.split('').reverse().join(''))
    // ['"this \"should\" match"', '"should"']

    // Re order the matches
    .reverse();
    // ['"should"', '"this \"should\" match"']

Хорошо, теперь объясним, что такое RegExp. Это регулярное выражение можно легко разбить на три части. Следующим образом:

# Part 1
(['"])         # Match a closing quotation mark " or '
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)
# Part 2
((?:          # Match inside the quotes
(?:           # Match option 1:
  \1          # Match the closing quote
  (?=         # As long as it's followed by
    (?:\\\\)* # A pair of escape characters
    \\        # 
    (?![\\])  # As long as that's not followed by an escape
  )           # and a single escape
)|            # OR
(?:           # Match option 2:
  (?!\1).     # Any character that isn't the closing quote
)
)*)           # Match the group 0 or more times
# Part 3
(\1)           # Match an open quotation mark that is the same as the closing one
(?!            # As long as it's not followed by
  (?:[\\]{2})* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)

Вероятно, это намного яснее в форме изображения: сгенерировано с помощью Jex Regulex

Изображение на github (Визуализатор регулярных выражений JavaScript). Извините, у меня недостаточно высокая репутация, чтобы включать изображения, поэтому пока это просто ссылка.

Вот суть примера функции, использующей эту более продвинутую концепцию: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

scagood
источник
0

Следует помнить, что регулярные выражения - не панацея для всего строкового. Некоторые вещи проще сделать с помощью курсора и линейного ручного поиска. CFL будет делать трюк довольно тривиально, но не так много реализаций CFL (AFAIK).

Хенрик Пол
источник
3
Это верно, но эта проблема вполне доступна для регулярных выражений, и существует множество их реализаций.
Алан Мур,
0

Более обширная версия https://stackoverflow.com/a/10786066/1794894

/"([^"\\]{50,}(\\.[^"\\]*)*)"|\'[^\'\\]{50,}(\\.[^\'\\]*)*\'|“[^”\\]{50,}(\\.[^“\\]*)*”/   

Эта версия также содержит

  1. Минимальная длина цитаты 50
  2. Дополнительный тип котировок (открытые и закрытые )
Rvanlaak
источник
0

Путался в regexpal и закончил с этим регулярным выражением: (Не спрашивайте меня, как это работает, я почти не понимаю, даже хотя я написал это lol)

"(([^"\\]?(\\\\)?)|(\\")+)+"
Петтер Тоусен
источник
0

Если искать с самого начала, может это сработает?

\"((\\\")|[^\\])*\"
user2267983
источник
0

Я столкнулся с аналогичной проблемой при попытке удалить строки в кавычках, которые могут помешать синтаксическому анализу некоторых файлов.

В итоге я получил двухэтапное решение, которое превосходит любое запутанное регулярное выражение, которое вы можете придумать:

 line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
 line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful

Легче читать и, вероятно, более эффективно.

マ ル ち ゃ ん だ よ
источник
0

Если ваша IDE - это IntelliJ Idea, вы можете забыть обо всех этих головных болях и сохранить свое регулярное выражение в строковой переменной, и когда вы скопируете его в двойные кавычки, оно автоматически изменится на приемлемый формат регулярного выражения.

пример на Java:

String s = "\"en_usa\":[^\\,\\}]+";

теперь вы можете использовать эту переменную в своем регулярном выражении или где угодно.

Арамис СМП
источник