Regex за съвпадение на всички случаи, които не са в кавички

От този въпрос, Заключих, че съвпадението на всички екземпляри на даден регулярен израз не в кавички е невъзможно. Това означава, че не може да съответства на екранирани кавички (напр.: "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
Хей, има ли някакъв начин регулярният израз да работи с метода .split() на JavaScript? Изглежда, че игнорира глобалния флаг... - person Azmisov; 25.06.2011
comment
Няма значение, просто забравих да сложа ?: във всички скоби: \+(?=(?:[^"\\]*(?:\\.|"(?:[^"\\]*\\.)*[^"\\]*"))*[^"]*$) - person Azmisov; 25.06.2011
comment
+1, това е отлично. какви са кавичките може да бъде в двойни или единични кавички''. примерен вход +bar+baz'not+or\'+or+\this+foo+bar+. Можете също така да добавите някакво обяснение към стъпките на регулярния израз. - person Scorpion; 30.11.2012
comment
Опитах да използвам това в проект и не успя. Открих, че причината е, ако имате една двойна кавичка в две единични кавички '"'... Това ще доведе до броя на двойните кавички в низа да бъде 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

Можете да използвате същия принцип за съпоставяне или разделяне. Вижте въпроса и статията в препратката, която също ще ви посочи примерни кодове.

Надяваме се, че това ви дава различна представа за много общ начин да направите това. :)

Ами празните низове?

Горното е общ отговор за демонстриране на техниката. Може да се променя в зависимост от вашите точни нужди. Ако се притеснявате, че текстът ви може да съдържа празни низове, просто променете квантора вътре в израза за улавяне на низ от + на *:

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

Вижте демонстрация.

Какво ще кажете за Escaped Quotes?

Отново, горното е общ отговор за демонстриране на техниката. Не само, че регулярният израз „игнорирайте това съвпадение“ може да бъде прецизиран според вашите нужди, можете да добавите множество изрази, които да игнорирате. Например, ако искате да сте сигурни, че екранираните кавички са адекватно игнорирани, можете да започнете с добавяне на редуване \\"| пред другите две, за да съпоставите (и игнорирате) екранираните двойни кавички.

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

Полученият израз има три клона:

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

Обърнете внимание, че в други разновидности на регулярен израз бихме могли да свършим тази работа по-лесно с lookbehind, но JS не го поддържа.

Пълният регулярен израз става:

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

Вижте демонстрация на regex и пълният скрипт.

Справка

  1. Как да съпоставим шаблон освен в ситуации s1, s2, s3
  2. Как да съпоставите модел, освен ако...
person zx81    schedule 15.05.2014
comment
Този подход всъщност е по-добър от начина за гледане напред, предложен от @Jens. Пише се по-лесно и има много по-добра производителност. Не забелязах и използвах начина за гледане напред, докато не срещнах проблем с производителността, че за да съответства на 1,5M текст, начинът за гледане напред използваше около 90 секунди, докато този подход се нуждаеше само от 600 ms. - 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