Как мне запретить адаптеру Sequel postgres-pr возвращать данные в неправильной кодировке?

Я получаю неверную кодировку от адаптера jeremyevans-postgres-pr, который является одним из те, которые сиквел рекомендует.

Я что-то делаю не так?

Пример кода:

require 'postgres-pr/connection'
c = PostgresPR::Connection.new('blah', 'blah', 'blah')

row = c.query("select name, cost from remedium.prescription").rows.last
row.each do |f|
  #f.force_encoding(Encoding::UTF_8) #-- uncomment this to 'fix' everything ;/

  enc = f.kind_of?(String) ? f.encoding : ''
  puts [f.class, f, f.inspect, enc].join(' ')
end

Вывод:

String Paracelsium "Paracelsium" ASCII-8BIT
String £0.00 "\xC2\xA30.00" ASCII-8BIT

Излишне говорить, что я получаю те же результаты от самого Sequel. И моя база данных закодирована не в ASCII, а в UTF8.

  • Прямо сейчас мне нужно поговорить с PostgreSQL, используя как Ruby, так и JRuby, и имеет смысл попробовать использовать одну и ту же цепочку инструментов в каждом случае, так что это очевидный адаптер для использования. Но.

  • исходный адаптер, который разветвил Джереми, ведет себя так же.

  • Я могу исправить это, принудительно закодировав кодировку по всем направлениям, но это будет очень больно делать во всем моем коде...


Некоторые дополнительные ответы, которые я, вероятно, должен был предоставить (спасибо):

  • версии -> Centos 7; Руби 2.3.1 и JRuby 9.1.1.0; гем версия 0.6.6
  • Язык ОС -> "LANG=en_GB.UTF-8"
  • Настройки Ruby по умолчанию -> Encoding.default_internal = nil; default_external = Кодировка::UTF-8

person Andy Jones    schedule 16.06.2016    source источник
comment
Какую версию Ruby вы используете? Какая ОС и какие настройки локали у него есть? Каков результат Encoding.default_internal и Encoding.default_external? Что, если вы запустите скрипт следующим образом: ruby ​​-E UTF-8 myscript.rb   -  person Pascal    schedule 16.06.2016
comment
Хорошие вопросы, спасибо. Общие вещи я ответил выше. Что касается вашего последнего вопроса: без изменений...   -  person Andy Jones    schedule 16.06.2016
comment
Просто чтобы убедиться: можете ли вы проверить вывод psql -l? Должен показывать кодировку ваших БД.   -  person Pascal    schedule 16.06.2016


Ответы (3)


postgres-pr не поддерживает кодировки. Я не планирую добавлять это сам, но я готов рассмотреть патчи.

Вы можете использовать плагин force_encoding Sequel::Model, чтобы исправить кодировку, по крайней мере, для тех мест, где вы используете модели.

person Jeremy Evans    schedule 16.06.2016
comment
Справедливо. Любая идея, почему по умолчанию используется ASCII? - person Andy Jones; 16.06.2016

Я попробовал ваш пример кода на одной из моих БД, и все сработало, как и ожидалось. Может быть что-то особенное в вашей установке? Вы можете проверить кодировку клиента с помощью

p c.query("SHOW client_encoding;").rows.first

И должен иметь возможность установить его, используя:

p c.query("SET CLIENT_ENCODING TO 'UTF-8';")

После отзывов Энди Джонса и комментариев Джереми я еще немного изучил исходный код. Редактирование вопроса, чтобы у меня были лучшие параметры форматирования, чем в качестве комментария.

Драйвер считывает US-ASCII из потока следующим образом:

    buffer.copy_from_stream(stream, length-4)

исходный код

Это, в свою очередь, вызывает Buffer#write, который вставляет данные в свой собственный @content:

@content[@position, sz] = str

исходный код

Теперь @content представляет собой строку определенного размера, заполненную знаками #, и создается она следующим образом:

def self.of_size(size)
  raise ArgumentError if size < 0
  new('#' * size
end

Это делает @content строкой UTF-8, если ваша система настроена на использование UTF-8. Объединение строки US-ASCII с UTF приведет к строке __UTF-8. С этого момента преобразование не происходит, поэтому он должен оставаться в этой кодировке. Содержимое столбца считывается с помощью метода Buffer.read, который делает следующее:

@content[@position, n]

исходный код

Это длинное объяснение просто для того, чтобы сказать: я не понимаю, почему вы видите вывод US-ASCII :-(

ЕСЛИ: ваша система не настроена на использование UTF-8 каким-то странным образом.

Кодировка по умолчанию была US-ASCII в Ruby 1.9 и изменена на UTF-8 в Ruby 2.2 (или раньше, не уверен?).

У тебя есть

# encoding: 

стиль комментария в начале файла?

Что произойдет, если вы сделаете

puts String.new.encoding

Какова ценность

puts __ENCODING__

Какова ценность

puts RUBY_VERSION

Пожалуйста, зарегистрируйте тот же файл, в котором вы запускаете тестовый сценарий БД.

person Pascal    schedule 16.06.2016
comment
К сожалению, вы правы только наполовину. Я действительно могу установить такую ​​кодировку, и когда я показываю кодировку, как вы предлагаете, это подтверждает, что кодировка изменилась. .... и затем он продолжает возвращать данные с кодировкой ASCII без изменений... - person Andy Jones; 16.06.2016
comment
Очень жаль. Это работает на моей машине (тм). Использование Руби 2.2.x. - person Pascal; 16.06.2016
comment
Согласитесь, очень впечатляет, что это работает на вашей машине, учитывая, что сам сопровождающий ответил на этот вопрос, сказав, что кодировки не поддерживаются ;) - person Andy Jones; 17.06.2016
comment
@AndyJones Я запускаю скрипт, предоставленный OP, и он показывает UTF-8. Какой смысл имеет смысл: ruby ​​по умолчанию использует кодировку UTF-8 уже довольно давно, а локаль моей ОС — UTF-8, как и БД, к которой я обращаюсь. Я также изучил исходный код драгоценного камня. Так и не смог найти, что кодировка принудительно ASCII. Так что я бы на самом деле ожидал UTF-8. Я прочитал комментарий Джереми. Я думаю, он имел в виду, что кодировка не поддерживается для изменения/применения на клиенте. Возможно, Джереми сможет дать больше информации... - person Pascal; 17.06.2016
comment
Что ж, все это верно и для меня, и я не получаю таких же результатов, поэтому должно происходить что-то еще, по крайней мере, для одного из нас. Я далеко не эксперт здесь, но я думал, что кодировка по умолчанию применяется только к фактическим строкам, а не к строкам, созданным из двоичных данных, которые действительно будут иметь кодировку ASCII-8BIT, что в основном означает, что Ruby не знает ? - person Andy Jones; 17.06.2016
comment
Да.... странно. Я изучил это немного больше и обновил ответ. Было бы интересно, если бы вы могли прочитать его и проверить вопросы. - person Pascal; 17.06.2016
comment
Разобрался - посмотри мой ответ. TL;DR: внутренняя кодировка ‹› внешняя кодировка... - person Andy Jones; 17.06.2016

Ключом к пониманию того, что здесь происходит, являются множественные настройки кодирования в ruby. Есть:

  • кодировка локали, установленная с помощью магического комментария или ключа командной строки -k.

  • внешняя кодировка по умолчанию, установленная с помощью Encoding.default_external или --external-encoding или -E

  • внутренняя кодировка по умолчанию, заданная с помощью Encoding.default_internal или --internal-encoding (или -E после двоеточия)

Ruby по умолчанию устанавливает для строки внутреннюю или внешнюю кодировку на основе некоторых довольно запутанных правил. Подробнее см. в документации. Но важной частью здесь является то, что когда строка создается из двоичных данных, кажется, что используется внутренняя, а не внешняя кодировка.

Моя внутренняя кодировка равна нулю, так что этого не происходило. (ASCII-8BIT — это кодировка, которую вы получаете, когда Ruby не знает, что такое кодировка — это в основном означает «для меня это просто данные; удачи в чтении».)

Если я передам --internal-encoding UTF-8 в командной строке, проблема исчезнет. В некотором смысле это не имеет ничего общего с жемчужиной Джереми.

Когда я помещаю -E UTF-8 в командную строку, это устанавливает внешнюю кодировку по умолчанию. Что в данном случае ничего не дало.

РЕДАКТИРОВАТЬ: В этом случае это сработало, но при некоторых (всех?) обстоятельствах Ruby транскодирует строку во внутреннюю кодировку, а не просто устанавливает значение кодировки, как в force_encoding. Это тяжело.

person Andy Jones    schedule 17.06.2016