問題3-1
FileCopyPracticeクラスのmainメソッドは未完成です。
あるテキストファイルの内容を、別のテキストファイルにコピーするプログラムを作成してください。
その方法は、コピー元ファイルからFileReaderクラスとBufferedReaderクラスをうまく使ってデータを読み取り、そのデータをコピー先ファイルにFileWriterクラスとBufferedWriterクラスとPrintWriterクラスをうまく使って書き込みます。
コピー元ファイル名およびコピー先ファイル名の指定にはコマンドライン引数を使用してください。
【実行例】
> java FileCopyPractice コピー元ファイル名 コピー先ファイル名
コマンドラインが不正(2つの文字列が渡ってこない)の場合は「2つのファイル名を正しく指定してください」と表示してプログラムを終了します。
コピー元ファイルが見つからない場合はFileNotFoundExceptionがスローされます。
その際は「ファイル名の指定が不正です」と表示してプログラムを終了します。
それ以外にIOExceptionがスローされますので、そのときは「入出力エラーです」と表示してプログラムを終了します。
オープンしたファイルは適切にクローズしてください。
【FileCopyPractice.java】
public class FileCopyPractice { public static void main(String[] args) { // ここにファイルコピー処理を記述してください } }
解答例
【ExceptionInheritancePractice.java】
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class FileCopyPractice { public static void main(String[] args) { BufferedReader br = null; PrintWriter pw = null; if(args.length != 2) { System.out.println("2つのファイル名を正しく指定してください"); return; } try { // コピー元ファイル名のオープン br = new BufferedReader(new FileReader(args[0])); // コピー先ファイル名のオープン pw = new PrintWriter(new BufferedWriter(new FileWriter(args[1]))); String str; // コピー元ファイルから1行分の文字列を取得 while((str = br.readLine()) != null) { // 取得した文字列をコピー先ファイルに書き込み pw.println(str); } } catch(FileNotFoundException e) { System.out.println("ファイル名の指定が不正です"); } catch(IOException e) { System.out.println("入出力エラーです"); } finally { try { if(br != null) { br.close(); } } catch(IOException e) { e.printStackTrace(); } // PrintWriterのcloseメソッドはIOException例外を送出しないため if(pw != null) { pw.close(); } } } }
解説
今回はファイルIOに関する問題です。
Javaでは、ファイルIOを行うクラス群がjava.ioパッケージに収められています。
ですので、使う際にはまずimport文を記述しましょう。
今回のように同じパッケージのクラスをたくさん使用する場合は、クラス名の部分をアスタリスクにして一括でインポートすることも可能です。
(ただしeclipseを使用している場合には、自動的に完全クラス名に置き換わってしまいます。)
import java.io.*;
これを「オンデマンドインポート」と言います。
しかし、開発現場ではオンデマンドインポートを推奨していないプロジェクトもありますので、その点は意識しておいてください。
今回の問題では、コピー元・コピー先ファイル名の指定にコマンドライン引数を使用します。
コマンドライン引数とは、プログラム実行時に渡すことのできる文字列情報のことです。
半角スペースで区切ることで、String配列の形でmainメソッドの引数に渡されます。
いつも記述しているString配列型引数のargsです。
今回は2つのデータが渡ってこないといけないので、配列オブジェクトが持っているlength属性で要素数をチェックしています。
ファイルの入力にはFileReaderクラス、出力にはFileWriterクラスを使うのが鉄則です。
しかし、この2つのクラスだけではパワーが弱いので、BufferedReaderクラスやBufferedWriterクラス、PrintWriterクラスなどを組み合わせてパワーアップさせています。
実際にデータをコピーしているのは、次のプログラムです。
while((str = br.readLine()) != null) { // 取得した文字列をコピー先ファイルに書き込み pw.println(str); }
BufferedReaderクラスのreadLineインスタンスメソッドを呼び出すことで、テキストファイルの1行を取得することができます。
テキストファイルが何行あるかは分からないので、while文を使ってループさせます。
while文の丸括弧の中が少し難しく見えます。
readLineメソッドの戻り値を文字列変数strに格納していますが、Javaというプログラミング言語は、「代入演算も値を返す」ようにできています。
int x; System.out.println(x = 10);
これはもちろん変数xに10が代入され、かつ「10」が出力されます。
話をreadLineメソッドに戻すと、このメソッドはファイルの終端まで到達するとnullを返します。
つまり、while文の評価の意味は「nullが返ってくるまでデータを取得し続けてください」ということです。
ファイルIO系クラスのほとんどのメソッドは検査例外IOException(とそのサブクラスの例外)をスローします。
ですので、一般的には大きなtryブロックを記述し、その中にファイル入出力処理を記述します。
そして、ファイルIOで特に重要なのは、ファイルを使える状態(オープンと言います)にしたら、最後は必ず後始末(クローズと言います)しなければいけないということです。
そして、ファイルのクローズ処理はfinallyブロックに記述して、必ず実行されるようにするのがベストです。
オープンしたファイルをクローズするにはcloseメソッドを実行するのですが、このcloseメソッドも検査例外IOExceptionをスローするので、finallyブロックに小さくtry-catchを記述します。
ちなみに、Java7からはリソースのクローズを自動的に行ってくれる「try-with-resources文」という新しい文法が追加されました。
tryの後ろに丸括弧を記述し、そこにクローズが必要なオブジェクトのインスタンス化と参照変数の宣言・代入を行うことで、自動的にcloseメソッドを呼び出してくれます。
参考図書
LINE公式アカウント
仕事が辛くてたまらない人生が、仕事が楽しくてたまらない人生に変わります。
【登録いただいた人全員に、無料キャリア相談プレゼント中!】