Обратная косая черта + захваченная группа в регулярном выражении Ruby - PullRequest
6 голосов
/ 22 апреля 2011

Как мне восстановить обратную косую черту перед захваченной группой?

Пример:

"foo+bar".gsub(/(\+)/, '\\\1')

Что я ожидаю (и хочу):

foo\+bar

что як сожалению получаю:

foo\\1bar

Как мне тут правильно сбежать?

Ответы [ 3 ]

7 голосов
/ 22 апреля 2011

Как уже говорили другие, вам нужно дважды избегать всего в этой строке. Таким образом, в вашем случае решение заключается в использовании '\\\\\1' или '\\\\\\1'. Но так как вы спросили почему, я постараюсь объяснить эту часть.

Причина в том, что последовательность замены анализируется дважды - один раз Ruby и один раз базовым механизмом регулярных выражений, для которого \1 является собственной escape-последовательностью. (Вероятно, это легче понять с помощью строк в двойных кавычках, поскольку одинарные кавычки вводят неоднозначность, в которой '\\1' и '\1' эквивалентны, а '\' и '\\' - нет.)

Так, например, простая замена здесь захваченной группой и строкой в ​​двойных кавычках будет:

"foo+bar".gsub(/(\+)/, "\\1")   #=> "foo+bar"

Это передает строку \1 в механизм регулярных выражений, который он понимает как ссылку на группу захвата. В строковых литералах Ruby "\1" означает что-то совсем другое (символ ASCII 1).

В данном случае мы действительно хотим, чтобы механизм регулярных выражений получал \\\1. Он также понимает \ как escape-символ, поэтому \\1 недостаточно и будет просто преобразован в буквальный вывод \1. Итак, нам нужно \\\1 в движке регулярных выражений, но чтобы добраться до этой точки, нам нужно также пройти мимо строкового литерального анализатора Ruby.

Чтобы сделать это, мы берем желаемый ввод регулярных выражений и снова удваиваем каждый обратный слеш, чтобы пройти через синтаксический анализатор строковых литералов Ruby. \\\1 поэтому требуется "\\\\\\1". В случае одинарных кавычек одна косая черта может быть опущена, поскольку \1 не является допустимой escape-последовательностью в одинарных кавычках и трактуется буквально.

Добавление

Одна из причин, по которой эта проблема обычно скрывается, заключается в использовании кавычек регулярного выражения в стиле /.+/, которые Ruby обрабатывает особым образом, чтобы избежать необходимости двойного экранирования всего. (Конечно, это не относится к gsub замещающим строкам.) Но вы все равно можете увидеть это в действии, если вы используете строковый литерал вместо литерала регулярного выражения в Regexp.new:

Regexp.new("\.").match("a")   #=> #<MatchData "a">
Regexp.new("\\.").match("a")  #=> nil

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

4 голосов
/ 22 апреля 2011

Это происходит из-за выхода двойной строки.В этом случае вы должны использовать 5 слешей.

"foo+bar".gsub(/([+])/, '\\\\\1')
2 голосов
/ 22 апреля 2011

Добавление \ еще два раза ускользает от этого должным образом.

irb(main):011:0> puts "foo+bar".gsub(/(\+)/, '\\\\\1')
foo\+bar
=> nil
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...