📂

Rubyでパス名をオブジェクトっぽく使う【Pathname】

2021/02/20に公開

大体下記の進化版
https://zenn.dev/kunosu/articles/7e01a31644575d0d50c2

Pathnameオブジェクトを作る

引数にパス文字列を渡すだけ。ワイルドカードでも可能
パスの存在有無は問わない

require 'pathname'

home_path_str = "C:/Users/xxx"
home_path = Pathname.new(home_path_str)
#home_path = Pathname(home_path_str)	# こっちでも可

puts home_path.to_s	# => C:/Users/xxx

ただし、区切り文字は/(スラッシュ)にする、文字コードをUTF-8にする必要がある
-> Ruby 3.1.1では不要。ここではスラッシュのままにする

パスを連結する

おそらく一番便利なやつ。
普通の文字列で複数連結しようとすると長くなってしまうが、Pathnameなら/で自然に繋げられる

home_path_str = "C:/Users/xxx"
#home_path_str = "C:/Users/xxx/"	# 最後に/がついていても結果は同じ
home_path = Pathname.new(home_path_str)

input_path = home_path / "yyy" / "zzz"	# 連結
puts input_path.to_s	# => C:/Users/xxx/yyy/zzz

指定パスが存在するかチェック

真偽を返す。パスがワイルドカード指定だとうまく行かない

メソッド trueを返す条件
path.exist? pathが存在する
path.directory? pathがディレクトリ
path.file? pathがファイル
home_path = Pathname.new("C:/Users/xxx")
# !FileTest.exist?(home_path.to_s)と同等
if !home_path.exist?
	raise ArgumentError, "パス #{home_path} は存在しません"
end

パスがワイルドカード指定の場合

file_path_str = "C:/Users/xxx/test*.txt"
# Dir.glob(file_path_str).empty? と同等
if Pathname.glob(file_path_str).empty?
	raise ArgumentError, "パス #{file_path_str} が正しくありません"
end

ディレクトリ配下のファイルをすべて検索

※パスが存在しない場合はループに入らない(エラーも出ない)

home_path = Pathname.new("C:/Users/xxx")
# Dir.glob("#{home_path.to_s}/**/*") と同等
Pathname.glob(home_path / "**/*") do |path|
	next if path.directory?	# ディレクトリは無視
	next if path.to_s.include?("tmp")	# パス名に"tmp"を含むファイルを無視

	puts path
end

例:すべての空ディレクトリに.gitkeepファイルを作成する

home_path = Pathname.new("C:/Users/xxx")
Pathname.glob(home_path / "**/*") do |path|
	next if path.file?
	next if path.to_s.include?(".git")

	if path.empty?
		puts path
		FileUtils.touch(path / '.gitkeep')
	end
end

直下のファイルだけを取得

再帰的には検索しない

home_path = Pathname.new("C:/Users/xxx")
home_path.each_child do |path|
	next if path.directory?	# ディレクトリは無視
	puts path
end
# パスが存在しないときは Errno::EXXX: エラーが発生

ファイル名・ディレクトリ名を取得

File.basename と同じ

path = Pathname.new("C:/Users/xxx/test.txt")
file_name = path.basename
# => test.txt
path = Pathname.new("C:/Users/xxx")
file_name = path.basename
# => xxx

拡張子を取得

File.extname と同じ

path = Pathname.new("C:/Users/xxx/test.txt")
ext_name = path.extname
# => .txt

ファイルを読み込む

※パスがワイルドカードのときはエラー(順番に読み込んではくれない)

まとめて読み込む

# test.txt は UTF-8 で書かれている
test_path = Pathname.new("test.txt")
test_path.open(mode = "r", :external_encoding => "UTF-8") do |file|
	puts file.read	# 全部出力
end

1行ずつ(色々方法がある)

test_path.open(mode = "r", :external_encoding => "UTF-8") do |file|
	file.each_line do |line|
		puts line
	end
end

# もしくは
test_path.each_line(:external_encoding => "UTF-8") do |line|
	puts line
end

行番号もほしいとき

test_path.open(mode = "r", :external_encoding => "UTF-8") do |file|
	file.each_line do |line|
		puts "#{file.lineno}行目:#{line}"
	end
end

test_path.each_line(:external_encoding => "UTF-8").with_index(1) do |line, line_num|
	puts "#{line_num}行目:#{line}"
end

テキストファイルの行数を取得

line_no = test_path.open do |file|
	while file.gets;end	# 最終行まで読み捨てる
	file.lineno	# 最終行の行番号を出力
end

puts line_no

Discussion