2016年度 発展プログラミング演習 第8講 Findプログラム 2日目
本日の内容
3. コマンドライン引数を解析する.
3-1. コマンドライン引数を解析する.
ここでは,2-1で作成した
parseArguments
メソッドを詳細に実装していきます.
2-1では,args
配列の全ての要素を
Arguments
のdirectory
フィールドに代入していました.
それをコマンドラインで渡されたオプションに応じて,
Arguments
の実体に設定していきます.
Arguments parseArguments(String[] array){ Arguments args = new Arguments(); args.directory = new File(array[0]); for(Integer i = 1; i < array.length; i++){ if(Objects.equals(array[i], "-help")){ args.help = true; } else if(Objects.equals(array[i], "-type")){ i++; args.type = array[i]; } // その他のオプションも同様に解析する. } return args; }
さて,前回,暫定的にparaseArguments
の内容を書きました.
ここでは,本格的にコマンドラインオプションを解析します.
前回までのparseArguments
の内容は,これから書くプログラムに置き換えてください.
まず,3行目で,コマンドラインオプションとして渡された0番目の要素を
File
型に変換し,Arguments
型の
directory
フィールドに代入しています.
仕様では,最初にディレクトリを指定することになっています.
そして,Javaのコマンドライン引数は,前回示した通り,
0番目の要素から始まります.
parseArguments
に渡されたString
型の配列の各要素について,コマンドラインで指定されたオプションを判定します.
0番目の要素はディレクトリの指定であり,オプションの指定は1番目以降ですので,
ループの開始インデックスが1になっている点に注意してください.
左のプログラムのように,コマンドライン引数のString
型の配列の要素が,プログラムのオプションと一致するかを確認します.
文字列の比較は,図書館システム 2日目の
変数の比較で勉強したように,
変数が指し示す先の一致性を見るのか,内容の一致性を見るのかを区別しましょう.
ここでは,内容の一致性を見るため,Objects.equals
メソッドを利用します.
また,-help
オプションは,指定されているか否かの判定だけで良いですが,
他の,例えば,-type
オプションは,引数が要求されます.
そのため,要素のインデックスを一つ増加させてオプションの引数を解析しています.
なお,ここでは,オプションの指定方法が間違ったとしても,エラーとなりません. オプションの引数が適切に指定されているかを確認するにはどうすれば良いかを考えてみましょう.
3-2. 文字列をInteger型に変換する.
その他のオプションも同様に解析しようとすると困ったことがおきます.
Arguments
型のフィールド,maximum
とminimum
は,Integer
型であり,String
型ではありません.
そのため,args.maximum = array[i];
で,
互換性のない型とコンパイルエラーとなってしまいます.
文字列(String
型)の"123"
と
Integer
型の123
は異なる値です.
この間の相互変換を行わなければいけません.
String numberString = "123"; Integer number = new Integer(numberString); String string = number.toString();
文字列からInteger
への変換は,左のように,Integer
型を初期化する時に文字列を渡せば実現できます.
もう一方の変換であるInteger
型から文字列への変換は,
toString
メソッドを呼び出すことで実現できます.
ただし,文字列からInteger
型への変換で,
数値に変換できない文字列を渡した場合,実行時にエラーが発生します.
また,Integer
型から文字列へは,toString
を利用しますが,
このメソッドは,実はあらゆる変数で利用できます.
どんな変数であっても,toString
メソッドの呼び出しは成功し,
その変数の文字列表現が入手できます.
その文字列表現が人に理解できる表現かどうかは別の問題ですが.
さて,この変換方法を踏まえて,コマンドラインオプションを解析し,
maximum
とminimum
に値を代入しましょう.
4. ファイル,ディレクトリの種別で検索できるようにする.
4-1. ファイルの種別で絞り込む.
では,まず,ファイルの種別での絞り込み,すなわち,コマンドラインのオプションのうち,
-type
での絞り込みを実現します.
-type
オプションは,オプションの引数として,d
もしくは,
f
が指定できます.d
が指定された場合は,
ディレクトリのみを出力し,f
が指定された場合は,ファイルの身が出力されるようになります.
void traverse(Arguments args, List<File> result, File file){ // 「ディレクトリを横断するメソッドを作成する」での作成内容を修正する. if(this.isTarget(args, file)){ result.add(file); } // 「ディレクトリを横断するメソッドを作成する」で作成した内容が続く. } Boolean isTarget(Arguments args, File file){ if(args.type != null){ // 引数の file が args.type に合致した種別か判定する. // 合致しなかった場合,false を返す. } // 他の検索処理をここに書いていく. return true; }
オプションの解析は,3-1で行いました.
次に行うべきことは,結果のリストへの追加の際の絞り込みです.
現状では,全てのファイル,ディレクトリがtraverse
メソッドの最初で,結果のリストに追加されます.
ここで,結果に追加すべきか否かを判定する処理を追加しましょう.
では,Boolean
型を返すisTarget
メソッドを定義しましょう.
引数はFile
型とArguments
型とします.
引数に受け取ったFile
が結果に含めるべきものであれば,true
を返し,
結果に含めないのであれば,false
を返します.
結果に含める,含めないの条件は,Arguments
が保持しています.
ここで,Arguments
のtype
フィールドが
null
ではない場合に判定処理を行います.
判定は,Arguments
型のtype
フィールドの値と
File
が指し示すパスの種類で行います.
どのような処理を書けば良いかを考えてみましょう.
また,このFileFinder
では,コマンドライン引数から複数の検索条件が指定できます.
これらはAND検索ですので,指定された全ての条件に合致した場合にのみ,
結果となるリストに追加されます.
つまり,一つでも条件がマッチしなければ条件全体がfalse
となります.
そのため,検索条件を判定していき,一致しなければfalse
を返し,
一番最後にtrue
を返せば全ての条件で判定できることになります.
5. ファイル,ディレクトリを名前で検索できるようにする.
5-1. ファイル,ディレクトリの名前を取得する.
ここでは,4-1で定義した
isTarget
メソッドを拡張します.
4-1では,ファイルの種別で,結果の絞り込みを行いました.
ここでは,別の条件を追加します.ファイル,ディレクトリの名前で検索する方法です.
そのため,ファイル,ディレクトリの名前を取得する方法を学習します.
ファイルの名前は,File
型の変数に対して,getName()
メソッドを呼び出すことで取得できます.
getName()
メソッドは引数なし,返り値はString
型です.
パス名は含まれずファイル名のみを返します.パス名も含めた名前を取得したい場合は,
getPath()
を利用しましょう.
5-2. ファイル・ディレクトリ名で絞り込む.
ファイル名の取得方法がわかったところで,ファイル名での絞り込みを行いましょう.
4-1で作成したisTarget
を修正してください.
現在,isTarget
メソッドの内容は,Arguments
のフィールドtype
が設定されているか(null
でないか)
で処理を行っているはずです.
そこに,今度は,Arguments
のフィールドname
が設定されている場合に,処理を追加しましょう.
比較するのは文字列ですから,Objects.equals
メソッドを利用して比較しましょう.
Arguments
のフィールドname
の型はString
です.
比較対象となるのは,File
ではありません.型が違うため,比較できないためです.
ファイルの名前を比較するのですから,File
型の変数からファイル名を表す
String
型の実態を取得しましょう.getName
で取得できるのでしたね.
6. ファイルをファイルサイズで検索できるようにする.
この処理は,4. ファイル,ディレクトリの種別で検索できるようにする, 5. ファイル,ディレクトリを名前で検索できるようにする が実現できていれば,特に難しいことはないでしょう.
ファイルサイズは,File
型の変数に対して,
length
メソッドを呼び出せば取得できます.
length
メソッドの返り値の型はLong
型です.
以前までと同じく,isTarget
メソッドに検索条件を追加してください.
以上の処理が実現できれば,コマンドラインオプションの指定を変え, 特定のファイルが見つかるかを確認してください.
なお,Arguments
のmaximum
,minimum
は
Integer
型,ファイルサイズはLong
型です.
本来であれば,型が異なるため,比較はできません.
しかし,両方の型とも,数値という共通概念があります.
C言語でもint
型とdouble
型など,異なる型であっても,
自動的に拡張してくれました.Javaでも同様に,数値に関しては,
扱える範囲の広い方に合わせて拡張してくれます.
すなわち,Integer
は32ビット,Long
型は64ビットで,
両方とも,整数を扱う型です.Long
の方がビット数が大きいため,
Long
の方が扱える範囲が広いです.そのため,Integer
と
Long
で比較,計算を行うと,計算の途中でLong
に型を合わせてから計算,比較が行われます.その結果,ユーザは気にせず比較が行えるようになっています.
練習問題
1. helpオプションを実現する.
1-3でコマンドラインのオプションを示し,
3-1でコマンドラインオプションを解析しました.
この練習問題では,help
オプションが指定された場合の処理を実現してください.
help
オプションが指定されると,ヘルプメッセージを表示し,
検索を行わずに終了します.どこにどのような処理を入れれば良いかを考えてください.
Javaプログラムを終了するには,main
メソッドが最後まで実行されれば,自動的に終了します.
また,Webページを検索すると,Javaプログラムを終了する命令として,
System.exit(0);
が紹介されているのを見つけるかもしれません.
しかし,この命令は使ってはいけません.その理由はFAQの
「なぜSystem.exitを使ってはダメなのでしょうか」
に示します.使わずに実現してください.
ヘルプメッセージは,以下のように
1-3
に示すメッセージをそのままSystem.out.println
で出力すれば良いです.
void showHelp(){ System.out.println("java FileFinder <DIR> [OPTIONS]"); System.out.println("DIR:"); System.out.println(" 検索を開始するディレクトリを指定する.必須項目."); System.out.println("OPTIONS:"); System.out.println(" -help: ヘルプメッセージを表示して終了する(検索しない)."); System.out.println(" -name <NAME>: ファイル名・ディレクトリ名で検索する.NAMEは必須."); System.out.println(" -type <d|f>: ファイルの種別で検索する.dはディレクトリ,fはファイルを指す."); System.out.println(" dもしくはfのどちらかを必ず指定する."); System.out.println(" このオプション自体を指定しない場合,両方を検索する."); System.out.println(" -maximum <SIZE>: ファイルサイズが指定のサイズ(SIZE)より小さいものを検索する."); System.out.println(" -minimum <SIZE>: ファイルサイズが指定のサイズ(SIZE)より大きいものを検索する."); System.out.println(" -grep <STRING>: 内容に指定された文字列(STRING)が含まれているファイルを検索する."); }
2. FileInfoを作成する.
$ ls -l FileInfo.* -rw-r--r-- 1 tamada staff 1062 5 26 16:39 FileInfo.class -rw-r--r-- 1 tamada staff 502 5 26 16:39 FileInfo.java $ java FileInfo FileInfo.java FileInfo.java: file, 378 bytes $ java FileInfo FileInfo.* FileInfo.class: file, 1062 bytes FileInfo.java: file, 502 bytes $ java FileInfo . .: directory, 748 bytes
ファイルの情報を出力するコマンドであるFileInfo
を作成しましょう.
左図のように,ファイル名とファイルの種別,ファイルサイズを出力してください.
ファイルサイズは,ファイル,ディレクトリを区別せずに取得できます.
本日のまとめ
今日,学んだ内容は次の通りです.
- ファイルから情報を取得する方法.
- ファイルの種別を取得する方法:
isDirectory
,isFile
メソッド. - ファイルの名前(パスを含まない)を取得する方法:
getName
メソッド. - ファイルサイズを取得する方法:
length
メソッド. - ファイルのパスを取得する方法:
getPath
メソッド.
- ファイルの種別を取得する方法:
String
型からInteger
型への変換方法.Integer
型からString
型への変換方法.