Domanda

Non capisco questo codice Ruby:

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

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

finora tutto come previsto.ma se cerchiamo 1 con /\\/, e sostituire con 2, codificato da '\\\\', perché otteniamo questo:

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

e poi, quando codifichiamo 3 con '\\\\\\', ne otteniamo solo 2:

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

qualcuno è in grado di capire perché una barra rovesciata viene inghiottita nella stringa di sostituzione?questo accade su 1.8 e 1.9.

È stato utile?

Soluzione

Questo è un problema perché backslash (\) serve come un carattere di escape per Le espressioni regolari e archi. Si potrebbe fare utilizzare la variabile speciale \ e per ridurre le barre inverse numero nella stringa di sostituzione gsub.

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

EDIT: devo dire che il valore di \ & è da una partita Regexp, in questo caso una singola barra rovesciata.

Inoltre, ho pensato che ci fosse un modo speciale per creare una stringa che disattivato il carattere di escape, ma a quanto pare no. Nessuno di questi produrrà due barre:

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

Altri suggerimenti

Risposta rapida

Se vuoi evitare tutta questa confusione, utilizzare la sintassi del blocco molto meno confusa.Ecco un esempio che sostituisce ciascuna barra rovesciata con 2 barre rovesciate:

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

Dettagli raccapriccianti

Il problema è che durante l'utilizzo sub (E gsub), senza blocco, rubino interpreta sequenze di caratteri speciali nel parametro di sostituzione.Purtroppo, sub utilizza la barra rovesciata come carattere di escape per questi:

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

Come ogni fuga, questo crea un ovvio problema.Se vuoi includere il valore letterale di una delle sequenze sopra (ad es. \1) nella stringa di output devi eseguirne l'escape.Quindi, per ottenere Hello \1, è necessario che la stringa sostitutiva sia Hello \\1.E per rappresentarlo come una stringa letterale in Ruby, devi evitare nuovamente le barre rovesciate in questo modo: "Hello \\\\1"

Quindi ci sono due diversi passaggi di fuga.Il primo prende la stringa letterale e crea il valore della stringa interna.Il secondo prende il valore della stringa interna e sostituisce le sequenze precedenti con i dati corrispondenti.

Se una barra rovesciata non è seguita da un carattere che corrisponde a una delle sequenze precedenti, la barra rovesciata (e il carattere che segue) passerà inalterata.Ciò influisce anche sulla barra rovesciata alla fine della stringa: passerà inalterata.È più semplice vedere questa logica nel codice Rubinius;basta cercare il to_sub_replacement metodo nel Classe di stringhe.

Ecco qualche esempio di come String#sub sta analizzando la stringa di sostituzione:

  • 1 barra rovesciata \ (che ha una stringa letterale di "\\")

    Passa inalterata perché la barra rovesciata si trova alla fine della stringa e non ha caratteri dopo.

    Risultato: \

  • 2 barre rovesciate \\ (che hanno una stringa letterale di "\\\\")

    La coppia di barre rovesciate corrisponde alla sequenza di barre rovesciate con escape (vedi \\ sopra) e viene convertito in una singola barra rovesciata.

    Risultato: \

  • 3 barre rovesciate \\\ (che hanno una stringa letterale di "\\\\\\")

    Le prime due barre rovesciate corrispondono a \\ sequenza e vengono convertiti in una singola barra rovesciata.Quindi la barra rovesciata finale si trova alla fine della stringa, quindi passa inalterata.

    Risultato: \\

  • 4 barre rovesciate \\\\ (che hanno una stringa letterale di "\\\\\\\\")

    Due coppie di barre rovesciate corrispondono ciascuna a \\ sequenza e vengono convertiti in una singola barra rovesciata.

    Risultato: \\

  • 2 barre rovesciate con il carattere al centro \a\ (che hanno una stringa letterale di "\\a\\")

    IL \a non corrisponde a nessuna delle sequenze di escape, quindi può passare inalterato.È consentita anche la barra rovesciata finale.

    Risultato: \a\

    Nota: Lo stesso risultato potrebbe essere ottenuto da: \\a\\ (con la stringa letterale: "\\\\a\\\\")

Col senno di poi, questo avrebbe potuto creare meno confusione se String#sub aveva utilizzato un carattere di escape diverso.Quindi non ci sarebbe la necessità di eseguire il doppio escape di tutte le barre rovesciate.

argh, subito dopo ho digitato tutto questo, mi sono reso conto che \ è usato per riferirsi a gruppi nella stringa di sostituzione. Credo che questo significa che hai bisogno di una \\ letterale nella stringa di sostituzione per ottenere uno \ sostituito. Per ottenere un \\ letterale è necessario quattro \s, in modo da sostituire una con due hai veramente bisogno di otto (!).

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

tutto ciò che mi manca? modi più efficienti?

Cancellazione di un po 'di confusione sulla seconda linea dell'autore di codice.

Hai detto:

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

2 backslash non sono sempre sostituiti qui. Si sta sostituendo 1 sfuggito barra rovesciata con due A di ( 'AA'). Cioè, se è stato utilizzato .sub(/\\/, 'a'), si dovrebbe vedere solo una 'a'

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

il libro piccone menziona questo problema esatto, in realtà. ecco un altro alternativo (da pagina 130 dell'ultima edizione)

str = 'a\b\c'               # => "a\b\c"
str.gsub(/\\/) { '\\\\' }   # => "a\\b\\c"
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top