File.open("sample.rb", "r", :encoding => Encoding::CP932) do |file|
file.each_line do |line|
do_something(line)
end
end
ファイル読み込み処理を行うと、頻繁に出くわすのがエンコード関係の例外。Encoding::InvalidByteSequenceError
など、その名もずばりな例外はもちろんのこと、たとえば読み込んだ行に対してline.split(",")
を実行したとたん、ArgumentError
が発生するということもあります。
このような例外はたいていファイルに含まれる不正なバイトやや変換できない文字が原因です。ではそのようなバイトや文字を読み込んだ場合でも、例外を発生させるのではなく、あたかも正しいバイトや文字を読み込んだかのようにふるまわせるにはどうすればよいでしょうか? あけすけにいってしまうと――ファイルの読み込み時に文字コード関係の例外を抑制するにはどうすればよいでしょうか?
__実はFile#open
の引数として、String#encode
のオプションを指定することができます。__これを利用しましょう。たとえば「読み込んだファイルに不正なバイトや変換不可能文字が含まれていた場合でも例外を発生させるのではなく、無視して処理を実行したい」というときには次のように書きます。
File.open("sample.rb", "r", :encoding => Encoding::CP932, :invalid => :replace, :undef => :replace) do |file|
file.each_line do |line|
do_something(line)
end
end
この例では「不正な文字」は置換されるため、例外の発生が抑制されるのですが、一方で__情報が失われる可能性があることにも注意する必要があります。__ただし「ファイル処理のたびにbegin/rescue/retry
を書く面倒さ」「巨大ファイルの処理中に例外が起きたために最初からやり直さねばならない絶望感」そして「困難なデバックの結果、その例外が本質的ではないところで起きたことが分かった時のやるせなさ」などなどを勘案すると、情報を失うことを覚悟で「不正な文字」を捨ててしまうのがもっとも現実的だとは思います。
なお「File#open
の引数にString#encode
と同じオプションが指定できる」ことですが、File#open
の「るりま」には明記されていません。「るりま」ではIO#open
にその旨が少し書かれているだけで、やや不親切なような気もしなくはない(´・ω・`)
また__csv
ライブラリにもファイルをCSV形式として開くCSV#open
が用意されていますが、こちらのメソッドはString#encode
と同じオプションを指定することはできません。__同じ名前なのにややこしい(´・ω・`) したがってCSV#open
を何も考えずに使っていると、文字コードがらみの例外が頻発することになります。
# この書き方だと「不正な文字」に出くわすと例外が発生する。
require "csv"
CSV.open("sample.csv", "r", :encoding => Encoding::CP932) do |csv|
csv.each do |row|
do_something(row)
end
end
この対処法は実は簡単です。上述の例ではCSV#open
の引数にパス文字列を与えていますが、これをFile
オブジェクトに変更します。そしてこのFile
オブジェクトを生成するFile#open
メソッドの引数として、String#encode
のオプションを指定すればよいというわけです。
# 2017-10-24: サンプルコードに誤りがあったため修正
# require "csv"
# File.open("sample.csv", "r", :encoding => Encoding::CP932, :invalid => :replace, :undef => :replace) do |file|
# CSV.open(file) do |csv|
# csv.each do |row|
# do_something(row)
# end
# end
# end
require "csv"
File.open("sample.csv", "r", :encoding => Encoding::CP932, :invalid => :replace, :undef => :replace) do |file|
CSV.parse(file) do |row|
do_something(row)
end
end
★参考★