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

Из этого вопроса я сделал вывод, что сопоставить все экземпляры данного регулярного выражения, не входящие в кавычки, невозможно. То есть он не может соответствовать экранированным кавычкам (например: "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#

4 ответа

Решение

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

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

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

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

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

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

Азмисов, воскрешая этот вопрос, потому что вы сказали, что искали 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);

Онлайн демо

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

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

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

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

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

Смотрите демо.

А как насчет побег цитаты?

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

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

Полученное выражение имеет три ветви:

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

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

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

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

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

Ссылка

  1. Как сопоставить шаблон с исключением ситуаций s1, s2, s3
  2. Как соответствовать шаблону, если...

Вы можете сделать это в три этапа.

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

Код ниже

// 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"}'

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

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

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

а также

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

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

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

Другие вопросы по тегам