Ruby считывает CSV-файл как UTF-8 и / или преобразует кодировку ASCII-8Bit в UTF-8 - PullRequest
52 голосов
/ 13 августа 2011

Я использую ruby ​​1.9.2

Я пытаюсь проанализировать файл CSV , который содержит некоторые французские слова (например, spécifié), и поместить содержимое в базу данных MySQL.

Когда я читаю строки из файла CSV,

file_contents = CSV.read("csvfile.csv", col_sep: "$")

Элементы возвращаются в виде строк, которые ASCII-8BIT закодированы (spécifié становится sp \ xE9cifi \ xE9), и строки типа "spécifié" не будут должным образом сохранены в моей базе данных MySQL.

Иегуда Кац говорит, что ASCII-8BIT - это действительно "двоичные" данные, что означает, что CSV не знает, как читать соответствующую кодировку.

Итак, если я попытаюсь заставить CSV форсировать кодировку так:

file_contents = CSV.read("csvfile.csv", col_sep: "$", encoding: "UTF-8")

Я получаю следующую ошибку

ArgumentError: invalid byte sequence in UTF-8: 

Если я вернусь к своим исходным строкам в кодировке ASCII-8BIT и изучу строку, которую мой CSV читает как ASCII-8BIT, она выглядит как «Non sp \ xE9cifi \ xE9» вместо «Non spécifié».

Я не могу преобразовать "Non sp \ xE9cifi \ xE9" в "Non spécifié", выполнив это "Non sp\xE9cifi\xE9".encode("UTF-8")

потому что я получаю эту ошибку:

Encoding::UndefinedConversionError: "\xE9" from ASCII-8BIT to UTF-8,

, который указал Кац, произойдет, потому что ASCII-8BIT на самом деле не является надлежащей строковой "кодировкой".

Вопросы:

  1. Могу ли я получить CSV для чтения моего файла в соответствующей кодировке? Если да, то как?
  2. Как преобразовать строку ASCII-8BIT в UTF-8 для правильного хранения в MySQL?

Ответы [ 3 ]

58 голосов
/ 13 августа 2011

deceze верно, то есть текст в кодировке ISO8859-1 (AKA Latin-1). Попробуйте это:

file_contents = CSV.read("csvfile.csv", col_sep: "$", encoding: "ISO8859-1")

И если это не сработает, вы можете использовать Iconv, чтобы исправить отдельные строки примерно так:

require 'iconv'
utf8_string = Iconv.iconv('utf-8', 'iso8859-1', latin1_string).first

Если latin1_string равно "Non sp\xE9cifi\xE9", то utf8_string будет "Non spécifié". Кроме того, Iconv.iconv может восстанавливать целые массивы одновременно:

utf8_strings = Iconv.iconv('utf-8', 'iso8859-1', *latin1_strings)

С более новыми Рубинами вы можете делать такие вещи:

utf8_string = latin1_string.force_encoding('iso-8859-1').encode('utf-8')

, где latin1_string считает, что это в ASCII-8BIT, но действительно в ISO-8859-1.

24 голосов
/ 20 ноября 2015

С ruby> = 1.9 вы можете использовать

file_contents = CSV.read("csvfile.csv", col_sep: "$", encoding: "ISO8859-1:utf-8")

Значение ISO8859-1:utf-8 означает: csv-файл имеет кодировку ISO8859-1, но конвертирует содержимое в utf-8

Если вы предпочитаете более подробный код, вы можете использовать:

file_contents = CSV.read("csvfile.csv", col_sep: "$", 
    external_encoding: "ISO8859-1", 
    internal_encoding: "utf-8"
  )
1 голос
/ 20 ноября 2015

Я какое-то время занимался этой проблемой, и никакие другие решения не работали для меня.

Единственное, что помогло мне - сохранить конфликтную строку в двоичный файл Файл, затем прочитайте файл в обычном режиме и используйте эту строку для подачи в модуль CSV:

tempfile = Tempfile.new("conflictive_string")
tempfile.binmode
tempfile.write(conflictive_string)
tempfile.close
cleaned_string = File.read(tempfile.path)
File.delete(tempfile.path)
csv = CSV.new(cleaned_string)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...