Ruby 1.8 Iconv UTF-16 в UTF-8 завершается с ошибкой \000 (Iconv::InvalidCharacter)

У меня возникают проблемы с обработкой текстовых файлов табличных данных, сгенерированных на компьютере с Windows. Я работаю в Руби 1.8. Следующее выдает ошибку ("\000" (Iconv::InvalidCharacter)) при обработке ВТОРОЙ строки из файла. Первая строка преобразована правильно.

require 'iconv'
conv = Iconv.new("UTF-8//IGNORE","UTF-16")
infile = File.open(tabfile, "r")
while (line = infile.gets)
  line = conv.iconv(line.strip)  # FAILS HERE
  puts line
  # DO MORE STUFF HERE
end

Странно то, что он без проблем читает и преобразует первую строку в файле. У меня есть флаг //IGNORE в конструкторе Iconv - я думал, что это должно было подавить такого рода ошибки.

Я ходил по кругу какое-то время. Любые советы будут высоко оценены.

Спасибо!

РЕДАКТИРОВАТЬ: решение hobbs исправляет это. Спасибо. Просто измените код на:

require 'iconv'
conv = Iconv.new("UTF-8//IGNORE","UTF-16")
infile = File.open(tabfile, "r")
while (line = infile.gets("\x0a\x00"))
  line = conv.iconv(line.strip)  # NO LONGER FAILS HERE
  # DOES MORE STUFF HERE
end

Теперь мне просто нужно найти способ автоматически определять, какой разделитель следует использовать.


person NAD    schedule 30.05.2011    source источник


Ответы (2)


Сообщение об ошибке довольно расплывчато, но я думаю, что оно недовольно тем фактом, что в строке найдено нечетное количество байтов, поскольку каждый символ в UTF-16 состоит из двух (иногда четырех) байтов. И я думаю, что причина это заключается в том, что вы используете gets -- строки в вашем файле разделены новой строкой UTF-16le, которая равна 0x0a 0x00, но gets разбивается (а strip удаляется) только 0x0a.

Для иллюстрации: предположим, что файл содержит

ab
cd

кодируется в UTF-16le. Это

0x61 0x00 0x62 0x00 0x0a 0x00 0x63 0x00 0x64 0x00 0x0a 0x00
    a         b         \n        c         d         \n

gets считывается до первой 0x0a, которую strip удаляет, поэтому первой прочитанной строкой является 0x61 0x00 0x62 0x00, которую iconv с радостью принимает и кодирует в UTF-8 как 0x61 0x62 — «ab». gets затем считывает до следующего 0x0a, который strip снова удаляет, так что во второй раз line получает 0x00 0x63 0x00 0x64 0x00 и теперь все облажались — мы рассинхронизировались на один байт и нужно преобразовать нечетное количество байтов, и iconv дует вверх, потому что это несовместимо с тем, что вы просили сделать.

В отсутствие фактического слоя кодирования/декодирования рабочего файла, я думаю, вы хотите изменить разделитель gets с "\n" ("\x0a") на "\x0a\x00", отказаться от использования strip, поскольку он не является чистым кодированием, и использовать print вместо puts, чтобы вы не добавляйте дополнительные концы строк (поскольку вы будете преобразовывать те, которые у вас уже есть).

Если вы работаете с файлами Windows, CRLF Windows в UTF-16le имеет значение "\x0d\x00\x0a\x00".

person hobbs    schedule 30.05.2011

Ответ выше хороший. Вы также можете преобразовать весь файл в UTF-8 перед его построчной обработкой, но это может привести к ухудшению потоковой передачи больших файлов.

person Community    schedule 31.05.2011