この連載は,Javaの文法を少しは知っているが,具体的なJavaの使い方をよく知らない初心者を対象に,毎回,簡単なサンプル・ツールを作成することで,実際に役立つJavaプログラムの書き方を紹介していきます。第2回のテーマは,ファイルの扱い方と並んで基本中の基本である「ディレクトリの扱い方」です。
この連載では日々の定型的な業務をJavaで自動化してラクする方法を紹介しています。前回はJavaによるファイル操作の基本を紹介しました。ですが,実際には,ファイルを扱うときには一緒にディレクトリ(Windowsでいうところのフォルダ)を扱う必要があるケースも多いものです。例えば,特定のディレクトリの配下にあるファイル群を処理したり,何らかの処理を行ったファイルを別のディレクトリに移動したりといった具合です。そこで,今回はディレクトリ操作の基本を見ていきます。
作成するサンプルは「ディレクトリ内検索ツール」です。あるディレクトリの下にあるファイルのうち,指定した条件に合った名前のファイルの一覧を出力するものです。このツールの特徴は,ディレクトリの中にさらにディレクトリがあってもちゃんと検索しにいく点です。プログラミングはちょっと難しいかもしれませんが,基本さえ押さえてしまえば怖くありません。一緒に見ていきましょう!
ツールの開発に入る前に,ディレクトリ操作で押さえておくべきポイントを挙げておきます。
(1)ディレクトリの構造を知る
(2)ディレクトリ操作の基本を知る
の二つです。この2点さえマスターしてしまえば,あとは意のままにディレクトリを操作できるようになります。
「再帰的構造」になっているディレクトリの階層
Javaでディレクトリを操作するには,java.ioパッケージにあるFileクラスを利用します。「おや,ディレクトリを操作するのにFileクラスを使うの?」と思ったかもしれません。そうなのです。実はJavaでは,ディレクトリをFileオブジェクトとして扱うようになっています。ディレクトリをファイルの一種であるとするのは,UNIXから来た考え方です。ディレクトリ構造を柔軟に扱うために,このような考え方を採用しているのです。ディレクトリをファイルの一種だとみなすことでどのようなメリットがあるのかを説明していきましょう。
例えば,図1のようなディレクトリ構造があったとします。tempディレクトリの下にtest1.txtというファイルとtest_dirというディレクトリがあり,test_dirディレクトリの中にtest2.txtというファイルがあります。このように,ディレクトリの中身はファイルかディレクトリになります。そして,そのディレクトリの中にはさらにファイルかディレクトリが存在する…というように,ディレクトリは入れ子の構造になっていることがわかると思います。このような入れ子構造を難しい言葉でいうと「再帰的構造」と呼びます。ディレクトリの中を見ると,再びディレクトリがある,つまり「自分と同じものに再び帰っている」という意味です。
UMLを使用してこの構造を表現すると図2のようになります(「UMLを読めるようになろう」を参照)。ディレクトリがディレクトリを保持している,つまり再帰的構造であることを表現しています。
ところが,図2の構造は実はきちんとした再帰にはなっていません。ディレクトリの中に,ディレクトリ以外のもの,つまりファイルも存在しています。これをUMLで表現すると図3のようになります。この図は,ディレクトリは再帰的だけれども,ファイルは再帰的ではないことを表現しています。
どうでしょうか? 登場人物が増えてちょっとややこしくなりました。一般にソフトウエアの設計はシンプルなほうがよく,登場人物もあまり多くないほうが覚えることも少なくて済みます。そこでJavaは大胆にも「じゃぁディレクトリをファイルの一種ということにしたらすっきりしない?」といっているのです。これをUMLで表現すると図4のようになります。図2の「ディレクトリ」が図4では「ファイル」に置き換わっています。このように考えることで,ファイルもディレクトリも同じように扱うことができるのです。
ちなみに「再帰的構造」という名前はいかついですが,とても基本的な構造であり,いろんな場面で目にします。例えば,HTMLはタグの中にタグがある再帰ですし,人間の親子関係も親(人間)が子(人間)を持つという再帰ですね。再帰はとても基本的な構造なので,昔からよく研究されており,プログラムでどのように処理すればよいかという定石が知られています。今回はあまり難しい部分には踏み込みませんが,再帰の基本的な扱い方を知れば,様々なデータ構造を扱うのが格段にラクになります。なにより「僕,再帰のプログラムを普通に書けますよ」と言えると,何だかちょっとかっこよくないですか?