RubyのFile/IOクラスで入力と出力、ファイルの読み取りと書き込み、フィルター作成:若手エンジニア/初心者のためのRuby 2.1入門(10)(2/2 ページ)
オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。今回は、Kernelモジュールに定義されている主な入出力メソッドとしてprint、puts、printf、gets、File/IOクラス、STDOUT、STDINの使い方、フィルターの作り方などを解説。
Fileクラスでファイルの読み取りと書き込み
ファイルに対する入出力を行いたい場合など、高度な入出力操作が必要な場合は、標準ライブラリに含まれるIOクラスが役に立ちます。操作対象がファイルであっても標準入出力であっても、統一されたインターフェースを通じてデータを操作できます。
後述しますが、FileクラスはIOクラスを継承しています。まずは、Fileクラスの使い方について解説します。
Fileクラスを使ってファイルを読み取る
ファイルを読み込んで処理する基本的な例を、io04.rbに示します。
file = File.open("wonderland.txt", "r") file.each do |line| puts "** #{line.chomp} **" end file.close
$ ruby io04.rb ** Alice ** ** White Rabbit ** ** Cheshire Cat ** ** March Hare **
1行目でFileクラスのインスタンスを生成しています。FileクラスはIOクラスを継承しているクラスなので、入出力をつかさどるIOクラスを、ファイルに対する入出力に特化させたものです。
「open」メソッドの第1引数にはファイル名を、第2引数には、ファイルの読み書きモードを指定します。
ここでは、読み込むだけでよいので読み取り専用を表す「"r"」を指定しています。ファイルに書き込む場合には「"w"」を指定します。モードの詳細については、Ruby公式の「module function Kernel.#open」が参考になります。
参照先のページにあるように、Kernelモジュールにも「open」というメソッドが定義されているので、io04.rbの1行目は「file = open("wonderland.txt", "r")」とも書けます。こちらの方がスッキリしていますが、ここではIOクラスやFileクラスを利用していることを意識するために、あえてKernelモジュールの「open」を使わずに説明しています。
io04.rbの3〜5行目では、「each」メソッドを利用してwonderland.txtを1行ずつ変数lineに読み取り、io03.rbのようにアスタリスクを付加して標準出力に流しています。Fileオブジェクトを使う場合でも、取り出してきた1行には改行が含まれているので「line.chomp」としていることに注意してください。
7行目では「close」メソッドを使ってファイルを閉じています。
ファイルは開いたら必ず閉じることを忘れないでください。もし閉じ忘れてもオペレーティングシステムがよしなにしてくれる場合がほとんどですが、ファイルは必ず閉じるべきです。それでもプログラマーは人間ですから、ファイルを閉じ忘れることもあります。しかし「open」メソッドはブロックを引数として受け取ってくれるので、io04.rbと同等の処理をio05.rbのように記述することもできます。
File.open("wonderland.txt", "r") do |file| file.each do |line| puts "** #{line.chomp} **" end end
$ ruby io05.rb ** Alice ** ** White Rabbit ** ** Cheshire Cat ** ** March Hare **
「open」メソッドのブロック引数にはFileオブジェクトが格納されるので、ブロック内でファイルの読み書きを行えます。io05.rbの2〜4行目では、io04.rbと同様にファイルから1行ずつ読み込んで標準出力に流しています。そしてブロックの終端に届いた時点で開いたファイルは自動的に閉じられるので、プログラマーは明示的に「close」メソッドを呼ぶなどの作業をしなくてもよくなります。
Fileクラスを使ってファイルに書き込む
ここまで、ファイルを読み込む例を見てきました。もちろん、Fileオブジェクトを通して文字を書き込むこともできます。io06.rbにファイルに書き込む例を示しましょう。
File.open("teapot.txt", "w") do |file| file.print("darjeeling tea\n") file.puts("assam tea") file.printf("%s\n", "ceylon tea") end
$ ruby io06.rb $ cat teapot.txt darjeeling tea assam tea ceylon tea
1行目で、ファイルを読み込む場合と同様、「open」メソッドを使ってファイルを開いています。ただし、今回はファイルに書き込みを行うため、モードに「"w"」を指定しています。
変数fileにはFileオブジェクトが格納されるので、後は「print」「puts」「printf」といったおなじみのメソッドでファイルに書き込めます。2〜4行目が実際にファイルに書き込んでいる部分です。
IOオブジェクトに対する入出力
ここまでの説明で、Fileクラスはファイル操作に特化した入出力クラスであることを確認しました。ファイルに限らず、何らかの入出力が可能なものを表すクラスがIOクラスです。
FileクラスはIOクラスを継承しています。このような関係があるために、相手がファイルであっても一般的な入力や出力であっても、同じようなインターフェースを通してデータのやりとりができます。
標準出力を表す「STDOUT」、標準入力を表す「STDIN」
最も身近なIOオブジェクトといえば、定数として定義されている標準出力を表す「STDOUT」や標準入力を表す「STDIN」でしょう。pryを使ってそれらのIOオブジェクトを確認してみましょう。
[1] pry(main)> STDOUT => #<IO:<STDOUT>> [2] pry(main)> STDIN => #<IO:<STDIN>> [3] pry(main)> $stdout => #<IO:<STDOUT>> [4] pry(main)> $stdin => #<IO:<STDIN>>
[1]や[2]でIOオブジェクトが得られていることを確認してください。また、[3]と[4]のように、グローバル変数「$stdout」「$stdin」を通して標準出力や標準入力のIOオブジェクトを取得することもできます。
標準入力から受け取ったデータを加工して標準出力に流す「フィルター」
これらのIOオブジェクトを活用すれば、IOクラスに定義された豊富なメソッドを利用できます。io07.rbに標準入力から入力された文字列を1行ずつ読み、行番号を付与して標準出力に流す例を示しましょう。
STDIN.each.with_index(1) do |line, i| puts "#{i}: #{line.chomp}" end
$ ruby io07.rb < wonderland.txt 1: Alice 2: White Rabbit 3: Cheshire Cat 4: March Hare
「STDIN」は標準入力を表すIOオブジェクトなので、「each」メソッドを用いてEnumeratorオブジェクトを得ることができます。Enumeratorオブジェクトでは「with_index」メソッドが使え、ブロック変数「line」に読み込んだ行の内容、「i」にインデックスが入ります。このとき、「with_index」メソッドの引数に1を与えているので、初期値が1になり、io07.rbの実行例のような出力を得ることができます。
「STDIN」「STDOUT」が真価を発揮するのは、他のコマンドとパイプでつないで利用するようなケースです。例えば、以下のように実行することで、「ls」コマンドの実行結果に行番号を付与し、さらにリダイレクトによってls_with_linenum.txtにその結果を出力できます。
$ ls -al | ruby io07.rb > ls_with_linenum.txt $ cat ls_with_linenum.txt 1: total 56 2: drwxr-xr-x 14 flost staff 476 Nov 21 14:18 . 3: drwxr-xr-x 15 flost staff 510 Nov 21 09:04 .. 4: -rw-r--r-- 1 flost staff 45 Nov 21 11:16 io01.rb 5: -rw-r--r-- 1 flost staff 51 Nov 21 11:23 io02.rb 6: -rw-r--r-- 1 flost staff 51 Nov 21 11:43 io03.rb 7: -rw-r--r-- 1 flost staff 106 Nov 21 13:09 io04.rb 8: -rw-r--r-- 1 flost staff 106 Nov 21 13:27 io05.rb 9: -rw-r--r-- 1 flost staff 137 Nov 21 13:40 io06.rb 10: -rw-r--r-- 1 flost staff 71 Nov 21 14:08 io07.rb 11: -rw-r--r-- 1 flost staff 0 Nov 21 14:19 ls_with_linenum.txt 12: -rw-r--r-- 1 flost staff 5 Nov 21 10:32 rabbit.txt 13: -rw-r--r-- 1 flost staff 14998 Nov 21 14:18 ruby_21_guide_10.md 14: -rw-r--r-- 1 flost staff 36 Nov 21 13:40 teapot.txt 15: -rw-r--r-- 1 flost staff 43 Nov 21 11:26 wonderland.txt
原稿執筆時の筆者の作業ディレクトリに含まれているファイルの一覧が、行番号とともにls_with_linenum.txtに格納されています。
このように、標準入力から受け取ったデータを加工して標準出力に流すようなプログラムを、一般的に「フィルター」と呼びます。
UNIX系OSでは豊富なフィルターが用意されています。実は、io07.rbのようなプログラムを使わずとも、「nl」というコマンドを使えば行番号を付与できます。他にも文字コードを変更するための「nkf」や、ソートに使える「sort」、重複する行を取り除く「uniq」など、いろいろなフィルターを利用できます。
練習課題として、「sort」コマンドや「uniq」コマンドをRubyで再実装してみるのも面白いでしょう。
もし標準のコマンドでは実現できないようなテキスト処理があっても、「STDIN」「STDOUT」を活用することでRubyを使ったオリジナルのフィルターを簡単に作ることができます。ぜひ、日々の業務の効率化に活用してください。
次回は、スレッド、ファイバー、プロセス
今回は、Rubyから入出力を行う方法を学びましたが、いかがでしたでしょうか。次回は、スレッド、ファイバー、プロセスといったトピックについて解説します。UNIX系OSに慣れた方はお分かりだと思いますが、特にプロセスについては、入出力とも密接に関連しています。
- Rubyで逆ポーランド変換機を作りgem作成&コマンドの使い方
- 難しいが強力! Rubyのメタプログラミング、self、特異クラス/メソッド、オープンクラスとモンキーパッチ
- RubyのThread、Fiber、Kernel、forkで並列処理やプロセスの深淵へ
- RubyのFile/IOクラスで入力と出力、ファイルの読み取りと書き込み、フィルター作成
- Rubyの例外とその捕捉――基本のbegin〜rescue〜endからensure、else、retry、後置rescueまで
- Rubyの面白さを理解するためのメソッド、ブロック、Proc、lambda、クロージャの基本
- Rubyのオブジェクト指向におけるクラスとモジュール、継承、Mixin、アクセス制御の使い方
- RubyのNumericとTimeで数値と時間をさまざまな操作・演算・判定
- RubyのString/Regexpクラスによる強力な文字列操作/正規表現
- RubyのRangeクラスと範囲オブジェクト、範囲演算子、イテレーターの使い方
- Rubyの配列、ハッシュテーブルを表現するArray、Hashクラスの使い方
- Ruby 2.1の基本構文/基本文法まとめ&Pryの使い方
- Rubyプログラミングを始めるための基礎知識とインストール
著者プロフィール
麻田 優真(Rails技術者認定シルバー試験問題作成者)
イタリア、ローマ生まれ。中学生のころHSPに初めて触れ、プログラミングの楽しさを知る。オープンソースやハッカーカルチャーを好む。C#からRubyに転向したときに、動的型付け言語の柔軟性やメタプログラミングの魅力に感動し、Rubyとともにプログラマーとしての人生を歩む決意を固める。
現在は奈良先端科学技術大学院大学で学生として所属するかたわら、株式会社アジャイルウェアでプログラマーとして従事。Ruby on Railsによるコンシューマー向けのWebサービスの開発などに尽力している。
好きなメソッドは、define_method。
Twitter:@Mozamimy、ブログ:http://blog.quellencode.org/
監修者プロフィール
山根 剛司(Ruby業務開発歴7年)
兵庫県生まれ。1997年からベンチャー系のパッケージベンダーで10年間勤務。当時、使用していた言語はJavaとサーバーサイドJavaScript。
2007年よりITコンサル会社に転職し、Rubyと出会って衝撃を受ける。基幹システムをRuby on Railsで置き換えるプロジェクトに従事。それ以来Ruby一筋で、Ruby on Railsのプラグインやgemも開発。
2013年より、株式会社アジャイルウェアに所属。アジャイルな手法で、Ruby on Railsを使って企業向けシステムを構築する業務に従事。
Ruby関西所属。
Twitter:@spring_kuma、Facebook:山根 剛司
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 公用語に英語、「再起動」したRubyKaigi 2013が東京で開催
いったん終了していたRubyコミュニティ主催の年次イベントが再開。技術色、国際色を強め、盛況のうちに幕を閉じた - Web業界、今から行くならRubyエンジニアが狙い目?
不況で冷え込んでいたIT業界の転職市場に、回復の兆しが見え始めている。だが、業種や職種によって採用数や条件に大きな差異が生まれている。転職市場の動向を追い、自身のキャリア戦略立案に生かしてほしい。 - Ruby 2.0.0がリリース、大規模化対応の機能などを搭載
生誕20年となる節目を迎えて、プログラミング言語「Ruby」の最新版がリリースされた - 新バージョンで何が変わるのか、Rubyはどこへ向かうのか:まつもと×笹田、Ruby 1.9を語る
- いよいよ始まるRuby 1.9への移行:開発コアメンバが語るRubyの今とこれから(前編)
- Rubyの今後の進化の方向性とは?:開発コアメンバが語るRubyの今とこれから(後編)
- 互換性や脆弱性の問題にどう対応していくのか:Rubyが抱える課題、NaClの前田氏が講演
- Rails Hub情報局:「なんでRubyなんか作った!? 迷惑だ!」に対するMatzの答え
- Rails Hub情報局:Rubyはイノベーション言語として選ばれている
- Rails Hub情報局:Rubyのまつもと氏は、一発屋で終わるのか?
- Rails Hub情報局:Rubyに魔法は要らない
- 数々の“スペル”で高度なプログラミング:Rubyの魔術
- 晴読雨読@エンジニアライフ:『たのしい開発 スタートアップRuby』――なぜRubyistたちはあれほど楽しそうなのか
- 「JRuby 1.7.0」登場、1年半ぶりのメジャーアップデート
JavaVM上のRuby実装「JRuby」の最新版となる「JRuby 1.7.0」が、10月22日にリリースされた。