Ruby lee el archivo CSV como UTF-8 y / o convierte la codificación ASCII-8Bit a UTF-8


Estoy usando ruby 1.9.2

Estoy tratando de analizar un archivo CSV que contiene algunas palabras francesas (por ejemplo, spécifié) y colocar el contenido en una base de datos MySQL.

Cuando leo las líneas del archivo CSV,

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

Los elementos regresan como Cadenas que están codificadas ASCII-8BIT (spécifié se convierte en sp\xE9cifi\xE9), y cadenas como "spécifié" NO se guardan correctamente en mi base de datos MySQL.

Yehuda Katz dice que ASCII-8BIT es realmente datos "binarios", lo que significa que CSV no tiene idea de cómo leer la codificación adecuada.

Entonces, si intento hacer que CSV fuerce la codificación de esta manera:

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

Obtengo el siguiente error

ArgumentError: invalid byte sequence in UTF-8: 

Si vuelvo a mis cadenas codificadas ASCII-8BIT originales y examino la Cadena que mi CSV leyó como ASCII-8BIT, se ve como "Non sp\xE9cifi\xE9" en lugar de "Non spécifié".

No puedo convertir "Non sp\xE9cifi\xE9" a "Non spécifié" haciendo esto "Non sp\xE9cifi\xE9".encode("UTF-8")

Porque obtengo este error:

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

Lo que Katz indicó que sucedería porque ASCII-8BIT no es realmente una cadena adecuada "codificación".

Preguntas:

  1. ¿Puedo hacer que CSV lea mi archivo en la codificación adecuada? Si es así, ¿cómo?
  2. ¿Cómo convertir una cadena ASCII-8BIT a UTF-8 para el almacenamiento adecuado en MySQL?
Author: jpemberthy, 2011-08-13

3 answers

Deceze es correcto, es decir, texto codificado ISO8859-1 (TAMBIÉN conocido como Latín-1). Prueba esto:

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

Y si eso no funciona, puede usar Iconv para arreglar las cadenas individuales con algo como esto:

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

Si latin1_string es "Non sp\xE9cifi\xE9", entonces utf8_string se "Non spécifié". También, Iconv.iconv puede desenmarañar arrays enteros a la vez:

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

Con Rubíes más nuevos, puedes hacer cosas como esta:

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

Donde latin1_string piensa que está en ASCII-8BIT pero está realmente en ISO-8859-1.

 53
Author: mu is too short,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:54

Con ruby > = 1.9 puedes usar

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

El ISO8859-1:utf-8 significa: El archivo csv está codificado en ISO8859-1, pero convierte el contenido a utf-8

Si prefiere un código más detallado, puede usar:

file_contents = CSV.read("csvfile.csv", col_sep: "$", 
    external_encoding: "ISO8859-1", 
    internal_encoding: "utf-8"
  )
 22
Author: knut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-20 19:15:25

He estado lidiando con este problema por un tiempo y ninguna de las otras soluciones funcionó para mí.

Lo que hizo el truco fue almacenar la cadena conflictiva en un archivo binario, luego leer el archivo normalmente y usar esta cadena para alimentar el módulo 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)
 1
Author: fguillen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-20 16:03:39