Вопрос

Я не понимаю этот Ruby-код:

>> puts '\\ <- single backslash'
# \ <- single backslash

>> puts '\\ <- 2x a, because 2 backslashes get replaced'.sub(/\\/, 'aa')
# aa <- 2x a, because two backslashes get replaced

пока все, как и ожидалось.но если мы будем искать 1 с помощью /\\/, и заменить на 2, закодированный '\\\\', почему мы получаем это:

>> puts '\\ <- only 1 ... replace 1 with 2'.sub(/\\/, '\\\\')
# \ <- only 1 backslash, even though we replace 1 with 2

и затем, когда мы кодируем 3 с помощью '\\\\\\', мы получаем только 2:

>> puts '\\ <- only 2 ... 1 with 3'.sub(/\\/, '\\\\\\')
# \\ <- 2 backslashes, even though we replace 1 with 3

кто-нибудь в состоянии понять, почему обратная косая черта проглатывается в заменяющей строке?это происходит на версиях 1.8 и 1.9.

Это было полезно?

Решение

Это проблема, поскольку обратная косая черта (\) служит escape-символом для регулярных выражений и строк.Вы можете использовать специальную переменную \&, чтобы уменьшить количество обратных косых черт в строке замены gsub.

foo.gsub(/\\/,'\&\&\&') #for some string foo replace each \ with \\\

РЕДАКТИРОВАТЬ:Следует отметить, что значение \& взято из совпадения с регулярным выражением, в данном случае это одна обратная косая черта.

Кроме того, я думал, что существует особый способ создания строки, отключающей escape-символ, но, видимо, нет.Ни один из них не приведет к созданию двух косых черт:

puts "\\"
puts '\\'
puts %q{\\}
puts %Q{\\}
puts """\\"""
puts '''\\'''
puts <<EOF
\\
EOF  

Другие советы

Быстрый ответ

Если вы хотите обойти всю эту путаницу, используйте гораздо менее запутанный блочный синтаксис.Вот пример, который заменяет каждую обратную косую черту на 2 обратные косые черты:

"some\\path".gsub('\\') { '\\\\' }

Ужасные Подробности

Проблема в том, что при использовании subgsub), без блока, ruby интерпретирует последовательности специальных символов в параметре замены.К сожалению, sub использует обратную косую черту в качестве экранирующего символа для этих:

\& (the entire regex)
\+ (the last group)
\` (pre-match string)
\' (post-match string)
\0 (same as \&)
\1 (first captured group)
\2 (second captured group)
\\ (a backslash)

Как и любое бегство, это создает очевидную проблему.Если вы хотите включить буквальное значение одной из приведенных выше последовательностей (например \1) в выходной строке вы должны экранировать ее.Итак, чтобы получить Hello \1, вам нужно, чтобы строка замены была Hello \\1.И чтобы представить это как строковый литерал в Ruby, вам нужно снова экранировать эти обратные косые черты следующим образом: "Hello \\\\1"

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

Если за обратной косой чертой не следует символ, соответствующий одной из приведенных выше последовательностей, то обратная косая черта (и следующий за ней символ) будут проходить без изменений.Это также влияет на обратную косую черту в конце строки - она будет проходить без изменений.Проще всего увидеть эту логику в коде рубиниуса;просто поищите to_sub_replacement метод в Класс String ( Строка).

Вот такие несколько примеров о том , как String#sub выполняется синтаксический анализ строки замены:

  • 1 обратная косая черта \ (который имеет строковый литерал типа "\\")

    Проходит без изменений, потому что обратная косая черта находится в конце строки и после нее нет символов.

    Результат: \

  • 2 обратные косые черты \\ (которые имеют строковый литерал типа "\\\\")

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

    Результат: \

  • 3 обратные косые черты \\\ (которые имеют строковый литерал типа "\\\\\\")

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

    Результат: \\

  • 4 обратные косые черты \\\\ (которые имеют строковый литерал типа "\\\\\\\\")

    Две пары обратных косых черт, каждая из которых соответствует \\ последовательность и преобразуется в одну обратную косую черту.

    Результат: \\

  • 2 обратные косые черты с символом посередине \a\ (которые имеют строковый литерал типа "\\a\\")

    Тот Самый \a не соответствует ни одной из управляющих последовательностей, поэтому ей разрешается проходить через нее без изменений.Завершающая обратная косая черта также разрешена через.

    Результат: \a\

    Примечание: Тот же результат можно было бы получить из: \\a\\ (с буквальной строкой: "\\\\a\\\\")

Оглядываясь назад, это могло бы быть менее запутанным, если бы String#sub использовал другой экранирующий символ.Тогда не было бы необходимости дважды экранировать все обратные косые черты.

ааа, сразу после того, как я все это напечатал, я понял, что \ используется для ссылки на группы в строке замены.Я думаю, это означает, что вам нужен буквальный \\ в строке замены, чтобы заменить ее \.Чтобы получить буквальный \\ тебе нужно четыре \s, поэтому для замены одного на два вам действительно понадобится восемь (!).

# Double every occurrence of \. There's eight backslashes on the right there!
>> puts '\\'.sub(/\\/, '\\\\\\\\')

что-нибудь, что мне не хватает?есть более эффективные способы?

Проясняем небольшую путаницу во второй строке кода автора.

Вы сказали:

>> puts '\\ <- 2x a, because 2 backslashes get replaced'.sub(/\\/, 'aa')
# aa <- 2x a, because two backslashes get replaced

2 обратные косые черты здесь не заменяются.Вы заменяете 1 сбежал обратная косая черта с двумя буквами «а» («аа»).То есть, если вы использовали .sub(/\\/, 'a'), вы увидите только одну букву «а»

'\\'.sub(/\\/, 'anything') #=> anything

на самом деле в книге о кирках упоминается именно эта проблема.вот еще один вариант (со стр. 130 последнего издания)

str = 'a\b\c'               # => "a\b\c"
str.gsub(/\\/) { '\\\\' }   # => "a\\b\\c"
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top