web-dev-qa-db-fra.com

Ruby lire le fichier CSV en UTF-8 et / ou convertir le codage ASCII-8Bit en UTF-8

J'utilise Ruby 1.9.2

J'essaie d'analyser un fichier CSV qui contient des mots français (par exemple spécifié) et de 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 qui sont ASCII-8BIT encodées (spécifié devient sp\xE9cifi\xE9), et les chaînes comme "spécifié" ne sont alors PAS correctement enregistré dans ma base de données MySQL.

Yehuda Katz dit que ASCII-8BIT est vraiment des données "binaires", ce qui signifie que CSV n'a aucune idée de comment lire l'encodage approprié.

Donc, si j'essaye 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 encodées ASCII-8BIT d'origine et que j'examine la chaîne que mon CSV lit comme ASCII-8BIT, cela ressemble à ceci "Non sp\xE9cifi\xE9" au lieu de "Non spécifié".

Je ne peux pas convertir "Non sp\xE9cifi\xE9" en "Non spécifié" en faisant ceci "Non sp\xE9cifi\xE9".encode("UTF-8")

parce que je reçois cette erreur:

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

ce que Katz a indiqué se produirait parce que ASCII-8BIT n'est pas vraiment un "encodage" de chaîne approprié.

Questions:

  1. Puis-je demander à CSV de lire mon fichier avec l'encodage approprié? Si oui, comment?
  2. Comment convertir une chaîne ASCII-8BIT en UTF-8 pour un stockage correct dans MySQL?
52
user141146

deceze est à droite, c'est-à-dire du texte encodé ISO8859-1 (AKA Latin-1). Essaye ça:

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

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

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

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

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

Avec les nouveaux Rubis, vous pouvez faire des choses comme ceci:

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

latin1_string pense qu'il est en ASCII-8BIT mais est vraiment en ISO-8859-1.

58
mu is too short

Avec Ruby> = 1.9, vous pouvez utiliser

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

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

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

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

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

La chose qui a fait l'affaire était de stocker le conflit chaîne dans un fichier binaire, puis de lire le fichier normalement et d'utiliser ce chaîne 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)
1
fguillen