6日でできる 新Java入門|ファイルの読み込み

ファイルの中に眠る修行記録を読み取れるようになると、Javaは一気に実戦向きになる。ファイル読み込みを覚えて、外の世界のデータを自在に扱おう

これまでのJava学習では、プログラムの中に直接書いた値を使って処理を進めることが多かったと思います。けれど、実際のプログラムでは、毎回ソースコードの中に大量のデータを書くわけにはいきません。単語一覧、点数表、設定情報、利用者データ、ログなど、外部のファイルに保存された内容を読み取って使う場面がたくさんあります。そこで大事になるのが ファイルの読み込み です。Javaには、文字を少しずつ読む方法、1行ずつ読み取る方法、文字コードを意識して安全に読む方法など、目的に応じた複数の手段が用意されています。

ドラゴンボールの世界でたとえるなら、修行書庫にたくさんの巻物が保管されていて、必要なときにその巻物を開いて中身を読み取るようなものです。短い修行メモなら少しずつ見てもよいですし、長い修行記録なら1行ずつ読んだ方が分かりやすいこともあります。さらに、古い巻物と新しい巻物では文字の書き方が違うかもしれないので、文字コードを意識して正しく読み解くことも大切になります。

この記事では、FileReader を使った基本的な読み込み、BufferedReader による1行ずつの読み込み、InputStreamReader と FileInputStream を組み合わせた文字コード指定付きの読み込みまで、順番に丁寧に整理していきます。例示するプログラムは Lesson21_1.java、Lesson21_2.java、Lesson21_3.java の3つだけに絞り、それぞれの役割や違いがはっきり分かるように解説していきます。

ファイルの読み込みとは何か

ファイルの読み込みとは、外部に保存されているデータを、Javaプログラムの中へ取り込んで利用することです。プログラムの中だけに値を書いていると、データが増えたときに管理が大変になります。そのため、実用的なプログラムではファイルを読む力がとても重要です。

ファイル読み込みが役立つ場面

場面読み込む内容
単語帳アプリ日本語と英語の対応表
設定ファイルアプリの初期設定
ログ確認実行結果の履歴
クイズアプリ問題文と正解データ

ドラゴンボール風に言えば、修行場にある巻物から 技名一覧 や 修行記録表 を読み取るような感覚です。

Javaでファイルを読む方法の全体像

Javaでは、ファイルの読み込みに複数のクラスが使えます。どのクラスを使うかによって、文字単位なのか、行単位なのか、文字コードを指定するのかが変わってきます。代表的なものとして、次の4つを押さえておくと整理しやすいです。

主要なファイル読み込みクラス

クラス名役割主な特徴
FileReader文字単位でテキストファイルを読むシンプルで基本を理解しやすい
BufferedReader1行ずつ効率よく読むreadLine() が使える
InputStreamReaderバイト入力を文字入力へ変換文字コードを指定できる
Scanner区切りごとに読み取るnextLine() などが使える

今回は、この中でも FileReader、BufferedReader、InputStreamReader を中心に見ていきます。

FileReaderとは何か

FileReaderクラスは、テキストファイルから文字を読み込むための基本的なクラスです。1文字ずつ読むこともできますし、文字配列へまとめて読み込むこともできます。シンプルなので、ファイル読み込みの入り口としてとても分かりやすいです。

FileReaderの基本操作

機能サンプル記述
ファイルを開くFileReader fr = new FileReader("sample1.txt");
文字配列で読み込むfr.read(charArray);
ファイルを閉じるfr.close();

ドラゴンボール風にたとえるなら、FileReader は巻物を開いて、文字を少しずつ目で追っていく読み方です。

FileReaderでファイル内容を表示する

ここでは、sample1.txt に入っている修行データをそのまま表示する例で見ていきます。元の果物と英単語の組み合わせではなく、技名と技コードの組み合わせに変えています。

ファイル名:sample1.txt

かめはめ波,KAMEHAMEHA
舞空術,BUKUJUTSU

ファイル名:Lesson21_1.java

import java.io.FileReader;

public class Lesson21_1 {
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("sample1.txt");
        char[] buffer = new char[128];

        while (true) {
            int len = fr.read(buffer);
            if (len < 0) {
                break;
            }
            System.out.print(new String(buffer, 0, len));
        }

        fr.close();
    }
}

実行結果

かめはめ波,KAMEHAMEHA
舞空術,BUKUJUTSU

このプログラムでは、FileReader で sample1.txt を開き、char配列に読み込んでいます。read() の戻り値 len には 実際に読めた文字数 が入り、ファイルの最後まで到達すると -1 が返ります。そこで len < 0 を条件にしてループを終了しています。

Lesson21_1.java の流れ

手順内容
1FileReaderでファイルを開く
2char配列を用意する
3read()で配列へ読み込む
4読み込んだ文字数だけStringへ変換して表示する
5最後にclose()で閉じる

read() の大事な性質

戻り値意味
0以上読み込んだ文字数
-1ファイル終端

この戻り値の意味を理解しておくと、ファイル読み込みのループがかなり分かりやすくなります。

BufferedReaderとは何か

BufferedReader は、Reader系クラスを包んで、もっと効率よく読み込むためのクラスです。特に便利なのが readLine() です。これを使うと、ファイルを 1行ずつ 読み取れます。テキストファイルを行単位で扱いたいときは、とても使いやすいです。

BufferedReaderの基本操作

主な操作記述例
FileReaderでファイルを開くFileReader fr = new FileReader("sample1.txt");
BufferedReaderで包むBufferedReader br = new BufferedReader(fr);
1行ずつ読むString line = br.readLine();
閉じるbr.close(); fr.close();

ドラゴンボール風に言えば、FileReader が文字を少しずつ追う読み方なら、BufferedReader は 巻物を行ごとに区切って読みやすくした道具 です。

BufferedReaderで行数を数えながら読む

ここでは、修行記録ファイルを1行ずつ読み取り、何行目かも一緒に表示する例にします。

ファイル名:Lesson21_2.java

import java.io.BufferedReader;
import java.io.FileReader;

public class Lesson21_2 {
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("sample1.txt");
        BufferedReader br = new BufferedReader(fr);

        int count = 0;
        String line;

        while ((line = br.readLine()) != null) {
            count++;
            System.out.println("修行記録 " + count + ": " + line);
        }

        br.close();
        fr.close();
    }

実行結果

修行記録 1: かめはめ波,KAMEHAMEHA
修行記録 2: 舞空術,BUKUJUTSU

このプログラムでは、readLine() を使って1行ずつ読み込んでいます。readLine() はファイルの終わりに到達すると null を返します。そのため、while ((line = br.readLine()) != null) という形で読み進めています。

Lesson21_2.java のポイント

ポイント内容
readLine()1行ずつ読み取る
終端判定ファイル終端では null が返る
count何行目かを数える
line読み込んだ1行分の文字列

行ごとにデータを扱うと、CSV風のテキストや設定ファイルを読むときにかなり便利です。

文字コードを意識する必要がある理由

日本語を含むファイルを扱うときは、文字コードを意識することがとても大切です。ファイルが UTF-8 で保存されているのに、別の文字コードとして読んでしまうと、文字化けすることがあります。

ドラゴンボール風にたとえるなら、古い巻物と新しい巻物で使われている文字体系が違うのに、同じ読み方で解読しようとしてしまうようなものです。正しい文字コードを指定することで、はじめて正しく読めます。

InputStreamReader と FileInputStream を組み合わせる意味

文字コードを指定したいときは、FileInputStream、InputStreamReader、BufferedReader を組み合わせる方法がよく使われます。これは少し長く見えますが、それぞれにきちんと役割があります。

3つのクラスの役割

クラス名主な役割何を扱うか本プログラムでの役割
FileInputStreamファイルからバイト単位で読むバイトデータファイルの生データを読む
InputStreamReaderバイトを文字に変換する文字データUTF-8として文字へ変換する
BufferedReader文字を効率よく1行ずつ読む行単位の文字列readLine() を使えるようにする

つまり、3段階になっています。

  1. ファイルを 生のバイト列 で読む
  2. そのバイト列を 文字コード付きで文字へ変換する
  3. 読みやすく 1行ずつ 扱えるようにする

文字コードを指定して読む例

ここでは UTF-8 を明示して、修行記録ファイルを安全に読む例を見ていきます。

ファイル名:Lesson21_3.java

import java.io.*;

public class Lesson21_3 {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(
            new InputStreamReader(new FileInputStream("sample1.txt"), "UTF-8")
        );

        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }

        br.close();
    }
}

実行結果

かめはめ波,KAMEHAMEHA
舞空術,BUKUJUTSU

このプログラムでは、FileInputStream でファイルをバイトとして開き、そのバイトを InputStreamReader が UTF-8 として文字へ変換し、最後に BufferedReader が1行ずつ読み取れる形にしています。

入力ストリームの流れ

この3つのクラスの連携を流れで見ると、とても分かりやすいです。

入力ストリームの流れ

段階役割
sample1.txt元のファイル
FileInputStreamバイトとして読む
InputStreamReaderバイトを文字へ変換する
BufferedReader文字列を1行ずつ取り出す
プログラム読み込んだ行を処理する

ドラゴンボール風にたとえるなら、巻物を取り出し、生の文字を解読し、読みやすく行ごとに整理して、ようやく修行内容として使えるようになる流れです。

ファイル読み込みで例外処理が必要な理由

ファイル操作では、いろいろな問題が起こる可能性があります。たとえば、ファイルが存在しない、読み込み中に失敗する、文字コードが合わない、といったことです。そのため、例外処理を考える必要があります。今回の例では簡単にするため、mainメソッドに throws Exception を付けています。

主なトラブル例

状況起こりうる問題
ファイルが存在しない開けない
読み込み中に問題が起きる途中で失敗する
文字コードが違う文字化けする

実運用では try-catch を使うことが多いですが、入門ではまず 例外が起こる可能性がある ことを知っておくのが大切です。

FileReader と BufferedReader と InputStreamReader の違いを整理する

ここまで学んだ3つの読み方を比べると、それぞれの得意分野が見えてきます。

3つの方法の比較

方法向いている場面特徴
FileReader基本的な文字読み込みシンプルで理解しやすい
BufferedReader行単位で読みたいときreadLine() が便利
InputStreamReader + BufferedReader文字コードを明示したいとき日本語を安全に扱いやすい

この違いを知っておくと、目的に応じてどの方法を使えばよいか判断しやすくなります。

ファイル読み込みの流れを図で整理

ファイル読み込みは、ファイルを開いて、その中の文字をプログラムへ取り込み、必要な単位で扱う流れです。

下図は、sample1.txt のようなファイルからデータが読み込まれ、FileReader または FileInputStream → InputStreamReader → BufferedReader を通って、最終的にプログラムの中で1行ずつ処理される流れを示しています。どのクラスがどの段階を担当しているかが分かる構成です。

↓クリックすると拡大表示されます。

この図が示していること

この図は、ファイルの内容がそのまま使われるのではなく、読み込み用クラスを通して少しずつプログラムへ渡されていることを示しています。とくに、文字単位、バイト単位、行単位という違いが見える構成です。

この図から分かること

この図を見ると、ファイル読み込みには役割の違うクラスが順番に協力していることが分かります。どのクラスを使うかで、読み取り方や安全性が変わることも理解しやすくなります。

3つのクラスの役割を図で整理する

InputStreamReader を使う読み方は少し長く見えるので、3つのクラスの役割を図で整理すると理解しやすいです。

下図は、ファイルからバイトを読む FileInputStream、そのバイトを文字へ変換する InputStreamReader、そして行単位で扱いやすくする BufferedReader の3段階を縦方向または横方向に並べて示したものです。1つひとつのクラスが違う役割を持って連携していることが見える図です。

↓クリックすると拡大表示されます。

この図が示していること

この図は、FileInputStream、InputStreamReader、BufferedReader が、それぞれ別の役割を持ちながら連携してファイルを読んでいることを示しています。1つのクラスだけで全部をしているわけではないことが見えます。

この図から分かること

この図を見ると、ファイル読み込みは 生データを取る、文字へ変える、行として扱う という段階的な流れでできていることが分かります。3つのクラスの役割の違いが分かります。

ファイル読み込みを理解すると、プログラムが外部データを扱えるようになる

Javaでファイルを読み込めるようになると、プログラムはソースコードの中だけに閉じた存在ではなくなります。外部のテキストデータを取り込み、その内容を使って処理を進められるようになるからです。大量のデータを扱うとき、ファイル読み込みは欠かせない技術です。

ドラゴンボールの世界でも、修行のたびに新しい巻物を開き、その内容を読んで次の課題を決めていくなら、巻物を正しく読める力が必要です。Javaのファイル読み込みもそれと同じで、外の世界にある情報を、正しく、効率よく、自分のプログラムへ取り込むための大切な力です。