Регулярное выражение для соответствия всем экземплярам, ​​не заключенным в кавычки

Из этого вопроса, Я пришел к выводу, что сопоставление всех экземпляров данного регулярного выражения не внутри кавычек невозможно. То есть он не может соответствовать экранированным кавычкам (например: "this whole \"match\" should be taken"). Если есть способ сделать это, о котором я не знаю, это решит мою проблему.

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

В частности, мне просто нужна альтернатива для работы с методами .split () и .replace (), но если бы она могла быть более обобщенной, это было бы лучше всего.

Например:
Строка ввода:
+bar+baz"not+or\"+or+\"this+"foo+bar+
замена + на #, не внутри кавычек, вернет:
#bar#baz"not+or\"+or+\"this+"foo#bar#


person Azmisov    schedule 24.06.2011    source источник


Ответы (4)


Фактически, вы можете сопоставить все экземпляры регулярного выражения, не заключенные в кавычки, для любой строки, где каждая открывающая цитата снова закрывается. Скажем, как в приведенном выше примере, вы хотите сопоставить \+.

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

\+(?=([^"]*"[^"]*")*[^"]*$)

Теперь вы не хотели бы подсчитывать экранированные кавычки. Это становится немного сложнее. Вместо [^"]*, который переходил к следующей цитате, вам также нужно учитывать обратную косую черту и использовать [^"\\]*. После того, как вы дойдете до обратной косой черты или кавычки, вам нужно игнорировать следующий символ, если вы встретите обратную косую черту, или же перейти к следующей неэкранированной цитате. Похоже на (\\.|"([^"\\]*\\.)*[^"\\]*"). Вместе вы получите

\+(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)

Я признаю, что это немного загадки. знак равно

person Jens    schedule 24.06.2011
comment
Спасибо! Не думал, что это возможно. Я понимаю 100% теории, около 60% регулярных выражений, и я опускаюсь до 0%, когда дело доходит до написания его самостоятельно. Ну, может, на днях. - person Azmisov; 24.06.2011
comment
Эй, есть ли способ заставить регулярное выражение работать с методом JavaScript .split ()? Вроде игнорирует глобальный флаг ... - person Azmisov; 25.06.2011
comment
Да ладно, просто забыл вставить ?: во все скобки: \+(?=(?:[^"\\]*(?:\\.|"(?:[^"\\]*\\.)*[^"\\]*"))*[^"]*$) - person Azmisov; 25.06.2011
comment
+1, это отлично. кавычки могут быть как в двойных, так и в одинарных кавычках ''. пример input + bar + baz'not + or \ '+ or + \ this + foo + bar +. Также вы можете добавить некоторые пояснения к шагам регулярного выражения. - person Scorpion; 30.11.2012
comment
Пытался использовать это в проекте, и это не удалось. Я обнаружил, что причина в том, что у вас была одна двойная кавычка внутри двух одинарных кавычек _1 _... Это привело бы к тому, что количество двойных кавычек в строке было бы odd - person anson; 18.07.2013
comment
Для этого выражения одинарные кавычки не имеют особого значения. В вашем случае это не так. - person Jens; 18.07.2013
comment
В этом последнем регулярном выражении похоже, что парные скобки не совпадают. Я вижу 4 открытия и 6 закрытий. - person jcollum; 15.04.2014
comment
Пожалуйста, взгляните на решение, предложенное @ zx81 в его ответе. Это легче написать, и он имеет гораздо лучшую производительность, если его можно использовать. - person Gildor; 03.02.2015

Азмисов, воскресив этот вопрос, потому что вы сказали, что ищете any efficient alternative that could be used in JavaScript и any elegant solutions that would work in most, if not all, cases.

Так случилось, что есть простое общее решение, о котором не упоминалось.

По сравнению с альтернативами, регулярное выражение для этого решения удивительно просто:

"[^"]+"|(\+)

Идея состоит в том, что мы сопоставляем, но игнорируем все, что находится в кавычках, чтобы нейтрализовать этот контент (слева от чередования). Справа мы захватываем все +, которые не были нейтрализованы в Группу 1, а функция замены проверяет Группу 1. Вот полный рабочий код:

<script>
var subject = '+bar+baz"not+these+"foo+bar+';
var regex = /"[^"]+"|(\+)/g;
replaced = subject.replace(regex, function(m, group1) {
    if (!group1) return m;
    else return "#";
});
document.write(replaced);

Online demo

Вы можете использовать тот же принцип для сопоставления или разделения. См. Вопрос и статью в справочнике, где также будут указаны образцы кода.

Надеюсь, это даст вам другое представление об очень общем способе этого. :)

А что насчет пустых строк?

Вышеупомянутое является общим ответом на демонстрацию техники. Его можно настроить в зависимости от ваших конкретных потребностей. Если вы беспокоитесь, что ваш текст может содержать пустые строки, просто измените квантификатор внутри выражения захвата строки с + на *:

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

См. демонстрацию.

А как насчет скрытых цитат?

Опять же, приведенное выше является общим ответом для демонстрации техники. Регулярное выражение «игнорировать это совпадение» можно не только изменить в соответствии с вашими потребностями, но и добавить несколько выражений, которые следует игнорировать. Например, если вы хотите убедиться, что экранированные кавычки должным образом игнорируются, вы можете начать с добавления чередования \\"| перед двумя другими, чтобы сопоставить (и игнорировать) случайные экранированные двойные кавычки.

Затем в разделе "[^"]*", который захватывает содержимое строк в двойных кавычках, вы можете добавить чередование, чтобы гарантировать совпадение экранированных двойных кавычек до того, как их " сможет превратиться в закрывающую сигнальную метку, превратив ее в "(?:\\"|[^"])*"

Результирующее выражение имеет три ветви:

  1. \\" для соответствия и игнорирования
  2. "(?:\\"|[^"])*" для соответствия и игнорирования
  3. (\+) для сопоставления, захват и обработка

Обратите внимание, что в других вариантах регулярных выражений мы могли бы легче выполнять эту работу с помощью ретроспективного просмотра, но JS не поддерживает его.

Полное регулярное выражение становится:

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

См. демонстрацию регулярных выражений и полный сценарий.

Ссылка

  1. Как сопоставить шаблон, кроме ситуаций s1, s2, s3
  2. Как сопоставить шаблон, если ...
person zx81    schedule 15.05.2014
comment
Этот подход на самом деле лучше, чем метод прогнозирования, предложенный @Jens. Его легче писать, и он имеет гораздо лучшую производительность. Я не замечал и использовал упреждающий способ, пока не столкнулся с проблемой производительности, которая заключалась в том, что для сопоставления 1,5-мегабайтного текста упреждающий способ использовал около 90 секунд, в то время как этот подход требовал всего 600 мс. - person Gildor; 03.02.2015
comment
Ага, так лучше =) - person Jens; 04.02.2015
comment
Я обнаружил, что это сработало только при изменении 5-й строки вашего примера на if (group1 === undefined ) return m;. Стоит отметить, что я искал пробелы; не плюс знаки. - person shennan; 29.05.2015
comment
Как бы вы избежали экранированных кавычек, используя это? Возможно ли это вообще с этим шаблоном? - person Pomme.Verte; 12.03.2016
comment
Кажется, это не работает с двойными кавычками без содержимого "" и экранированными кавычками \". regex101.com/r/yR7xV5/1 - person Brian Low; 01.04.2016
comment
@BrianLow Вы правы. Ответ был призван продемонстрировать технику как можно проще. Я расширил его в ответ на ваш комментарий (см. Разделы «Как насчет пустых строк?» И «Как насчет экранированных кавычек?». - person zx81; 03.04.2016
comment
@ D.Mill Извините за задержку, см. Развернутый ответ. - person zx81; 03.04.2016
comment
Спасибо! Я сослался на ваш метод (и этот пост) в своем более конкретном решении: stackoverflow.com/a/64617472/3799617 - person justFatLard; 31.10.2020

Вы можете сделать это за три шага.

  1. Используйте глобальную замену регулярного выражения, чтобы извлечь все содержимое тела строки в дополнительную таблицу.
  2. Сделай свой перевод через запятую
  3. Используйте глобальную замену регулярного выражения, чтобы поменять местами тела строк обратно

Код ниже

// Step 1
var sideTable = [];
myString = myString.replace(
    /"(?:[^"\\]|\\.)*"/g,
    function (_) {
      var index = sideTable.length;
      sideTable[index] = _;
      return '"' + index + '"';
    });
// Step 2, replace commas with newlines
myString = myString.replace(/,/g, "\n");
// Step 3, swap the string bodies back
myString = myString.replace(/"(\d+)"/g,
    function (_, index) {
      return sideTable[index];
    });

Если вы запустите это после настройки

myString = '{:a "ab,cd, efg", :b "ab,def, egf,", :c "Conjecture"}';

ты должен получить

{:a "ab,cd, efg"
 :b "ab,def, egf,"
 :c "Conjecture"}

Это работает, потому что после шага 1

myString = '{:a "0", :b "1", :c "2"}'
sideTable = ["ab,cd, efg", "ab,def, egf,", "Conjecture"];

поэтому единственные запятые в myString находятся за пределами строк. Шаг 2, затем запятые превращаются в новые строки:

myString = '{:a "0"\n :b "1"\n :c "2"}'

Наконец, мы заменяем строки, содержащие только числа, их исходным содержанием.

person Mike Samuel    schedule 24.06.2011
comment
+1 для элегантного решения без регулярных выражений. Однако регулярное выражение немного более гибкое для того, что я делаю. - person Azmisov; 24.06.2011

Хотя ответ zx81 кажется наиболее эффективным и чистым, он нуждается в следующих исправлениях, чтобы правильно улавливать экранированные кавычки:

var subject = '+bar+baz"not+or\\"+or+\\"this+"foo+bar+';

и

var regex = /"(?:[^"\\]|\\.)*"|(\+)/g;

Также уже упомянутые «group1 === undefined» или «! Group1». Особенно 2. кажется важным принять во внимание все, что задано в исходном вопросе.

Однако следует отметить, что этот метод неявно требует, чтобы строка не имела экранированных кавычек вне пар неэкранированных кавычек.

person Marius    schedule 25.10.2015