Java入門|ファイルからデータを読み込む

ファイルから過去の記録を読み出せるようになると、Javaプログラムは一気に実戦向きになる

前の内容では、ファイルへデータを書き出す方法を見てきました。
ファイルに保存できるようになると、その場だけで終わらないプログラムが作れるようになります。
そして、その次に大切になるのが、保存しておいたデータをあとから読み込むことです。

Javaでは、ファイルからの読み込みもストリームの考え方で扱います。
つまり、キーボード入力のときと同じように、データの流れとして考えながら処理していくわけです。
この考え方がつながって見えるようになると、入出力全体がかなり整理しやすくなります。

ドラゴンボールの世界でたとえるなら、神殿の記録庫やカプセルコーポレーションの端末に保存しておいた修行データを、あとから取り出して確認するようなものです。
その場で見ただけでは消えてしまう情報も、ファイルに残しておけば、次の修行や分析に活用できますよね。
Javaのファイル入力も、まさにその感覚です。

ここでは、ファイルから文字列を読み込む基本から、大量の数値データを読み込んで処理する流れまでを、ドラゴンボールの世界観に置き換えながらやさしく整理していきます。

ファイルから入力するとは何か

ファイルから入力するとは、保存してあるデータをプログラムの中へ取り込むことです。

画面に表示するだけのプログラムでは、実行のたびにデータを毎回手で入力しなければなりません。
でもファイルにデータを保存しておけば、その内容をまとめて読み込めます。
このしくみがあることで、より多くのデータを扱えるようになります。

ドラゴンボール風にいうと、修行のたびに「前回の戦闘力はどうだったかな」と口頭で聞き直すのではなく、記録ファイルからまとめて読み込めるようになるイメージです。

ファイル入力でもストリームを使う

ファイルからの読み込みも、キーボードからの入力と同じくストリームで扱います。
つまり、データがファイルからプログラムへ流れてくる通り道を用意し、その流れの中で文字列や数値を取り出します。

今回の基本形では、次の2つのクラスを使います。

クラス名役割
FileReaderファイルを読み込むための文字ストリーム
BufferedReaderバッファを介して効率よく読み込むための文字ストリーム

この組み合わせは、ファイルから1行ずつ読み込むときの基本になります。

まずは文字列を2行読み込む例を見てみよう

最初は、ファイルに保存されている2行の文字列を読み込んで表示するシンプルな例です。

まず、同じフォルダに次のような内容のファイルを保存しておくイメージです。

training1.txt

悟空の朝修行
ベジータの夜修行

そして、このファイルを読むプログラムが次です。

ファイル名:Sample8.java

import java.io.*;

class Sample8
{
    public static void main(String[] args)
    {
        try{
            BufferedReader br =
                new BufferedReader(new FileReader("training1.txt"));

            String str1 = br.readLine();
            String str2 = br.readLine();

            System.out.println("修行記録ファイルに書かれている2つの内容は");
            System.out.println(str1 + "です。");
            System.out.println(str2 + "です。");

            br.close();
        }
        catch(IOException e){
            System.out.println("ファイルの入出力で問題が発生しました。");
        }
    }
}

プログラムの流れ

このプログラムでは、ファイルから2行読み込んで、それを画面に表示しています。
流れを整理すると次のようになります。

手順内容
1FileReader で training1.txt を開く
2BufferedReader で包んで読み込みやすくする
3readLine を2回使って2行取り出す
4取り出した文字列を画面に表示する
5close でファイルを閉じる

ここで注目したいのは、キーボード入力でも使った readLine が、ファイル入力でも使われていることです。
入力元がキーボードからファイルに変わっただけで、1行ずつ文字列を取り出す考え方は同じです。

FileReader の役割

この部分です。

new FileReader("training1.txt")

FileReader は、指定したファイルから文字を読み込むための基本のストリームです。
つまり、どのファイルを読み込み対象にするかをここで決めています。

ドラゴンボールの世界でたとえるなら、神殿の記録庫のどの巻物を開くか指定しているようなものです。

BufferedReader の役割

次に、FileReader を BufferedReader で包んでいます。

new BufferedReader(new FileReader("training1.txt"))

BufferedReader は、バッファを使って効率よく読み込めるようにするクラスです。
さらに readLine を使って1行ずつ読み取れるのも大きな特徴です。

ドラゴンボール風にいうと、記録庫から取り出した情報をそのまま読むのではなく、いったん整理しながら読みやすくしているような感じです。

readLine で1行ずつ読み込む

読み込みそのものは、次の行で行っています。

String str1 = br.readLine();
String str2 = br.readLine();

1回目の readLine で1行目、2回目の readLine で2行目が読み込まれます。
これで、training1.txt に書かれていた2つの文字列を別々に扱えるようになります。

実行結果のイメージはこうです。

修行記録ファイルに書かれている2つの内容は
悟空の朝修行です。
ベジータの夜修行です。

ファイルから大量のデータを読み込む意味

ファイル入力の本当の便利さは、2行や3行の文字列を読むことだけではありません。
むしろ、大量のデータをまとめて扱えるところに大きな価値があります。

キーボードから何十個も何百個も値を入力するのは大変です。
でも、あらかじめファイルに保存しておけば、プログラムがそれをまとめて読み込み、計算や分析ができます。

これはとても実用的です。
ドラゴンボール風にたとえるなら、何人もの戦士の戦闘力データをファイルに保存しておき、それをまとめて読み込んで最高値や最低値を調べるようなものです。

数値を大量に読み込む例を見てみよう

次は、数値データをファイルから読み込み、最大値と最小値を調べる例です。

まず、同じフォルダに次のようなファイルを用意しておきます。

training2.txt

80
68
22
33
56
78
33
56

ここでは、8人分の修行戦闘力メモというイメージで考えます。

ファイル名:Sample9.java

import java.io.*;

class Sample9
{
    public static void main(String[] args)
    {
        try{
            BufferedReader br =
                new BufferedReader(new FileReader("training2.txt"));

            int[] power = new int[8];
            String str;

            for(int i = 0; i < power.length; i++){
                str = br.readLine();
                power[i] = Integer.parseInt(str);
            }

            int max = power[0];
            int min = power[0];

            for(int i = 0; i < power.length; i++){
                if(max < power[i])
                    max = power[i];
                if(min > power[i])
                    min = power[i];

                System.out.println(power[i]);
            }

            System.out.println("最高の戦闘力は" + max + "です。");
            System.out.println("最低の戦闘力は" + min + "です。");

            br.close();
        }
        catch(IOException e){
            System.out.println("ファイルの入出力で問題が発生しました。");
        }
    }
}

Sample9.java の前半でしていること

前半では、ファイルから1行ずつ読み込んで、その文字列を整数へ変換し、配列に入れています。

int[] power = new int[8];
String str;

for(int i = 0; i < power.length; i++){
    str = br.readLine();
    power[i] = Integer.parseInt(str);
}

ここで大事なのは、readLine で読み込まれるのは 文字列 だということです。
training2.txt の中身が数字に見えていても、ファイルから読んだ直後は文字列として受け取られます。

そのため、計算に使うには Integer.parseInt で整数へ変換する必要があります。

処理役割
br.readLine()1行を文字列として読む
Integer.parseInt(str)文字列を int に変換する

ドラゴンボール風にいうと、記録端末から取り出した情報はまず文字の形で届き、それを実際の戦闘力の数値として解釈し直しているようなものです。

Sample9.java の後半でしていること

後半では、配列に入った8個の値を順番に調べながら、最大値と最小値を求めています。

int max = power[0];
int min = power[0];

for(int i = 0; i < power.length; i++){
    if(max < power[i])
        max = power[i];
    if(min > power[i])
        min = power[i];

    System.out.println(power[i]);
}

この処理で、すべての戦闘力を表示しながら、最高値と最低値を更新していきます。

最後に、

System.out.println("最高の戦闘力は" + max + "です。");
System.out.println("最低の戦闘力は" + min + "です。");

で結果を表示しています。

実行結果のイメージはこうです。

80
68
22
33
56
78
33
56
最高の戦闘力は80です。
最低の戦闘力は22です。

ファイル入力の便利さがよく分かるところ

この例の大事なポイントは、データそのものをコードに直接書いていないことです。
数値はすべてファイルから読み込んでいます。

そのため、もしデータを変えたくなったら、Javaコードを書き換えるのではなく、training2.txt の中身を変更するだけで対応できます。
これはとても便利です。

方法変更のしやすさ
コードに直接数値を書くデータ変更のたびにソース修正が必要
ファイルから読み込むファイル内容を変えるだけでよい

ドラゴンボール風にいえば、修行メンバーの戦闘力一覧をプログラム本体に彫り込んでしまうのではなく、別の記録ファイルとして管理しているようなものです。
だから記録が更新されても、プログラム本体はそのままで使えます。

キーボード入力との共通点

ここまで見てくると、ファイル入力とキーボード入力には共通点が多いことが分かります。

項目キーボード入力ファイル入力
基本の考え方ストリームストリーム
1行読み込みreadLinereadLine
効率化BufferedReaderBufferedReader
例外処理IOExceptionIOException

違うのは、入力元だけです。

入力元使用クラス
キーボードInputStreamReader + BufferedReader
ファイルFileReader + BufferedReader

このつながりが見えると、Javaの入出力がばらばらの技術ではなく、ひとつの考え方でまとまっていることがよく分かります。

close が必要な理由

読み込みが終わったあとは、ファイルを閉じる必要があります。

br.close();

これは書き込みのときと同じで、使い終わったファイルをきちんと閉じるためです。
ファイルを開きっぱなしにしないようにすることは、とても大切です。

ドラゴンボール風にいうと、記録庫から巻物を取り出して確認したあと、ちゃんと元の場所へ戻して扉を閉めるようなものです。

例外処理が必要な理由

どちらのサンプルでも try-catch を使っています。

catch(IOException e){
    System.out.println("ファイルの入出力で問題が発生しました。");
}

ファイルは外部の資源なので、

  • ファイルが存在しない
  • 読み込めない
  • アクセス中に問題が起きる

といった可能性があります。
そのため、IOException に備える必要があります。

図でファイル入力の流れを整理する

この図は、ファイルの中の文字列が FileReader と BufferedReader を通り、readLine によって1行ずつ String として取り出される流れを表しています。
ファイル入力もストリームで扱われていることが分かります。

大量データの読み込みを図で整理する

この図は、ファイルから読み込んだ文字列データを整数へ変換し、配列へ入れてから最大値と最小値を計算する流れを表しています。
ファイル入力が、ただの読み込みではなく、その先のデータ処理につながっていることが分かります。

ここで押さえておきたいポイント

最後に、このテーマで大事な点を整理しておきます。

ポイント内容
FileReaderファイルを文字として読み込むための基本のストリーム
BufferedReader効率よく読み込み、readLine を使いやすくする
readLine1行ずつ文字列として読み込む
Integer.parseInt文字列を整数へ変換する
close読み込み後にファイルを閉じる
IOExceptionファイル入力時の問題に備える例外
ファイル入力の利点大量データをまとめて扱えること

ファイルからデータを読み込めるようになると、Javaプログラムはぐっと現実的になります。
その場の入力だけに頼らず、保存しておいた情報をあとから活用できるようになるからです。

ドラゴンボールの世界でも、修行記録や戦闘力データを記録庫から取り出して分析できれば、より深い判断や準備ができますよね。
Javaのファイル入力もそれと同じで、過去に残した情報を今の処理へ生かすための大切な技術です。