📂

Rubyでのディレクトリ・ファイル操作

2021/02/19に公開約4,000字
  • 環境
    • Windows10
    • Ruby 3.1.1

もっと便利なPathnameクラス

https://zenn.dev/kunosu/articles/379ee3eedc413c07de6f

引数でパスを渡したとき

  • パスの最後に\(円マーク)はつけてもつけなくてもいい
    • パスをダブルクオーテーションで囲うときは、エスケープ扱いにならないよう最後に円マークはつけないこと
呼び出し時.bat
ruby test.rb "C:\Users\xxx"

以下はRuby 3.1.1では不要(変換しなくてもそのままで動作する)

  • 区切り文字は /(スラッシュ)に置換する
    • \(円マーク)だとエスケープ文字扱いで検索などがうまく行かない
  • 文字コードをutf-8にする
dir_path = ARGV[0]
puts dir_path # => C:\Users\xxx

dir_path = ARGV[0].gsub("\\", "/").encode(Encoding::UTF_8)
puts dir_path # => C:/Users/xxx

カレントディレクトリの取得

pwd = Dir.pwd

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

真偽を返す

メソッド trueを返す条件
FileTest.exist?(path) pathが存在する
FileTest.directory?(path) pathがディレクトリ
FileTest.file?(path) pathがファイル
if !FileTest.exist?(path)
	raise ArgumentError, "パス #{path} は存在しません"
end

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

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

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

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

# test以下のすべてのファイルを検索
Dir.glob("#{test}/**/*") do |path|
	next if FileTest.directory?(path)	# ディレクトリは無視
	puts path
end

Dir.glob の引数はワイルドカードで指定できる

ディレクトリ配下の拡張子付きファイル数を数える

file_num = Dir.glob("#{test}/**/*.*").count

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

一番最後のスラッシュに続く文字列を切り出している

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

拡張子を取得

拡張子がない場合は空の文字列

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

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

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

puts line_no

ファイルを1行ずつ読み込む

# test.txt は UTF-8 で書かれている
File.foreach("test.txt", :external_encoding => "UTF-8") do |line|
	line.chomp!	# 改行削除
	next if (line == "")	# 空行を無視

	puts line
end

行番号もほしいとき

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

xml形式のファイルを読み込む

下記のファイルから123,456,789を読み込む

<list>
	<id>123</id>
	<id>456</id>
	<id>789</id>
</list>
require 'rexml/document'

File.open("test.xml", :external_encoding => "UTF-8") do |file|
	xml = REXML::Document.new(file)
	xml.elements.each("list/id") do |element|
		puts element.text
	end
end

下記のファイルから123,456,789を読み込む

<list>
	<a id=123>
	<a id=456>
	<a id=789>
</list>
require 'rexml/document'

File.open("test.xml", :external_encoding => "UTF-8") do |file|
	xml = REXML::Document.new(file)
	xml.elements.each("list/a") do |element|
		# Hash形式で取得{"id" => 123}
		hash = element.attributes
		puts hash["id"]
	end
end

csv形式のファイルを読み込む

一行ずつ配列として読み込む

require "csv"

CSV.foreach("test.csv", encoding: "UTF-8:UTF-8") do |csv_line|
	csv_line.each do |cell|
		puts cell
	end
end

行番号・列番号付き

require "csv"

CSV.foreach("test.csv", encoding: "UTF-8:UTF-8").with_index(1) do |csv_line, line_num|
	csv_line.each_with_index do |cell, column_num|
		puts "#{line_num}#{column_num + 1}列目:#{cell}"
	end
end

csv形式のファイルとして出力

require "csv"

csv_arr = [[1, 2, 3], [4, 5, 6]]

CSV.open("test.csv", "w" ,:encoding => "UTF-8") do |csv|
	# 1行ずつ出力
	csv_arr.each do |row|
		csv << row
	end
end

下記が出力される

1,2,3
4,5,6

ファイルをコピーする

同名ファイルがある場合は上書きする

require 'fileutils'

src_path = "C:/..." # コピー元のパスを指定
dest_path = Dir.pwd

# src_path にあるファイルをカレントディレクトリへコピーする
FileUtils.copy(src_path, dest_path)

テキストファイルの文字コードを取得する

一回全部読み込んでからNKFで判定する

require 'nkf'

txt_path_str = "test.txt"
file = File.read(txt_path_str)
file_encode = NKF.guess(file).to_s	# => UTF-8など

File.open(txt_path_str, :external_encoding => file_encode) do |file|
	# 文字コードに合わせて開く
end

テキストファイルの改行コードがCR+LFか判定する

# 改行コードが変換されないようバイナリで開く
File.open("test.txt", "rb") do |file|
	# 改行コードがCR+LFか判定
	if file.read.include?("\r\n")
		# -> CR+LFの改行コードがある
	end
end

Discussion

ログインするとコメントできます