web-dev-qa-db-ja.com

Ruby UTF-8としてCSVファイルを読み込む、および/またはASCII-8BitエンコードをUTF-8に変換する

私はRuby 1.9.2を使用しています

私はCSVファイルを解析にフランス語の単語(例:spécifié)を含め、その内容をMySQLデータベースに配置しようとしています。

CSVファイルから行を読み取ると、

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

要素はASCII-8BITエンコードされた文字列として返され(spécifiéはsp\xE9cifi\xE9になります)、「spécifié」などの文字列はMySQLデータベースに適切に保存されません。

Yehuda Katz は、ASCII-8BITは実際には「バイナリ」データであり、CSVには適切なエンコーディングの読み方がわからないことを意味します。

そのため、CSVで次のようなエンコードを強制しようとした場合:

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

次のエラーが表示されます

_ArgumentError: invalid byte sequence in UTF-8: 
_

元のASCII-8BITエンコード文字列に戻って、CSVがASCII-8BITとして読み込んだ文字列を調べると、「Nonspécifié」ではなく「Non sp\xE9cifi\xE9」のように見えます。

この"Non sp\xE9cifi\xE9".encode("UTF-8")を実行しても、「Non sp\xE9cifi\xE9」を「Nonspécifié」に変換できません。

私はこのエラーを取得するため:

_Encoding::UndefinedConversionError: "\xE9" from ASCII-8BIT to UTF-8_、

aSCII-8BITは実際には適切な文字列「エンコード」ではないため、Katzが示したようになります。

質問:

  1. CSVを取得して、適切なエンコードでファイルを読み取ることができますか?もしそうなら、どのように?
  2. MySQLに適切に保存するためにASCII-8BIT文字列をUTF-8に変換するにはどうすればよいですか?
52
user141146

deceze は正しい、つまりISO8859-1(別名Latin-1)でエンコードされたテキストです。これを試して:

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

それがうまくいかない場合は、 Iconv を使用して、次のような個々の文字列を修正できます。

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

latin1_string"Non sp\xE9cifi\xE9"、次にutf8_string"Non spécifié"。また、Iconv.iconvは一度に配列全体を解体できます。

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

新しいルビーを使用すると、次のようなことができます。

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

ここで、latin1_stringはASCII-8BITであると考えていますが、実際にはISO-8859-1にあります。

58
mu is too short

Ruby> = 1.9を使用できます

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

ISO8859-1:utf-8の意味:csvファイルはISO8859-1-エンコードされていますが、コンテンツをutf-8に変換します

より詳細なコードが必要な場合は、次を使用できます。

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

私はしばらくの間この問題に対処してきましたが、他のどのソリューションも私のために機能しませんでした。

トリックを作ったのは、競合するstringbinaryファイルに保存してから、通常どおりファイルを読み取り、このstringを使用して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