Как сопоставить что-то с регулярным выражением, которое не находится между двумя специальными символами?

У меня есть строка вроде этого:

a b c a b "a b" b a "a"

Как я могу сопоставить каждый a, который не является частью строки, ограниченной "? Я хочу сопоставить все, что здесь смело:

a bc a b "ab" b a "a"

Я хочу заменить эти соответствия (или скорее удалить их, заменив их пустой строкой), поэтому удаление цитируемых частей для сопоставления не будет работать, потому что я хочу, чтобы они оставались в строке. Я использую Ruby.

Ответ 1

Предполагая, что кавычки правильно сбалансированы и нет экранированных кавычек, тогда легко:

result = subject.gsub(/a(?=(?:[^"]*"[^"]*")*[^"]*\Z)/, '')

Это заменяет все a пустой строкой тогда и только тогда, когда существует четное число котировок перед совпадающим a.

Объяснение:

a        # Match a
(?=      # only if it followed by...
 (?:     # ...the following:
  [^"]*" #  any number of non-quotes, followed by one quote
  [^"]*" #  the same again, ensuring an even number
 )*      # any number of times (0, 2, 4 etc. quotes)
 [^"]*   # followed by only non-quotes until
 \Z      # the end of the string.
)        # End of lookahead assertion

Если вы можете избежать кавычек в кавычках (a "length: 2\""), это все еще возможно, но будет сложнее:

result = subject.gsub(/a(?=(?:(?:\\.|[^"\\])*"(?:\\.|[^"\\])*")*(?:\\.|[^"\\])*\Z)/, '')

Это по существу такое же регулярное выражение, как и выше, только подставляя (?:\\.|[^"\\]) для [^"]:

(?:     # Match either...
 \\.    # an escaped character
|       # or
 [^"\\] # any character except backslash or quote
)       # End of alternation

Ответ 2

js-coder, воскресив этот древний вопрос, потому что у него было простое решение, которое не было упомянуто. (Нашел свой вопрос, проведя некоторое исследование для заданий по поиску регулярных выражений.)

Как вы можете видеть, регулярное выражение действительно крошечное по сравнению с регулярным выражением в принятом ответе: ("[^"]*")|a

subject = 'a b c a b " a b " b a " a "'
regex = /("[^"]*")|a/
replaced = subject.gsub(regex) {|m|$1}
puts replaced

Смотрите эту живую демонстрацию

Ссылка

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

Как сопоставить шаблон, если...

Ответ 3

Полномасштабное решение регулярных выражений для любовника regex, не заботясь о производительности или читаемости кода.

В этом решении предполагается отсутствие синтаксиса экранирования (с синтаксисом escaping, a in "sbd\"a" считается как внутри строки).

псевдокод:

processedString = 
    inputString.replaceAll("\\".*?\\"","") // Remove all quoted strings
               .replaceFirst("\\".*", "") // Consider text after lonely quote as inside quote

Затем вы можете сопоставить текст, который вы хотите, в processedString. Вы можете удалить вторую замену, если вы рассматриваете текст после одиночной цитаты как внешнюю цитату.

ИЗМЕНИТЬ

В Ruby регулярное выражение в коде выше будет

/\".*?\"/

используется с gsub

и

/\".*/

используется с sub


Чтобы решить проблему замены, я не уверен, возможно ли это, но стоит попробовать:

  • Объявить счетчик
  • Используйте регулярное выражение /(\"|a)/ с функцией gsub и функцией поставки.
  • В функции, если совпадение ", затем увеличивайте счетчик и возвращайте " в качестве замены (в основном, без изменений). Если совпадение a, проверьте, равен ли счетчик: если даже укажите свою заменяющую строку; в противном случае просто поставьте все, что соответствует.