55 votes

Ruby lit le fichier CSV en UTF-8 et/ou convertit l'encodage ASCII-8Bit en UTF-8

J'utilise ruby 1.9.2

J'essaie de analyser un fichier CSV qui contient certains mots français (p. ex. spécifié) et placer le contenu dans une base de données MySQL.

Quand je lis les lignes du fichier CSV,

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

Les éléments reviennent sous forme de chaînes de caractères qui sont ASCII-8BIT encoded (spécifié becomes sp \xE9cifi\xE9 ), et les chaînes de caractères comme "spécifié" ne sont alors PAS correctement enregistrées dans ma base de données MySQL.

Yehuda Katz dit que l'ASCII-8BIT est en réalité des données "binaires", ce qui signifie que CSV n'a aucune idée de la façon de lire l'encodage approprié.

Donc, si j'essaie de faire CSV forcer l'encodage comme ceci :

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

J'obtiens l'erreur suivante

ArgumentError: invalid byte sequence in UTF-8: 

Si je reviens à mes chaînes codées en ASCII-8BIT d'origine et que j'examine la chaîne que mon CSV a lue en ASCII-8BIT, elle ressemble à ceci "Non sp \xE9cifi\xE9 " instead of "Non spécifié".

Je n'arrive pas à convertir "Non sp \xE9cifi\xE9 "en "Non spécifié" en procédant comme suit "Non sp\xE9cifi\xE9".encode("UTF-8")

parce que je reçois cette erreur :

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

ce qui, selon Katz, se produirait parce que l'ASCII-8BIT n'est pas vraiment un "codage" correct des chaînes de caractères.

Questions :

  1. Puis-je faire en sorte que CSV lise mon fichier dans l'encodage approprié ? Si oui, comment ?
  2. Comment convertir une chaîne de caractères ASCII-8BIT en UTF-8 pour la stocker correctement dans MySQL ?

61voto

mu is too short Points 205090

deceze a raison, il s'agit d'un texte codé ISO8859-1 (AKA Latin-1). Essayez ceci :

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

Et si cela ne fonctionne pas, vous pouvez utiliser Iconv pour arranger les chaînes individuelles avec quelque chose comme ça :

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

Si latin1_string es "Non sp\xE9cifi\xE9" entonces utf8_string sera "Non spécifié" . Aussi, Iconv.iconv peut démêler des tableaux entiers à la fois :

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

Avec les Rubis les plus récents, vous pouvez faire des choses comme ça :

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

donde latin1_string pense être en ASCII-8BIT mais est en réalité en ISO-8859-1.

35voto

knut Points 10107

Avec ruby >= 1.9 vous pouvez utiliser

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

El ISO8859-1:utf-8 signifie : Le fichier csv est codé ISO8859-1, mais le contenu est converti en utf-8.

Si vous préférez un code plus verbeux, vous pouvez utiliser :

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

1voto

fguillen Points 11849

Je suis confronté à ce problème depuis un certain temps et aucune des autres solutions n'a fonctionné pour moi.

La chose qui a fait le tour était de stocker le conflit chaîne de caractères dans un binaire Fichier, puis lire le fichier normalement et en utilisant ceci chaîne de caractères pour alimenter le module 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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X