Rails: кодирование проблем с сериализованными хэшами, несмотря на UTF8 - PullRequest
9 голосов
/ 19 декабря 2011

Я только что обновился с ruby ​​1.9.2 до ruby ​​1.9.3p0 (2011-10-30 ревизия 33570).Мое приложение rails использует postgresql в качестве базы данных.Системный языковой стандарт UTF8, как и кодировка базы данных.Кодировка по умолчанию для приложения rails также UTF8.У меня есть китайские пользователи, которые вводят китайские и английские символы.Строки хранятся в виде строк в кодировке UTF8.

Версия Rails: 3.0.9

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


Пример:

Это сериализованный хэш, который сохраняется как строка UTF8в базе данных:

broken = "--- !map:ActiveSupport::HashWithIndifferentAccess \ncheckbox: \"1\"\nchoice: \"Round Paper Clips  \\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n\"\ninfo: \"10\\xE7\\x9B\\x92\"\n"

Чтобы преобразовать эту строку в хэш рубина, я десериализовал ее с помощью YAML.load:

broken_hash = YAML.load(broken)

Возвращает хэш с искаженным содержимым:

{"checkbox"=>"1", "choice"=>"Round Paper Clips  ï¼\u0088å\u009B\u009Eå½¢é\u0092\u0088ï¼\u0089\r\n", "info"=>"10ç\u009B\u0092"}

Предполагается, что искаженный материал будет китайским в кодировке UTF8.broken_hash['info'].encoding говорит мне, что ruby ​​думает, что это #<Encoding:UTF-8>.Я не согласен.

Интересно, что все остальные строки, которые не были сериализованы ранее, выглядят хорошо.В той же записи другое поле содержит китайские иероглифы, которые выглядят правильно - в консоли rails, консоли psql и браузере.Каждая строка - независимо от того, был ли сериализованный хеш или простая строка - сохранена в базе данных, поскольку обновление тоже выглядит хорошо.


Я пытался преобразовать искаженный текст из возможной неправильной кодировки (как GB2312 или ANSI) до UTF-8, несмотря на утверждение ruby ​​о том, что это уже UTF-8 и, конечно, я потерпел неудачу.Это код, который я использовал:

require 'iconv'
Iconv.conv('UTF-8', 'GB2312', broken_hash['info'])

Сбой, потому что ruby ​​не знает, что делать с недопустимыми последовательностями в строке.

Я действительно хочу запустить скрипт для исправлениявсе старые, предположительно сломанные сериализованные хеш-строки и покончим с этим.Есть ли способ преобразовать эти битые строки во что-то похожее на китайский снова?


Я только что поиграл с закодированной строкой UTF-8 в необработанной строке (называемой «разбитой» в приведенном выше примере).Это китайская строка, которая закодирована в сериализованной строке:

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

Я заметил, что ее легко преобразовать в настоящую строку в кодировке UTF-8, удалив ее (исключивescape-косые черты).

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

Возвращает правильную китайскую строку в кодировке UTF-8: "(回形针)\r\n"

Вещество разваливается только при использовании YAML.load(...) для преобразования строки в хэш рубина.Возможно, мне следует обработать необработанную строку до того, как она будет передана в YAML.load.Просто заставляет меня задуматься, почему это так ...


Интересно!Вероятно, это связано с движком YAML «psych», который теперь используется по умолчанию в 1.9.3.Я переключился на двигатель «syck» с YAML::ENGINE.yamler = 'syck', и разорванные строки были правильно проанализированы.

Ответы [ 2 ]

12 голосов
/ 20 декабря 2011

Это, похоже, было вызвано различием в поведении двух доступных движков YAML "syck" и "psych".Чтобы установить двигатель YAML на syck:

YAML::ENGINE.yamler = 'syck'

Чтобы установить двигатель YAML обратно в psy:

YAML::ENGINE.yamler = 'psych'

Syck«Движок обрабатывает строки, как ожидалось, и преобразует их в хэши с правильными китайскими строками.Когда используется механизм «psych» (по умолчанию в ruby ​​1.9.3), преобразование приводит к искаженным строкам.

Добавление указанной выше строки (первой из двух) к config/application.rb устраняет эту проблему.Механизм «syck» больше не поддерживается, поэтому я, вероятно, должен использовать этот обходной путь, чтобы выиграть время, чтобы сделать строки приемлемыми для «psych».

9 голосов
/ 20 декабря 2011

Из файла 1.9.3 NEWS :

* yaml
  * The default YAML engine is now Psych. You may downgrade to syck by setting
    YAML::ENGINE.yamler = 'syck'.

Очевидно, что движки Syck и Psych YAML обрабатывают не-ASCII-строки различными и несовместимыми способами.

Учитывая хэш, как у вас:

h = {
    "checkbox" => "1",
    "choice"   => "Round Paper Clips  (回形针)\r\n",
    "info"     => "10盒"
}

Использование старого движка Syck:

>> YAML::ENGINE.yamler = 'syck'
>> h.to_yaml
=> "--- \ncheckbox: "1"\nchoice: "Round Paper Clips  \\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n"\ninfo: "10\\xE7\\x9B\\x92"\n"

мы получаем уродливый формат двойной обратной косой черты, который у вас есть в вашей базе данных. Переключение на Psych:

>> YAML::ENGINE.yamler = 'psych'
=> "psych"
>> h.to_yaml
=> "---\ncheckbox: '1'\nchoice: ! "Round Paper Clips  (回形针)\\r\\n"\ninfo: 10盒\n"

Строки остаются в обычном формате UTF-8. Если мы вручную облажаемся, чтобы кодировка была латинской-1:

>> Iconv.conv('UTF-8', 'ISO-8859-1', "\xEF\xBC\x88\xE5\x9B\x9E\xE5\xBD\xA2\xE9\x92\x88\xEF\xBC\x89") 
=> "ï¼\u0088å\u009B\u009Eå½¢é\u0092\u0088ï¼\u0089"

тогда мы получаем ту чепуху, которую вы видите.

Документация YAML довольно тонкая, поэтому я не знаю, сможете ли вы заставить Psych понять старый формат Syck. Я думаю, у вас есть три варианта:

  1. Используйте старый неподдерживаемый и устаревший движок Syck, вам нужно будет YAML::ENGINE.yamler = 'syck', прежде чем что-нибудь YAML.
  2. Загрузите и декодируйте весь свой YAML с помощью Syck, а затем перекодируйте и сохраните его с помощью Psych.
  3. Прекратите использовать serialize в пользу ручной сериализации / десериализации с использованием JSON (или другого стабильного, предсказуемого и переносимого текстового формата) или используйте таблицу ассоциаций, чтобы вообще не хранить сериализованные данные.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...