「Rubyベストプラクティス」を読む4
4章はファイルやテキスト処理、正規表現をあつかっています。
目次:4章 テキスト処理とファイル管理
4.1 状態トラッキングによる行指向のファイル処理
テキスト処理について、パーサーをつくる一つの方法が紹介されています。
# 本に載っているパーサーのコードを一部簡単にして書く def parse(file_name) # 状態を保持する配列 section = [] File.foreach(file_name) do |line| # 状態を変更する処理 # 状態はスタックで管理する case line when /^Start(\w+)/ section.push $1 next when /^End(\w+)/ section.pop next end # 現在の状態で行う処理を決める case section when ["Font", "Metrics"] parse_char(line) when ["Kern"] parse_kern(line) when ["Metric"] next # 処理しなくてもよいときは、nextで飛ばす else parse_generic(line) # その他一般の処理 end end end
4.2 正規表現
アンカーや量指定子の使い方が解説されている。
4.3 ファイルを扱う
テンポラリファイルを使う必要があるときは、自作するよりtempfileライブラリを使う。
require 'tempfile' temp = Tempfile.new('foo.txt') temp << some_data # 読み込むときは、rewindで巻き戻す temp.rewind temp.each do |line| # 何かする end temp.close # テンポラリファイルをすぐに削除したいときは、Tempfile#close! を使う。 # temp.close! # テンポラリディレクトリを指定することもできる a = Tempfile.new("foo.txt", "path/to/tmpdir")
File.foreachはEnumeratorを返すので、テキスト処理にEnumerableのメソッドが使える
sum = 0 File.foreach("data.txt"){|line| sum += line[/total: (\d+)/, 1].to_f} # Enumeratorを使った処理 enum = File.foreach("data.txt") sum = enum.inject(0){|s, r| s + r[/total: (\d+)/, 1].to_f}
ファイル処理をしていて、現在の行数を知りたいときは、File#linenoを使う
keys = [] values = [] File.open("foo.txt") do |file| file.each do |line| (file.lineno.odd? ? keys : values) << line.chomp end end Hash[*keys.zip(values).flatten]
ファイルに修正を施して保存する場合、"r+"でファイルを開いて処理するよりも、テンポラリファイルに処理結果を書いておいて、最後にリネームするやり方のほうが簡単。
require "tempfile" require "fileutils" file_name = "data.txt" temp = Tempfile.new("working") File.foreach(file_name) do |line| # 何かする temp << result end temp.close # 安全のため、ファイルをバックアップしておく FileUtils.cp(file_name, "#{file_name}.bak") FileUtils.mv(temp.path, file_name)