Альтернатива регулярному выражению: сопоставить все экземпляры не в кавычках
Из этого вопроса я сделал вывод, что сопоставить все экземпляры данного регулярного выражения, не входящие в кавычки, невозможно. То есть он не может соответствовать экранированным кавычкам (например: "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);
Вы можете использовать тот же принцип для сопоставления или разделения. Смотрите вопрос и статью в ссылке, которая также укажет вам примеры кода.
Надеюсь, что это дает вам другое представление об очень общем способе сделать это.:)
А как насчет пустых строк?
Выше приведен общий ответ для демонстрации техники. Его можно настроить в зависимости от ваших потребностей. Если вы беспокоитесь, что ваш текст может содержать пустые строки, просто измените квантификатор внутри выражения захвата строк с +
в *
:
"[^"]*"|(\+)
Смотрите демо.
А как насчет побег цитаты?
Опять же, выше, это общий ответ, чтобы продемонстрировать технику. Вы можете не только откорректировать регулярное выражение "игнорировать это соответствие", но и добавить несколько выражений, которые нужно игнорировать. Например, если вы хотите убедиться, что экранированные кавычки должным образом игнорируются, вы можете начать с добавления альтернативы. \\"|
перед двумя другими для того, чтобы соответствовать (и игнорировать) разбегающиеся двойные кавычки.
Далее в разделе "[^"]*"
который захватывает содержимое строк в двойных кавычках, вы можете добавить чередование, чтобы убедиться, что экранированные двойные кавычки сопоставляются до их "
имеет шанс превратиться в заключительного стража, превратив его в "(?:\\"|[^"])*"
Полученное выражение имеет три ветви:
\\"
соответствовать и игнорировать"(?:\\"|[^"])*"
соответствовать и игнорировать(\+)
соответствовать, захватывать и обрабатывать
Обратите внимание, что в других разновидностях регулярных выражений мы могли бы легче выполнять эту работу с lookbehind, но JS не поддерживает ее.
Полное регулярное выражение становится:
\\"|"(?:\\"|[^"])*"|(\+)
Смотрите демо-версию регулярного выражения и полный скрипт.
Ссылка
Вы можете сделать это в три этапа.
- Используйте regex global replace для извлечения всего содержимого тела строки в боковую таблицу.
- Сделайте перевод запятой
- Используйте 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. кажется важным, чтобы на самом деле учесть все, что было задано в первоначальном вопросе.
Следует отметить, что этот метод неявно требует, чтобы строка не экранировала кавычки вне пар неэкранированных кавычек.