Ruby基礎復習(8) Fileクラス

『パーフェクトRuby』p.196より。わりと苦手な分野。

まずはファイルをひらく。#openして変数に格納してもいいし、ブロックを引き渡して処理させることもできる。後者の場合は処理が終わると自動でクローズしてくれるので、こっちの方が楽っぽい。#readはファイルの内容全体を読み込む一方、#getsを使うと1行ずつ読み込むことができる。あるいは#each_line#each_charといったメソッドも。

 1file = File.open('example.txt')
 2p file.read # example.txtの内容を表示
 3file.close
 4
 5File.open 'example.txt' do |file|
 6  p file.read
 7end
 8
 9File.read('example.txt')
10
11File.open 'example.txt' do |file|
12  while line = file.gets
13    p line
14  end
15end
16
17File.open 'example.txt' do |file|
18  f.each_line do |line|
19    p line
20  end
21end

書き込むときは#openの第二引数にファイルを開くモードを指定する。デフォルトは'r'、すなわち読み込みモードで、他は以下の通り。基本はrが読み込み、wが書き込み、aが追記で、+を付けると読み書き両用モードになる。またbを後置するとバイナリモードで開かれる。

| r | 読み込みモード | | r+ | 読み書き両用モード(読み書き位置は先頭から) | | w | 上書き書き込みモード | | w+ | 新規作成して読み書き両用モード | | a | 追記書き込みモード | | a+ | 追記読み書き両用モード(読み込み位置は先頭から、書き込みは追記形式) |

1File.open 'example.txt', 'w' do |f|
2  f.write 'hoge'
3end

もっと単純に#writeメソッドだけでも書き込み可能。

1File.write 'example.txt', 'fuga'

先のファイルを開くモードの話の中で「読み込み位置は先頭から」という表現があったが、IOオブジェクトではファイル内の今どこを読み/書きしているかというアクセス位置が存在する。#getsでは1行ずつ読み込みを行ったように、読み/書きを行うことでアクセス位置は進んでいく。先頭まで戻りたい場合は#rewindを使う。また#seekメソッドは第二引数に定数で指定した基準位置より、第一引数の整数分アクセス位置を移動させることができる。#posは絶対的にアクセス位置を指定して動かせる。

 1File.open 'example.txt' do |f|
 2  f.puts
 3  f.rewind # 先頭位置まで戻る
 4
 5  f.seek 10 # 先頭から10進む
 6  f.seek -10, IO::SEEK_END # 末尾(SEEK_END)から10戻った位置に移動
 7
 8  f.pos = 25 # 先頭から25バイト目に移動
 9  f.pos # => 25
10end

文字のエンコーディングについては、「外部」と「内部」という概念を持つ。外部はファイルのエンコーディング情報であり、内部はRuby上で処理する際のエンコーディング情報。例えばEUC-JPのファイルをutf-8で変換して取り扱い、書き込みはEUC-JPで、といったことができる。エンコーディングの設定には#set_encodingメソッドを使う。引数を1つだけ取る場合は外部エンコーディングを設定し、2つ取る場合は第一引数が外部、第二引数が内部を設定する。あるいはFile#openするときに、読み書きモードと一緒にエンコーディングも指定することができる。

 1File.open 'example.txt' do |f|
 2  f.set_encoding('utf-8') # 外部エンコーディングをutf-8に設定
 3
 4  f.set_encoding('utf-8', 'EUC-JP') # 外部エンコーディングをutf-8、内部エンコーディングをEUC-JPに設定
 5  f.set_encoding('utf-8:EUC-JP') # 外部エンコーディングをutf-8、内部エンコーディングをEUC-JPに設定
 6end
 7
 8File.open 'example.txt', 'r:utf-8:EUC-JP' do |f|
 9  p f.external_encoding # => "utf-8"
10  p f.internal_encoding # => "EUC-JP"
11end

ファイルのロックには#flockメソッドを利用する。ロックのモードはここに記載の定数を使って指定するのだが、主にFile::LOCK_EXが排他ロックであることを覚えとけばいいような気も。

1File.open 'example.txt', 'w' do |f|
2  f.flock File::LOCK_EX
3end

その他、ファイル情報取得系のメソッドをつらつらと。これらはファイルオブジェクトから取得するだけではなく、File.atime(filename)の形でFileクラスのクラスメソッドでも呼び出すことができる。

 1File.open 'example.txt' do |f|
 2  f.atime # 最終アクセス日時
 3  f.ctime # 最終変更日時
 4  f.mtime # 最終更新日時
 5
 6  f.size # ファイルサイズ
 7
 8  f.ftype # ファイルタイプ 以下真偽判定メソッドも有り
 9  f.file?
10  f.directory?
11  f.symlink?
12
13  f.writable? # => false
14  f.readable? # => true
15  f.executable? # => false
16
17  f.owned? # => false (自身がファイル所有者か?)
18  f.gid # ファイル所有者のGID
19  f.uid # ファイル所有者のUID
20end

ファイル操作系。

 1# ファイル名変更、ファイル移動
 2File.rename 'hoge', 'fuga'
 3File.rename 'hoge', 'dir/hoge'
 4
 5# ファイル削除
 6File.unlink 'hoge'
 7
 8# シンボリックリンク作成
 9File.symlink 'target', 'link'
10
11# ハードリンク作成
12File.link 'target', 'link'
13
14# ファイルモード変更
15File.chmod 0600, 'filename'
16
17# 所有者、グループの変更
18File.chown 100, 100, 'filename'

ファイルパスに関するもろもろ。

 1# ファイルのあるディレクトリパスの取得
 2File.dirname("etc/sample.txt") # => "/etc"
 3
 4# 第一引数に与えたファイルパスに対する、ファイル名の取得。第二引数でsuffix指定。
 5File.basename("etc/sample.txt") # => "sample.txt"
 6File.basename("etc/sample.txt", ".txt") # => "sample"
 7
 8# 拡張子の取得
 9File.extname("etc/sample.txt") # => ".txt"
10
11# ファイルパスの連結(引数は可変長)
12File.join("/usr/local", "bin/ruby") # => "/usr/local/bin/ruby"
13
14# ファイルパスからdirnameとbasenameを取得し配列生成
15File.split("/usr/local/bin/ruby") # => ["/usr/local/bin", "ruby"]
16
17# 絶対パスの展開
18File.expand_path("~") # => "/home/chroju"
19File.expand_path("filename", "~") # => "/home/chroju/filename"
20
21# absolute_pathでは~を展開しない
22File.absolute_path("~") # => "/home/chroju/~"

Dirクラスも触れたいのだが、長くなるので一旦ここまで。