やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

組込ライブラリ(File)

 ファイル操作する。

成果物

情報源

File

ファイルアクセスのためのクラスです。

通常 Kernel.#open または File.open を使って生成します。 IO クラスがインクルードしている File::Constants は File クラスに関係する定数を格納したモジュールです。また File::Stat は stat 構造体( stat(2) 参照)を表すクラスです。

メンバ抜粋

特異メソッド

absolute_path absolute_path? atime basename birthtime blockdev? chardev? chmod 
chown ctime delete directory? dirname empty? executable? executable_real? 
exist? exists? expand_path extname file? fnmatch fnmatch? ftype grpowned? 
identical? join lchmod lchown link lstat lutime mkfifo mtime new open owned? 
path pipe? readable? readable_real? readlink realdirpath realpath rename setgid? 
setuid? size size? socket? split stat sticky? symlink symlink? truncate umask 
unlink utime world_readable? world_writable? writable? writable_real? zero?

インスタンスメソッド

atime birthtime chmod chown ctime flock lstat 
mtime path size to_path truncate

定数

ALT_SEPARATOR PATH_SEPARATOR SEPARATOR Separator
系統 メンバ
生成 new, open, mkfifo
削除 delete/unlink
権限 chmod/lchmod, chown/lchown
パス absolute_path, absolute_path?, expand_path, path, realdirpath, realpath, to_path, SEPARATOR, basename, dirname, join, rename, split
メタデータ stat/lstat, size, atime/ctime/mtime/birthtime

new, open

new(path, mode = "r", perm = 0666) -> File
open(path, mode = "r", perm = 0666) -> File
open(path, mode = "r", perm = 0666) {|file| ... } -> object

 newで開く。

f = File.new("testfile", "r")
f.class # => File
f.close

 openで開く。

f = File.open("testfile", "r")
f.class # => File
f.close

 openで読み書き。ブロック終端で自動的に閉じるためclose不要。

File.open("testfile", "w", 0755) { |f| f.print "test" }
File.read("testfile")  # => "test"

 動作させてみて次のようなことがわかった。

モード 状態 結果
r 指定ファイルがない No such file or directory @ rb_sysopen - testfile (Errno::ENOENT)
w 指定ファイルがない 新規ファイル生成
r 指定ファイルがある 読込モードで開く
w 指定ファイルがある 上書きモードで開く(ファイルポインタ先頭)
mode 概要
r
r+ 読書(ファイルがないときエラー)
w
w+ 読書(ファイルがないとき新規作成)
a 追記
a+ 追記+読
rb,r+b,wb,w+b,ab,a+b バイナリ

dirname, basename, extname, split

p File.dirname('/tmp/0.0.1.rb')        # /tmp
p File.basename('/tmp/0.0.1.rb', '.*') # 0.0.1
p File.extname('/tmp/0.0.1.rb')        # .rb

 一発でいい感じに分割できない。分割したいパターンがたくさんあるから。

パターン メソッド
/ごとに分割
ディレクトリ/全ファイル名 File.split
ディレクトリ/名前/拡張子(.あり)
ディレクトリ/名前/拡張子(.なし)

 /ごとに分割・結合するのは以下。File.expand_path, String.split, Array.joinを使う。

# 分割
p File.expand_path(__FILE__).split(File::SEPARATOR)
# 結合
p File.expand_path(__FILE__).split(File::SEPARATOR).join(File::SEPARATOR)

 先祖ディレクトリとファイル名に分割するなら一発。

d, f = File.split('/tmp/dir/0.0.1.txt') # ["/tmp/dir", "0.0.1.txt"]

 ファイル名と拡張子に分割するのは厄介。以下が最短。

p File.basename('/tmp/dir/0.0.1.rb', '.*') # 0.0.1
p File.extname('/tmp/dir/0.0.1.rb')        # .rb

 basenameの第二引数はサフィックス指定。それに一致するなら、それを取り除いた文字列を返す。

 私としては以下のように配列で分割して欲しかった。そしたらあとは多重代入で完了できたのに。

name, ext = ... # ['0.0.1', '.rb']

 もっといえば、以下のように分割して欲しかった。

dirs, name, ext = ... # ['/tmp/dir/', '0.0.1', '.rb']
['/tmp/dir/', '0.0.1', '.rb'].join # /tmp/dir/0.0.1.rb

 拡張子だけ変えるとか、拡張子をのぞいた名前のところだけ変えるとか、そういうときは少し面倒くさいことになりそう。

expand_path, absolute_path

メソッド 展開 絶対パス
expand_path
absolute_path

 expand_pathを常用すべき。

所感

 細かいメソッドは必要に迫られたときに勉強しよう。

対象環境

$ uname -a
Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux