タイトル通り。
これまであまりファイルの扱いをするような実装をしてこなかったのでメモ。
- Ruby...2.4.3
- CSV...2.4.8
File.open
ファイルアクセスのためのクラス。openに引数を渡すことでファイルオブジェクトを生成する。
File.open
とFile.new
で何が違うんだろうと思ったのだけどおそらく挙動は一緒っぽい。
With no associated block, File.open is a synonym for File.new.
ブロックでopenを使う場合は自動でcloseされるのでそこの違い。
class File - Documentation for Ruby 2.4.0
class File (Ruby 2.4.0 リファレンスマニュアル)
File.openを使うときに気をつけたいこと
自分的に気をつけなきゃなーと思ったこと
- ブロックで使用する
- テキストかバイナリかをちゃんと指定する
- エンコードまわりの例外対策をちゃんとしておく
ブロックで使用する
これはもうお決まりというかいろんな書籍でも書いてあることなのだけどうっかりブロック使わずcloseもせず開けっぱなしにしちゃってたので、なるほどこのタイミングかと思い。
f = File.open("testfile", "r")
開けっ放しはダメで、
f = File.open("testfile", "r") f.close
こうでもなく、
f = '' File.open("testfile", "r") do |file| f = file.read end
これがよい。ブロックの外(前)でf
の初期化だけ忘れずに。
テキストかバイナリかをちゃんと指定する
File.openの第二引数ではファイルアクセスのモードを指定する。
大体r(read)かw(write)かは忘れないと思うけど、ちゃんとテキストかバイナリかも指定するクセをつけようと思いました。
読み込みモードで開く
f = File.open("testfile", "r")
読み込みモードでテキストファイルとして開く
f = File.open("testfile", "rt")
この後エンコードしたりする時に改行文字が含まれているとめんどくさいことになりやすいので、ちゃんとテキストファイルとして読み込んであげるのが良さそう。
エンコードまわりをちゃんとしておく
当たり前っちゃ当たり前なのだけど、予期せぬ文字コードのファイルを開こうとした時にエラーになったり文字化けしたりしないようちゃんと書いておく。
f = File.open("testfile", "rt:Shift_JIS:UTF-8", invalid: :replace, undef: :replace, replace: '?')
:invalid => :replace
...変換元のエンコーディングにおいて不正なバイトがあった場合に、不正なバイトを置換文字で置き換える:undef => :replace
...変換先のエンコーディングにおいて文字が定義されていない場合に、未定義文字を置換文字で置き換える:replace => string
...:invalid => :replace
や:undef => :replace
で用いられる置換文字を指定。デフォルトは Unicode 系のエンコーディングならば U+FFFD、それ以外では "?" を使用。
または、
content = File.read("testfile") character_code = NKF.guess(content).to_s f = File.open("testfile", "rt:#{character_code}:UTF-8", invalid: :replace, undef: :replace, replace: '?')
など。
File.read
するとメモリを消費してしまうのでデータ量の多いファイルを扱う場合はどうなんだろうか...と思いつつ何かいい案があれば教えてください。。
おまけ:CSVクラスでのencodeオプション
エンコードまわりのオプションを使いたくて、CSVファイルを扱うCSVクラスでもなんとなく同じことができるかなーと思って試したけどできなかった。
File.open
で使えるinvalid: :replace
とかundef: :replace
とかは使えなくてちょっとハマりました。
CSV.open("testfile", undef: :replace, invalid: :replace, replace: '?') do |row| p row end
=> ArgumentError: Unknown options: undef, invalid, replace.
なのでCSVで上記のオプションを使いつつファイルを扱いたいときは、File.open
の中でCSVの処理をおこなう。
File.open("testfile", "rt:Shift_JIS:UTF-8", invalid: :replace, undef: :replace, replace: '?') do |file| CSV.parse(file) do |row| p row end end
でOK。
で、たとえばencoding
はどちらでも使えるのに、なぜoptionsはFileクラスでは使えてCSVクラスでは使えないんだろう、と思って少し調べてみたものの
- File.openはI/O.openのように振る舞う
- I/O.openのoptionsは
String#encode
に対応している String#encode
でinvalid
やundef
のようなオプションキーが使用できる
というところまでしかわからなかった。
...と思ったのだけど、どうやらCSV3.1.6以降は使えるようになっているらしい。
不正なバイトを置換文字で置き換えるCSV.openオプション - koicの日記
素敵〜。
というか組み込みライブラリのバージョンの調べ方知らないので調べてみたところ
> p CSV::VERSION "2.4.8" => "2.4.8"
ほえ〜。
参考
class File (Ruby 2.4.0 リファレンスマニュアル)
class CSV (Ruby 2.4.0 リファレンスマニュアル)
class IO - Documentation for Ruby 2.4.0