前回に引き続きファイルの入出力についての解説です。
前回の記事はこちら phoeducation.work
テキストファイルの読み書き
FileReaderクラスとFileWriterクラスはデータの読み書きを、16ビット(文字)単位で行います。
Java言語は、1文字をUnicodeで16ビットデータとして扱っています。
キャラクタストリームを利用すると、文字コードは自動的に変換されるので、文字コードを意識する必要がありません。
コンストラクタとして、Fileオブジェクトをとるものと、Stringオブジェクトとしてファイル名を取るものがあります。
これらコンストラクタはFileNotFoundExceptionという例外をスローする可能性があります。
ファイルを読み込むには、read()メソッドを使用します。
これは、入力ストリームから1文字(1バイト)ずつを読み込み、ファイルの終わりに達すると-1を返します。
read()メソッドでは、読み込まれる際にchar型ではなくint型で返されるので、read()メソッドの戻り値が-1でない場合は、char型にキャストして文字情報を取得するようにしなければなりません。
write()は出力ストリームに1バイトずつ書き込む要求をJavaVMに送ります。
つまりwrite()メソッドを呼びだしたらすぐにファイルに書き込まれるわけでありません。
これはファイルの読み書きを高速化するためにバッファと言う場所にデータをためておき、後からまとめて読み書きの処理をします。
flush()はバッファにたまっているデータを出力先にただちに書き込みます。
これらのメソッドはIOExceptionの例外を発生する可能性があるので、例外処理をする必要があります。
【FileReaderとFileWriterのコンストラクタ】
【FileReaderとFileWriterのメソッド】
【FileWriterクラスとFileReaderクラスの利用】
前回の記事で作成した「filesample」フォルダを使用します。
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; class FileSample2{ public static void main(String args[]){ File file = new File("filesample¥¥filetext2.txt"); FileWriter fw = null; FileReader fr = null; try{ fw = new FileWriter(file); fw.write("未来"); fw.flush(); fr = new FileReader(file); int data = 0; while((data = fr.read()) != -1){ System.out.print((char)data); } }catch(FileNotFoundException e){ System.out.println("ファイルがありません"); }catch(IOException e){ System.out.println("書き込み時にエラーが発生しました"); }finally{ try{ fw.close(); fr.close(); }catch(IOException e){} } } }
実行結果
コンソールの表示
未来
ファイル出力
この例では、filesampleディレクトリにfiletext2.txtというファイルを作成し、データの入出力を行います。
まず8行目で、filesample¥¥filetext2.txtのFileオブジェクトを作成します。
¥はエスケープシーケンス文字です。
9行目~10行目でFileWriterとFileReaderのオブジェクトを格納する変数fwとfrを作成し、まずはnullを格納しておきます。
これらのコンストラクタやメソッドはFileNotFoundException 例外を投げる可能性があるので、catch文でこの例外を受け止める処理を書いておきます。
12行目でFileWriterクラスのコンストラクタにFileオブジェクトを指定することで、filesample¥¥filetext2.txtに書き出しをすることができるようになります。
13行目でwrite()の引数に「未来」という文字列を指定し、filesample¥¥filetext2.txtに書き出しています。
14行目でflush()を使用してバッファにたまったデータを完全にファイルに書き出します。
今回の出力データは「未来」という漢字を指定していますが文字化けしていません。
FileWriterクラスとFileReaderクラスは、キャラクタストリーム(16ビット単位で読み書きする)を使用しているので、入出力データの文字コードは自動的に変換されます。
Java言語では1文字をUnicode(16ビットデータ)として扱っているので、キャラクタストリームを使用すると、OSの文字コードを意識することなく入出力を行うことができ、漢字「未来」は文字化けしていないのです。
16行目では、FileReaderクラスのコンストラクタにFileオブジェクトを指定して、filesample¥¥filetext2.txtから読み込みをすることができるようになります。
17行目のint data = 0;
は、入力ストリームから1文字ずつ読み込まれた値を格納します。
ここで1文字ずつ読み込まれる際、char型ではなくint型であることに注意します。
19行目~21行目では、FileReaderクラスのread()は、ファイルの終わりに達すると-1を返すのでwhile文を使用し、-1でない間はデータを1文字ずつ読み込みます。
20行目ではread()メソッドの戻り値はint型なので、char型にキャストします。
22行目~25行目では、catchブロックで例外処理を記述しておきます。
そして26行目~31行目のfinallyブロックでFileWriterとFileReaderのストリームをclose()で閉じておきます。
close()メソッドもIOExceptionの例外を発生する可能性があるのでtry~catchブロック文で囲みます。
close()を記述し忘れると、他のプログラムから読み書きできなくなったり、そのファイルを開けなくなったりする可能性があります。
ところが、きちんとclose()を記述していても実行されないことがあります。
このサンプルプログラムのようにflush()が実行された時点で、IOException例外が発生すると、その時点でプログラムが終了してしまい、close()が実行されない可能性が出てきます。
そこでclose()は必ず実行されるfinallyブロックに記述するようにします。
ポイント!Java7から導入されたtry-with-resource文
上記の説明でclose文はfinallyブロックに記述するべきと説明しました。
しかしFileWriterクラスの処理で例外が発生し、オブジェクトが生成されなかった場合、fwはnullを格納したまま、filallyブロックに制御が移り、close()を実行するとNullPointerExceptionを発生するので、close()処理を行う際には、fwがnullかどうかのチェックを行う必要があります。
そこでJava7からtry-with-resource文が導入されました。
try(リソース; [リソース] )
tryの後ろに()を記述し、その中にクローズの対象になるオブジェクトの生成処理を記述します。
オブジェクトが複数ある場合には、セミコロンで区切って記述します。
クローズの対象になるような入出力のオブジェクトをリソースと言います。tryブロックにリソース生成を記述することからtry-with-resource文と呼ばれます。
tryブロックにリソース生成を記述することで、tryブロックが終了する際に暗黙的にclose()が呼び出され、リソースが解放されます。すなわち、close()を明示的に記述しなくて済むようになります。
【try-with-resource文の利用】
先ほど作成された「filetext2」ファイルをいったん削除して、前のサンプルと同じ結果になることを確認しましょう。
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; class FileSample3{ public static void main(String args[]){ File file = new File("filesample¥¥filetext2.txt"); try(FileWriter fw = new FileWriter(file); FileReader fr = new FileReader(file)){ fw.write("未来"); fw.flush(); int data = 0; while((data = fr.read()) != -1){ System.out.print((char)data); } }catch(FileNotFoundException e){ System.out.println("ファイルがありません"); }catch(IOException e){ System.out.println("書き込み時にエラーが発生しました"); } } }
実行結果
コンソールの表示
未来
ファイル出力
10行目~11行目でtry文の後ろに()を記述して、この中にFileWriterオブジェクトとFileReaderオブジェクトを生成しています。
これによりfinallyブロックに記述するclose()を省略することができます。
バイナリファイルの読み書き
FileInputStreamクラスとFileOutputStreamクラスを使用するとバイナリファイルを読み書きすることができます。
【FileInputStreamとFileOutputStremのコンストラクタ】
【FileInputStreamとFileOutputStremのメソッド】
コンストラクタもメソッドもほぼテキストファイルの読み書きをするFilereaderクラス、FileWriterクラスとほぼ変わりませんが、FileOutputStreamクラスのwrite()の引数がint型であることに注目してください。
FileWriterクラスのwrite()は文字列を書き込むため引数にString型をとりました。
一方FileOutputStreamクラスはバイナリデータを書き込みます。
バイナリデータは2進数表現であるため、引数はint型となっているのです。
しかし、プログラム記述時に書き込みたいデータを2進数で記述するのは大変なので、文字列をバイトデータ(2進数)に変換する、getBytes()メソッドを使用すると便利です。
【FileInputStremとFileOutputStreamの利用】
先ほどと同様に、作成された「filetext2」ファイルをいったん削除して確認しましょう。
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; class FileSample4{ public static void main(String args[]){ File file = new File("filesample¥¥filetext2.txt"); try(FileOutputStream fos = new FileOutputStream(file); FileInputStream fis = new FileInputStream(file)){ fos.write("mirai".getBytes()); int data = 0; while((data = fis.read()) != -1){ System.out.print((char)data ); } }catch(FileNotFoundException e){ System.out.println("ファイルがありません"); }catch(IOException e){ System.out.println("書き込み時にエラーが発生しました"); } } }
実行結果
コンソールの表示
mirai
ファイル出力
11行目では「mirai」という文字列をgetBytes()でバイトコード(2進数)に変換して、ファイルに書き込んでいます。
9行目~15行目ではtry-with-resource文を使用しているため、close()を省略することができます。
「ファイルの入出力 Part3」へ続きます。
phoeducation.work
参考図書
LINE公式アカウント
仕事が辛くてたまらない人生が、仕事が楽しくてたまらない人生に変わります。
【登録いただいた人全員に、無料キャリア相談プレゼント中!】