Pergunta

Eu não entendo esse código de rubi:

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

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

Até agora, tudo o esperado. Mas se procurarmos por 1 com /\\/, e substitua por 2, codificado por '\\\\', por que entendemos isso:

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

E então, quando codificamos 3 com '\\\\\\', só temos 2:

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

Alguém capaz de entender por que uma barra de barriga é engolida na sequência de substituição? Isso acontece em 1.8 e 1.9.

Foi útil?

Solução

Isso é um problema porque a barra de barriga () serve como um caractere de fuga para regexps e strings. Você pode usar a variável especial & para reduzir as barras -barras de números na corda de substituição do GSUB.

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

EDIT: Devo mencionar que o valor de & é de uma correspondência regexp, neste caso uma única barra de barra.

Além disso, pensei que havia uma maneira especial de criar uma string que desativou o personagem Escape, mas aparentemente não. Nada disso produzirá duas barras:

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

Outras dicas

Resposta rápida

Se você quiser evitar toda essa confusão, Use a sintaxe do bloco muito menos confuso. Aqui está um exemplo que substitui cada barragem por 2 barras de barriga:

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

Detalhes horríveis

O problema é que, ao usar sub (e gsub), sem um bloco, Ruby interpreta Sequências especiais de personagens no parâmetro de substituição. Infelizmente, sub usa a barra de barriga como personagem de fuga para estes:

\& (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)

Como qualquer fuga, isso cria um problema óbvio. Se você deseja incluir o valor literal de uma das seqüências acima (por exemplo \1) Na sequência de saída, você precisa escapar. Então, para conseguir Hello \1, você precisa que a corda de substituição seja Hello \\1. E para representar isso como uma corda literal em Ruby, você deve escapar daquelas barras de barriga novamente como esta: "Hello \\\\1"

Então, há Dois passes de fuga diferentes. O primeiro pega a string literal e cria o valor interno da string. O segundo leva esse valor interno da string e substitui as seqüências acima pelos dados correspondentes.

Se uma barra de barriga não for seguida por um personagem que corresponda a uma das seqüências acima, a barra de barragem (e o caractere a seguir) passará por inalterado. Isso também afeta uma barra de barriga no final da string - ela passará por inalterada. É mais fácil ver essa lógica no código Rubinius; basta procurar o to_sub_replacement Método no Classe de string.

Aqui estão alguns exemplos de como String#sub está analisando a sequência de substituição:

  • 1 barra de barriga \ (que tem uma corda literal de "\\")

    Passa por inalterado porque a barra de barriga está no final da corda e não tem caracteres depois dela.

    Resultado: \

  • 2 barras de barriga \\ (que têm uma corda literal de "\\\\")

    O par de barras de barriga corresponde à sequência de barra de barriga escape (veja \\ acima) e é convertido em uma única barra de barriga.

    Resultado: \

  • 3 barras de barriga \\\ (que têm uma corda literal de "\\\\\\")

    As duas primeiras barras de barriga correspondem ao \\ sequência e seja convertido em uma única barra de barriga. Em seguida, a barragem final está no final da corda, por isso passa por inalterado.

    Resultado: \\

  • 4 barras de barriga \\\\ (que têm uma corda literal de "\\\\\\\\")

    Dois pares de barras de barriga correspondem ao \\ sequência e seja convertido em uma única barra de barriga.

    Resultado: \\

  • 2 barras de barriga com caráter no meio \a\ (que têm uma corda literal de "\\a\\")

    o \a Não corresponde a nenhuma das seqüências de fuga, por isso é permitido passar por inalterado. A barra de barriga também é permitida.

    Resultado: \a\

    Observação: O mesmo resultado pode ser obtido de: \\a\\ (com a corda literal: "\\\\a\\\\")

Em retrospectiva, isso poderia ter sido menos confuso se String#sub tinha usado um personagem de fuga diferente. Depois, não haveria a necessidade de escapar duas vezes todas as barras.

Argh, logo depois que eu digitei tudo isso, percebi que \ é usado para se referir a grupos na sequência de substituição. Eu acho que isso significa que você precisa de um literal \\ na corda de substituição para substituir uma \. Para conseguir um literal \\ você precisa de quatro \S, para substituir um por dois, você realmente precisa de oito (!).

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

alguma coisa que estou perdendo? Algumas maneiras mais eficientes?

Limpando um pouco de confusão na segunda linha de código do autor.

Você disse:

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

2 barras de barriga não estão sendo substituídas aqui. Você está substituindo 1 escapei barragem com dois A's ('aa'). Isto é, se você usou .sub(/\\/, 'a'), você só veria um 'a'

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

O livro da picareta menciona esse problema exato, na verdade. Aqui está outra alternativa (da página 130 da última edição)

str = 'a\b\c'               # => "a\b\c"
str.gsub(/\\/) { '\\\\' }   # => "a\\b\\c"
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top