Почему Ruby gsub не заменяет второе вхождение этого шаблона?

#ruby #regex #gsub #ruby-1.8.7

#рубиновый #регулярное выражение #gsub #рубин-1.8.7 #ruby #ruby-1.8.7

Вопрос:

У меня есть немного кода для экранирования двойных кавычек из строки, которая может включать предварительно экранированные кавычки; например:

 This is a "string"
  

Используя следующий код с Ruby 1.8.7p374:

 string.gsub!(/([^\])"/, '1"')
  

Тем не менее, я получаю забавный крайний случай, когда пытаюсь использовать его в следующей строке: ab""c => ab""c . Я бы ожидал, что он избежал обеих кавычек.

Это определенно не большая проблема, но мне стало любопытно.
Это ошибка с моим выражением лица? gsub Ошибка / особенность?

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

Комментарии:

1. Проблема заключается в перекрывающихся выражениях, ([^\]) они не будут соответствовать " только что замененным вами.

2. Непонятно, что у вас есть. Если " в конце находится символ двойной кавычки, то это будет описано как " . Но тогда, " перед этим, это означало бы, что у вас есть символ обратной косой черты, за которым следует символ двойной кавычки, который будет описан \" как экранированный. Это действительно то, что у вас есть?

Ответ №1:

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

Вы правы, что предварительное утверждение исправит это. Но без этого у вас есть несколько вариантов в Ruby 1.8.7.

  1. Повторяйте, пока не будет сделано никаких замен ( gsub! возвращает nil , если совпадений не было):

    loop { break unless string.gsub!(/([^\])"/, '1"') }

  2. Для 1.8.7 у вас нет предварительных утверждений. Но вы можете изменить строку, использовать предварительные утверждения для внесения изменений, а затем изменить ее обратно:

    string = string.reverse.gsub(/"(?!\)/, '"\').reverse

Ответ №2:

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

После gsub сопоставления b" и замены оно продолжается с последнего совпадения, просматривая следующее " , но не просматривая ранее использованные символы.

Возможно, вы сможете решить вашу проблему с помощью lookbehind в более новых версиях Ruby, но это не решит проблему с началом строки. Способ исправить это — использовать G привязку (которая доступна в Ruby 1.8.7), которая соответствует тому месту, где закончилось предыдущее совпадение или в начале строки. Итак, вы ищете a " , который либо сразу после косой черты, либо находится в начале текущего совпадения (это означает, что a " только что было сопоставлено, или это начало строки). Что-то вроде этого:

 string.gsub!(/([^\]|G)"/, '1"')
  

Это преобразует строку "ab""c в "ab""c .