Frage

Ich verstehe diesen Rubincode nicht:

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

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

Bisher alle wie erwartet. Aber wenn wir nach 1 mit suchen /\\/, und ersetzen durch 2, codiert von durch '\\\\', warum bekommen wir das:

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

und dann, wenn wir 3 mit codieren '\\\\\\', Wir bekommen nur 2:

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

Jemand in der Lage zu verstehen, warum ein Backslash in der Ersatzzeichenfolge verschluckt wird? Dies geschieht auf 1.8 und 1.9.

War es hilfreich?

Lösung

Dies ist ein Problem, da Backslash () als Fluchtcharakter für Regexps und Zeichenfolgen dient. Sie können die Special Variable verwenden und die Anzahl der Backslashes in der GSUB -Ersatzzeichenfolge reduzieren.

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

Bearbeiten: Ich sollte erwähnen, dass der Wert von & stammt aus einer Regexp -Übereinstimmung, in diesem Fall ein einzelner Backslash.

Außerdem dachte ich, dass es eine besondere Möglichkeit gab, eine Zeichenfolge zu erstellen, die den Escape -Charakter deaktivierte, aber anscheinend nicht. Keines davon wird zwei Schrägstriche erzeugen:

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

Andere Tipps

Schnelle Antwort

Wenn Sie all diese Verwirrung umgehen möchten, Verwenden Sie die viel weniger verwirrende Blocksyntax. Hier ist ein Beispiel, das jeden Backslash durch 2 Backslashes ersetzt:

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

Grausame Details

Das Problem ist, dass bei der Verwendung sub (und gsub), ohne Block, interpretiert Ruby Spezielle Zeichensequenzen im Ersatzparameter. Leider, sub Verwendet den Backslash als Fluchtcharakter für diese:

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

Wie jede Flucht schafft dies ein offensichtliches Problem. Wenn Sie möchten, dass Sie den wörtlichen Wert einer der oben genannten Sequenzen einschließen (z. B. \1) In der Ausgangszeichenfolge müssen Sie ihm entkommen. Also, um zu bekommen Hello \1, Sie müssen die Ersatzzeichenfolge sein Hello \\1. Und um dies als Streicher in Ruby darzustellen, müssen Sie diesen Backslashes wieder so entkommen: "Hello \\\\1"

Also gibt es Zwei verschiedene Fluchtpässe. Der erste nimmt die Zeichenfolge wörtlich und erstellt den internen Zeichenfolgenwert. Der zweite nimmt diesen internen Zeichenfolgenwert ein und ersetzt die obigen Sequenzen durch die übereinstimmenden Daten.

Wenn ein Backslash nicht von einem Charakter folgt, der einer der oben genannten Sequenzen entspricht, wird der Backslash (und den folgenden Charakter) unverändert durchlaufen. Dies wirkt sich auch auf einen Backslash am Ende der Saite aus - sie wird unverändert durchlaufen. Es ist am einfachsten, diese Logik im Rubinius -Code zu sehen. Suchen Sie einfach nach dem to_sub_replacement Methode in der Stringklasse.

Hier sind einige Beispiele wie String#sub Parsen die Ersatzzeichenfolge:

  • 1 Backslash \ (was eine Saitenbuchstätte von hat "\\")

    Pässt unverändert, weil der Backslash am Ende der Saite liegt und danach keine Zeichen hat.

    Ergebnis: \

  • 2 Backslashes \\ . "\\\\")

    Das Paar Backslashes entspricht der entkommenen Backslash -Sequenz (siehe \\ oben) und wird in einen einzelnen Backslash konvertiert.

    Ergebnis: \

  • 3 Backslashes \\\ . "\\\\\\")

    Die ersten beiden Backslashes entsprechen den \\ Sequenz und werden in einen einzelnen Backslash konvertiert. Dann befindet sich der letzte Backslash am Ende der Saite, so dass er unverändert durchläuft.

    Ergebnis: \\

  • 4 Backslashes \\\\ . "\\\\\\\\")

    Jeweils zwei Paare von Backslashes entsprechen den \\ Sequenz und werden in einen einzelnen Backslash konvertiert.

    Ergebnis: \\

  • 2 Backslashes mit Charakter in der Mitte \a\ . "\\a\\")

    Das \a Stimmt nicht zu einer der Escape -Sequenzen überein, sodass sie unverändert durchlaufen darf. Der nachverfolgende Backslash ist ebenfalls erlaubt.

    Ergebnis: \a\

    Notiz: Das gleiche Ergebnis konnte erzielt werden: \\a\\ (mit der wörtlichen Schnur: "\\\\a\\\\")

Im Nachhinein hätte dies weniger verwirrend sein können, wenn String#sub hatte einen anderen Fluchtcharakter verwendet. Dann würde es nicht die Notwendigkeit geben, allen Backslashes doppelt zu entkommen.

Argh, gleich nachdem ich das alles ausgegeben hatte, wurde mir klar \ wird verwendet, um auf Gruppen in der Ersatzzeichenfolge zu verweisen. Ich denke, das bedeutet, dass Sie ein wörtliches brauchen \\ in der Ersatzzeichenfolge, um einen zu ersetzen \. Einen buchstäblichen bekommen \\ Sie brauchen vier \s, also, um eins durch zwei zu ersetzen, brauchen Sie tatsächlich acht (!).

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

Fehlt mir etwas? effizientere Wege?

Ein wenig Verwirrung in der zweiten Codezeile des Autors.

Du sagtest:

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

2 Backslashes werden hier nicht ersetzt. Sie ersetzen 1 entkommen Backslash mit zwei A ('AA'). Das heißt, wenn Sie benutzt haben .sub(/\\/, 'a'), Sie würden nur einen "a" sehen

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

Das Spickaxe -Buch erwähnt eigentlich genau dieses Problem. Hier ist eine weitere Alternative (aus Seite 130 der neuesten Ausgabe)

str = 'a\b\c'               # => "a\b\c"
str.gsub(/\\/) { '\\\\' }   # => "a\\b\\c"
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top