Ruby TIPS

Ruby TIPS

ファイルから1行/段落ごと読み込む(入力する)には?

2017年5月19日

Rubyでテキストファイルから文字列を読み込むための方法として、ファイル内の全テキスト内容を先頭から1行単位ずつもしくは1段落ずつループ処理する方法と、ファイルから読み込んだ全ての行を配列として返す方法を説明する。

ローグ・インターナショナル 羽山 博
  • このエントリーをはてなブックマークに追加

 前々回の「ファイルから文字列を読み込む(入力する)には?(基本編)」では、Rubyでテキストファイルから文字列を読み込むための基本的な方法を解説し、さらにファイル操作をブロックで記述する方法や、読み込み専用モードでファイルを開く方法、文字コードの基本的な取り扱い方を見た。

 前回の「ファイルから1文字ずつ読み込む(入力する)には?」および今回ではその続編として、さまざまな単位でファイルの内容を読み込むメソッドを見ていく。今回は1行ずつ読み込む方法と段落ごと読み込む方法を見ていこう。

ファイル入出力の基本 − 入力編

ファイルから1行ずつ読み込む ― getsメソッド、readlineメソッド

 1行ずつの読み込みには、getsメソッドまたはreadlineメソッドを使う。この2つのメソッドの働きは、基本的には同じだが、ファイルの末尾に達したときにgetsメソッドがnilを返し、readlineメソッドがEOFErrorを発生させるという点だけが異なる。考え方は、前回説明したgetcreadcharメソッドとほぼ同じなので、プログラム例(リスト1.7)と実行例(実行例1.7)だけを示しておこう。ちなみに、リスト1.7のコードでreadlineを試したい場合は、単純に「gets」を「readline」に置換するだけでよい。

sample007.rb
File.open("data001.txt"){|f|
  p f.gets  # 1行目
  p f.gets  # 2行目
  p f.gets  # 3行目
}
リスト1.7 ファイルの先頭から3行読み込んで表示するプログラム

ファイルに含まれる行数が3行より少ない場合、getsメソッドではnilが返されるので、読み込めなかった行数だけnilが表示される。readlineメソッドでは、ファイルの末尾以降の行を読み込もうとした時点でEOFErrorエラーとなる。

ターミナル
$ ruby sample007.rb
"青龍\n"
"朱雀\n"
"白虎\n"
実行例1.7 1行ずつ読み込んで3行分表示する

テキストモードでファイルを開いているので、改行文字がLFCRCRLFのいずれであってもLFとして取り扱われる。

全ての行を1行ずつ読み込む(ブロックを利用) ― each_lineメソッド、foreachメソッド

 全ての行を1行ずつ読み込むには、each_lineメソッドやforeachメソッドが使える。

each_lineメソッド

 まず、each_lineメソッドからプログラム例と実行例を見ていこう。

 each_lineメソッドは、ファイルから1行ずつ読み込み、ブロックパラメーターに読み込んだ行を渡す。

sample008.rb
File.open("data001.txt", mode = "rt"){|f|
  f.each_line{|line|
    p line
  }
}
リスト1.8 ファイルの先頭から1行ずつ、全ての行を読み込んで表示するプログラム

単純に1行ずつ読み込み、その行を表示する。この例では、読み込まれた行はブロックパラメーターのlineに渡される。

 実行例は以下の通り。

ターミナル
$ ruby sample008.rb
"青龍\n"
"朱雀\n"
"白虎\n"
"玄武"
実行例1.8 全ての行を読み込んで表示する

ファイルに含まれる行が1行ずつ表示された。行をそのまま出力するなら、リスト1.8のpメソッドの代わりにprintメソッドを使えばよい。なお、このファイルの末尾には改行文字は入力されていない。行末の改行文字を削除するなら、リスト1.8のlineline.chompとすればよい。

foreachメソッド

 ファイルの内容を1行ずつ読み込むには、Fileクラス(厳密にはその継承元であるIOクラス)の特異メソッドであるforeachメソッドも使える。

 foreachメソッドはeach_lineメソッドとほぼ同じ機能だが、Fileクラスの特異メソッドであるので、前述のリスト1.8のようにFile.openメソッドのブロックパラメーターからメソッド呼び出しをするのではなく、直接、File.foreachという呼び出しになることに注意。このため、リスト1.8と同じ処理を行うのであれば、以下のようにより簡単に書けるというメリットもあるが、openメソッドと違って文字コードやアクセスモードは指定できない(読み込みのみに使える)というデメリットもある。

sample009.rb
File.foreach("data001.txt"){|line|
  p line
}
リスト1.9 Fileクラスのforeachメソッドを使って1行ずつ読み込み、全ての行を表示するプログラム

 実行結果は実行例1.8と同じなので省略する。

段落ごとに読み込む

 ここまでに説明してきたgetsreadlineeach_lineメソッドでは、改行文字としてどの文字を使うかをそれぞれの引数rsに指定できる。改行文字として引数rsnilを指定すると「改行なし」と見なされる。一方、空文字列"")を指定すれば連続する改行が行の区切りと見なされるので、連続する改行を段落として取り扱うようなときに便利である。

 以下は段落を扱うプログラム例と実行例である。

sample010.rb
f = File.open("data002.txt", mode = "rt"){|f|
  f.each_line(rs=""){|line|
    p line.chomp(rs="")
  }
}
リスト1.10 段落を1つずつ読み込んで、全ての段落を表示するプログラム

each_lineメソッドの引数rsに空文字列を指定しているので、連続した改行文字が行の区切りとなる。なお、読み込んだ行の最後には改行文字が付けられたままなので、chompメソッドで行末の改行文字を取り除いている。chompメソッドでも、改行文字として空文字列を指定すると連続する改行文字が取り除かれる。

ターミナル
$ more data002.txt
これが第一段落です。

段落と段落の間は改行文字2つで区切られています。

したがって、この行と
この行は同じ段落になります。
$ ruby sample010.rb
"これが第一段落です。"
"段落と段落の間は改行文字2つで区切られています。"
"したがって、この行と\nこの行は同じ段落になります。"
実行例1.9 全ての段落を読み込んで表示する

最初に、このプログラムで読み出すdata002.txtファイルの内容を確認しておく。段落は3つ。最後の2行は途中で改行されているが、改行文字は1つしか入っていないので、同じ段落と見なされる。

ファイルから読み込んだ行を配列として返す ― readlinesメソッド

 ファイルから1行ずつ読み込むのに使われるreadlineメソッドと名前の似たreadlinesメソッドも利用できる(リスト1.11)。ただし、readlinesメソッドは、読み込んだ行を配列として返す。

sample011.rb
s = []
File.open("data001.txt", mode = "rt"){|f|
  s = f.readlines
}
p s
リスト1.11 ファイルから全ての行を読み込み、行を配列として返すプログラム

readlinesメソッドに引数を指定しなければ、全ての行が読み込まれ、配列として返される。引数limitを指定すると、指定したバイト数だけ読み込まれる。

 実行例は以下の通り。

ターミナル
$ ruby sample011.rb
["青龍\n", "朱雀\n", "白虎\n", "玄武"]
実行例1.10 readlinesメソッドにより読み込んだ行を配列に変換する

ファイルの全ての行が配列として返された。行の末尾にある改行文字は削除されずに返されることに注意。

 readlinesメソッドでは、行の末尾にある改行文字も返されてしまう。改行文字を取り除きたければ、getsgetlineeach_lineforeachメソッドのいずれかにより1行ずつ読み込んで、chompメソッドで改行文字を削除して配列に追加するとよい。具体的にはリスト1.12のようなコードになる。

sample012.rb
s = []
File.foreach("data001.txt"){|line|
  s << line.chomp
}
p s
リスト1.12 1行ずつ読み込み、行末の改行文字を削除して、配列に追加するプログラム

<<は配列に要素を追加するメソッド。chompメソッドを使って文字列の末尾にある改行文字を削除する。

 実行例は以下の通り。

ターミナル
$ ruby sample012.rb
["青龍", "朱雀", "白虎", "玄武"]
実行例1.11 読み込んだ行の行末から改行文字を取り除いて配列に変換する

改行文字が削除されたので、各行の内容だけが配列に追加された。実行例1.11と比べてみるとよい。

まとめ

 getsメソッドとreadlineメソッドは1行読み込むのに使う。each_lineメソッドやFileクラスの特異メソッドであるforeachメソッドでは、読み込んだ各行を、ブロックを使って処理できる。readlinesメソッドは、読み込んだ行を配列として返す。

処理対象:文字列の読み込み(入力) カテゴリ:ファイル入出力
API:IOクラス|Fileクラス カテゴリ:組み込みライブラリ
API:getsメソッド|readlineメソッド|each_lineメソッド|foreachメソッド|readlinesメソッド カテゴリ:IOクラス

※以下では、本稿の前後を合わせて5回分(第19回~第23回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

19. ファイルから文字列を読み込む(入力する)には?(基本編)

テキストファイルから文字列を読み込むための基礎を解説。ファイル操作をブロックで記述する方法や、ファイルを開く際に「テキスト読み出し専用モード」でアクセスしたり文字コードを指定したりする方法、BOM付きファイルを処理する方法を説明する。

20. ファイルから1文字ずつ読み込む(入力する)には?

Rubyでテキストファイルから文字列を読み込むための方法として、ファイルから1文字単位で文字を取得する方法と、ファイル内の全テキスト内容を先頭から1文字ずつループ処理する方法を説明する。

21. 【現在、表示中】≫ ファイルから1行/段落ごと読み込む(入力する)には?

Rubyでテキストファイルから文字列を読み込むための方法として、ファイル内の全テキスト内容を先頭から1行単位ずつもしくは1段落ずつループ処理する方法と、ファイルから読み込んだ全ての行を配列として返す方法を説明する。

22. ファイルに文字列を書き込む(出力する)には?

テキストファイルに文字列を書き込むための基本を解説。新規書き込みと追加の方法を確認した後、任意の位置から書き込む方法や読み書き両用モードでファイルを利用する方法を説明する。

23. ファイルの排他制御を行うには? その際のデッドロック問題とは?

1つのファイルに複数のプログラムから同時アクセスすると、上書きによりデータが消失する可能性がある。これを回避するために排他制御を行う方法と、その際に問題となるデッドロックを回避する方法について説明する。

サイトからのお知らせ

Twitterでつぶやこう!


Build Insider賛同企業・団体

Build Insiderは、以下の企業・団体の支援を受けて活動しています(募集概要)。

ゴールドレベル

  • 日本マイクロソフト株式会社
  • グレープシティ株式会社