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:
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')
où latin1_string
pense qu'il est en ASCII-8BIT mais est vraiment en ISO-8859-1.
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"
)
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)