2011-08-13 9 views
46

estoy usando ruby ​​1.9.2Rubí leer el archivo CSV como UTF-8 y/o convertir ASCII de 8 bits de codificación a UTF-8

Estoy intentando analizar un archivo CSV que contiene algo de francés palabras (por ejemplo, spécifié) y colocar los contenidos 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 se ASCII-8BIT codificado (spécifié convierte 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 información "binaria", lo que significa que CSV no tiene idea de cómo leer la codificación adecuada.

Por lo tanto, si trato de hacer CSV forzar la codificación de la siguiente manera:

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

me sale el siguiente error

ArgumentError: invalid byte sequence in UTF-8: 

Si vuelvo a mi original ASCII-8BIT codificada Cadenas y examinar la cadena que mi CSV lee como ASCII-8BIT, se ve así: "Non sp \ xE9cifi \ xE9" en lugar de "Non spécifié".

no puedo convertir "\ xE9cifi \ xE9 no sp" a "no spécifié" al hacer esto "Non sp\xE9cifi\xE9".encode("UTF-8")

porque me sale este error:

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

el cual indicó Katz ocurriría porque ASCII-8BIT no es realmente una "codificación" de cadenas adecuada.

Preguntas:

  1. ¿Puedo conseguir CSV para leer mi archivo en la codificación adecuada? ¿Si es así, cómo?
  2. ¿Cómo convierto una cadena ASCII-8BIT a UTF-8 para un almacenamiento correcto en MySQL?
+0

Parece que el archivo podría no ser codificación UTF-8; ¿Has verificado la codificación real del archivo? – coreyward

+3

Su archivo no está codificado en UTF-8. é en UTF-8 debería ser 'C3 A9', no' E9'. Parece que estás tratando con ISO-8859-1 en su lugar. – deceze

+3

Creo que lo descubrí: my_ascii_8bit_string.unpack ("C *"). Pack ("U *") parece funcionar. – user141146

Respuesta

51

deceze es correcto, es decir ISO8859-1 (También conocido como Latin-1) texto codificado. Prueba esto:

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

Y si eso no funciona, puede utilizar Iconv para arreglar las cuerdas 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 habrá utf8_string"Non spécifié" .Además, Iconv.iconv puede unmangle matrices enteras a la vez:

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

Con los nuevos rubíes, se pueden 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 es realmente en la norma ISO -8859-1.

+1

muchas gracias. – user141146

+2

Tenga en cuenta que Ruby ahora quiere que use 'String # encode' en lugar de usar' iconv'. – duma

+1

@duma: ¿mejor ahora? Dejé las viejas cosas de Iconv y agregué una breve nota sobre el uso de 'force_encoding' y' encode' en lugar de Iconv. –

1

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

Lo que hizo el truco consistía en almacenar la conflictiva cadena en un archivo binario, a continuación, leer el archivo normalmente y usar este 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) 
21

Con ruby> = 1.9 puede utilizar

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

El ISO8859-1:utf-8 un significado: El archivo CSV se ISO8859-1 - codificada, pero conve ta el contenido a UTF-8

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

file_contents = CSV.read("csvfile.csv", col_sep: "$", 
    external_encoding: "ISO8859-1", 
    internal_encoding: "utf-8" 
) 
+0

esta respuesta se ve mejor que la aceptada – serggl

+0

Esto es impresionante . Antes, tenía que poner un 'bom' para este utf-16 csv:' '' CSV.read ('nom_nom_nom.csv', {: headers => true,: col_sep => "\ t",: encoding = > 'bom | utf-16le'}) '' ', de lo contrario arrojaría errores. Ahora es: '' 'CSV.read ('nom_nom_nom.csv', {: headers => true,: col_sep =>" \ t ", external_encoding: 'utf-16', internal_encoding:" utf- 8 "}) ' ''. – Hahn

Cuestiones relacionadas