Rails: Kodierung von Problemen mit serialisierten Hashes trotz UTF8

8

Ich habe gerade von Ruby 1.9.2 zu Ruby 1.9.3p0 (2011-10-30 Revision 33570) aktualisiert. Meine Rails-Anwendung verwendet postgresql als Datenbank-Backend. Das Systemgebietsschema ist UTF8, ebenso wie die Datenbankcodierung. Die Standardcodierung der Rails-Anwendung ist ebenfalls UTF8. Ich habe chinesische Benutzer, die chinesische Zeichen sowie englische Zeichen eingeben. Die Zeichenfolgen werden als UTF8-codierte Zeichenfolgen gespeichert.

Schienenversion: 3.0.9

Seit der Aktualisierung werden einige der vorhandenen chinesischen Zeichenfolgen in der Datenbank nicht mehr korrekt angezeigt. Dies betrifft nicht alle Zeichenfolgen, sondern nur diejenigen, die Teil eines serialisierten Hashs sind. Alle anderen Zeichenfolgen, die als einfache Zeichenfolgen gespeichert sind, scheinen weiterhin korrekt zu sein.

Beispiel:

Dies ist ein serialisierter Hash, der als UTF8-String in der Datenbank gespeichert wird:

%Vor%

Um diese Zeichenfolge in einen Ruby-Hash zu konvertieren, deserialiere ich ihn mit YAML.load :

%Vor%

Dies gibt einen Hash mit verzerrtem Inhalt zurück:

%Vor%

Das verstümmelte Zeug soll UTF8-kodiertes Chinesisch sein. broken_hash['info'].encoding sagt mir, dass Ruby denkt, dass dies #<Encoding:UTF-8> ist. Ich stimme nicht zu.

Interessanterweise sehen jedoch alle anderen Strings, die vorher nicht serialisiert wurden, gut aus. Im selben Datensatz enthält ein anderes Feld chinesische Zeichen, die genau richtig aussehen - in der Rails-Konsole, der psql-Konsole und dem Browser. Jede Zeichenfolge --- egal ob serialisierter Hash oder einfacher String --- wird in der Datenbank gespeichert, da das Update auch gut aussieht.

Ich habe versucht, den verstümmelten Text von einer möglichen falschen Kodierung (wie GB2312 oder ANSI) in UTF-8 zu konvertieren, trotz der Behauptung von Ruby, dass dies bereits UTF-8 war und natürlich habe ich versagt. Dies ist der Code, den ich verwendet habe:

%Vor%

Dies schlägt fehl, weil Ruby nicht weiß, was mit illegalen Sequenzen in der Zeichenfolge zu tun ist.

Ich möchte wirklich nur ein Skript ausführen, um alle alten, vermutlich kaputten serialisierten Hash-Strings zu reparieren und damit fertig zu werden. Gibt es eine Möglichkeit, diese kaputten Strings wieder in etwas ähnlich Chinesisches umzuwandeln?

Ich habe gerade mit der codierten UTF-8-Saite in der rohen Saite gespielt (im obigen Beispiel "gebrochen" genannt). Dies ist die chinesische Zeichenfolge, die in der serialisierten Zeichenfolge codiert ist:

chinese = "\xEF\xBC\x88\xE5\x9B\x9E\xE5\xBD\xA2\xE9\x92\x88\xEF\xBC\x89\r\n\"

Ich habe festgestellt, dass es einfach ist, dies in eine echte UTF-8-codierte Zeichenkette umzuwandeln, indem Sie es entfernen (Entfernen der Escape-Backslashes).

chinese_ok = "\xEF\xBC\x88\xE5\x9B\x9E\xE5\xBD\xA2\xE9\x92\x88\xEF\xBC\x89\r\n"

Dies gibt eine korrekte UTF-8-kodierte chinesische Zeichenfolge zurück: "(回形针)\r\n"

Das Ding fällt nur auseinander, wenn ich YAML.load(...) verwende, um die Zeichenfolge in einen Ruby-Hash zu konvertieren. Vielleicht sollte ich die rohe Zeichenfolge verarbeiten, bevor sie YAML.load zugeführt wird. Ich frage mich nur, warum das so ist ...

Interessant! Dies ist wahrscheinlich auf die YAML-Engine "psych" zurückzuführen, die standardmäßig in 1.9.3 verwendet wird. Ich wechselte zur "syck" -Engine mit YAML::ENGINE.yamler = 'syck' und die unterbrochenen Strings werden korrekt analysiert.

    
rekado 19.12.2011, 07:01
quelle

2 Antworten

12

Dies scheint durch einen Unterschied im Verhalten der beiden verfügbaren YAML-Engines "syck" und "psych" verursacht worden zu sein. So setzen Sie die YAML-Engine auf syck:

YAML::ENGINE.yamler = 'syck'

Setzen Sie die YAML-Engine zurück auf psych:

YAML::ENGINE.yamler = 'psych'

Die "syck" -Engine verarbeitet die Strings wie erwartet und konvertiert sie in Hashes mit korrekten chinesischen Strings. Wenn die "psych" -Engine verwendet wird (Standard in Ruby 1.9.3), führt die Konvertierung zu verzerrten Zeichenfolgen.

Das Hinzufügen der obigen Zeile (die erste der beiden) zu config/application.rb behebt dieses Problem. Die "syck" -Engine wird nicht länger beibehalten, daher sollte ich diese Problemumgehung wahrscheinlich nur verwenden, um mir etwas Zeit zu verschaffen, um die Zeichenfolgen für "psych" akzeptabel zu machen.

    
rekado 20.12.2011, 02:39
quelle
9

Aus der 1.9.3 NEWS-Datei :

%Vor%

Offenbar behandeln die SYCK- und Psych-YAML-Engines Nicht-ASCII-Zeichenfolgen auf verschiedene und inkompatible Weise.

Gegeben ein Hash wie du hast:

%Vor%

Verwenden der alten Syck-Engine:

%Vor%

Wir erhalten das hässliche Doppel-Backslash-Format, das Sie derzeit in Ihrer Datenbank haben. Wechsel zu Psych:

%Vor%

Die Strings bleiben im normalen UTF-8-Format. Wenn wir die Kodierung manuell auf Latin-1 aufschrauben:

%Vor%

Dann erhalten wir die Art von Unsinn, die Sie sehen.

Die YAML-Dokumentation ist ziemlich dünn, also weiß ich nicht, ob Sie Psych zwingen können, das alte Syck-Format zu verstehen. Ich denke, Sie haben drei Möglichkeiten:

  1. Benutze die alte, nicht unterstützte und veraltete Syck-Engine, du müsstest YAML::ENGINE.yamler = 'syck' vor dir haben, YAML irgendetwas.
  2. Laden und dekodieren Sie alle Ihre YAML mit Syck und dann neu kodieren und speichern Sie es mit Psych.
  3. Beenden Sie die Verwendung von serialize zugunsten der manuellen Serialisierung / Deserialisierung mit JSON (oder einem anderen stabilen, vorhersagbaren und portablen Textformat) oder verwenden Sie eine Zuordnungstabelle, damit Sie keine serialisierten Daten speichern.
mu is too short 20.12.2011 02:41
quelle