Regex для рядка з цитатами, що процитуються


120

Як отримати підрядок " It's big \"problem "за допомогою регулярного виразу?

s = ' function(){  return " It\'s big \"problem  ";  }';     

1
Як ви знаходите "It" у рядку, який містить лише "Is"? Я б вирішив це для вас, але я не знаю, які конвенції про єдину цитату / скасування застосовуються до мови, якою ви користуєтесь.
Джонатан Леффлер


2
Власне, дивлячись на дати, я бачу, що інше питання є дублікатом цього. У будь-якому випадку обов'язково перевіряйте мою відповідь .
егергернер

@ridgerunner: Я голосую, щоб закрити це, як ви запропонували. Правда, інше питання є останнім часом, але воно також набагато краще (в основному завдяки вашій відповіді).
Алан Мур

Відповіді:


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

Працює в тренері Regex та Workstach PCRE.

Приклад тесту в JavaScript:

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


23
Має сенс. Звичайна англійська: дві лапки, що оточують нуль або більше "будь-якого символу, який не є цитатою чи зворотною косою рисою" або "зворотна косою рисою, яку супроводжує будь-який символ" Я не можу повірити, що не думав цього робити ...
Ajedi32

7
Я відповім сам. =) (?:...)- це пасивна або не захоплююча група. Це означає, що пізніше його не можна буде повернути.
magras

після багато пошуків і тестування багато - це справжнє і єдине рішення, яке я знайшов для цієї поширеної проблеми. Дякую!
онкоберез

9
спасибі за це. Мені хотілося також узгодити одинарні лапки, тому я в кінцевому підсумку адаптував це до цього:/(["'])(?:[^\1\\]|\\.)*?\1/
Лео

З var s = ' my \\"new\\" string and \"this should be matched\"';, такий підхід призведе до несподіваних результатів.
Wiktor Stribiżew

32

Цей похід походить від nanorc.sample, доступного у багатьох дистрибутивах Linux. Він використовується для підсвічування синтаксису рядків у стилі C

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

З var s = ' my \\"new\\" string and \"this should be matched\"';, такий підхід призведе до несподіваних результатів.
Wiktor Stribiżew

1
c.nanorc був першим місцем, куди я пішов. Не вдалося змусити його працювати як частину літерального рядка C, поки не подвійно уникати всього подібного" \"(\\\\.|[^\\\"])*\" "
hellork

Це працює з функціями egrep та re_comp / re_exec від libc.
fk0

19

Як надає ePharaoh, відповідь така

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

Щоб вищезазначене було застосовано або до рядків з цитатами, або з цитатами з подвійним цитуванням, використовуйте

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

2
Це єдиний набір, який працював на мене з однією великою рядком, котируваним 1,5 Кб, що містить 99 біг. Кожен інший вираз на цій сторінці пробився в моєму текстовому редакторі з помилкою переповнення. Хоча більшість тут працює у браузері, просто щось пам’ятати. Fiddle
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.
Маркус Джардеро

4

Цей ідеально працює на PCRE і не підпадає під StackOverflow.

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

Пояснення:

  1. Кожен рядок в лапках починається з Char: ";
  2. Він може містити будь-яку кількість будь-яких символів: .*?{Lazy match}; закінчується символом без втечі [^\\];
  3. Заява (2) є Lazy (!) Необов’язковою, оскільки рядок може бути порожнім (""). Так:(.*?[^\\])??
  4. Нарешті, кожна цитується рядок закінчується Char ( "), але їй може передувати парна кількість знаків втечі (\\\\)+; і це жадібний (!) необов'язковий: ((\\\\)+)?+{Жадібний збіг}, тому що рядок може бути порожнім або без закінчення пар!

Це не найефективніший зразок світу, але ідея цікава. Зауважте, що ви можете скоротити його так:"(.*?[^\\])?(\\\\)*"
Казимир та Іполит

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

Зображення на github (JavaScript Regular Expression Visualizer.) Вибачте, я не маю достатньо високої репутації, щоб включати зображення, тож це лише посилання на даний момент.

Ось суть прикладу функції, що використовує цю концепцію, яка є дещо досконалішою: https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js


0

Треба пам’ятати, що регулярні виразки - це не срібна куля для всього рядка. Деякі речі простіше зробити курсором і лінійними, ручними, шукатими. CFL буде робити трюк досить тривіально, але не так багато реалізацій CFL (AFAIK).


3
Щоправда, але ця проблема цілком відповідає можливостям регулярних виразів, і їх реалізація дуже багато.
Алан Мур

0

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

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

Ця версія також містить

  1. Мінімальна довжина котирування 50
  2. Додатковий тип пропозицій (відкрити та закрити )



0

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

Я закінчився двоступеневим рішенням, яке перемагає будь-який складний регулярний вираз, який ви можете придумати:

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

Легше читати і, мабуть, більш ефективно.


0

Якщо ваш IDE - це IntelliJ Idea, ви можете забути всі ці головні болі і зберегти ваш регулярний вираз у змінній String, і при копіюванні та вставці його всередині подвійної лапки він автоматично зміниться у прийнятний для регулярного виведення формат.

приклад на Java:

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

тепер ви можете використовувати цю змінну у своєму регулярному виразі або де завгодно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.