Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
36
Help us understand the problem. What is going on with this article?
@YujiSoftware

Javaのバージョン別、1行ずつファイルを読む方法まとめ

(この記事は 地平線に行く とのマルチポストです)

Java でファイルを読み込む処理は、バージョンが上がるごとにどんどん簡単に書けるようになっていきました。
今回は、どれだけ簡単になっていったかを Java のバージョンごとにまとめて説明します。

なお、ここでは以下の処理を行うコードをもとにしています。

  • そこそこ大きいテキストファイルを一行ずつ読み込む
  • 文字コードは UTF-8

Java 1.1, 1.2, 1.3

public static void main(String[] args) throws IOException {
    File file = new File(args[0]);
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream(file)
                            , "UTF-8"));

        String line;
        while((line = reader.readLine()) != null) {
            // 処理
        }
    } finally {
        if (reader != null) {
            reader.close();
        }
    }
}

長いですね。

この時代に FileReader というファイルからテキストを読み込むクラスがあるにはあります。
ただ、このクラスでは 問答無用でシステムデフォルトの文字コードが使われてしまいます。
そのため、Windows から Linux に持っていった場合に文字化けが起きる問題がよく起こっていました。

それを避けるためには、文字コードの指定ができる InputStreamReader を使う必要があります。
InputStreamReader 自体ではファイルの読み込みができないので、FileInputStream を使う必要があります。

さらに、一行ずつ読み込むためには BufferedReader を使う必要があります。これ自体はバッファしかしてくれません。

なので、結果として FileInputStreamInputStreamReaderBufferedReader とラップしていく必要があります。

Java 1.4, 1.5, 6

public static void main(String[] args) throws IOException {
    File file = new File(args[0]);
    Charset charset = Charset.forName("UTF-8");

    BufferedReader reader = null;
    try {
        reader = new BufferedReader(
                    new InputStreamReader(
                        new FileInputStream(file), charset));

        String line;
        while ((line = reader.readLine()) != null) {
            // 処理
        }
    } finally {
        if (reader != null) {
            reader.close();
        }
    }
}

Charset クラスができました。
これにより、文字コードを型で表せるようになりました。

ただ、UTF-8 を使う場合には Charset.forName("UTF-8") とする必要がありました。
なので、型安全ではあるけれど、文字列で指定した方が楽という状況でした。

Java 7


public static void main(String[] args) throws IOException {
    Path path = Paths.get(args[0]);
    Charset charset = StandardCharsets.UTF_8;

    try (BufferedReader reader = Files.newBufferedReader(path, charset)){
        String line;
        while ((line = reader.readLine()) != null) {
            // 処理
        }
    }
}

一気にシンプルになりました。

まず、StandardCharsets クラスが追加されました。これにより、わざわざ標準の文字コード1であれば文字列で指定せずに済むようになりました。
また、Files クラスと newBufferedReader(path, charset) メソッドが追加され、いちいちいろんなクラスを new する必要がなくなりました。2

あと、このバージョンで Path クラスが追加されました。
ファイルを読み込むだけだと File クラスと大差ないですが、パスの操作が簡単になりました。

さらに、try-with-resources が追加され、めんどくさかった close 処理をいい感じにやってくれるようになりました。

----
ちなみに…。
このバージョンから まとめて読み込んで List<String> に格納してくれる Files.readAllLines​(Path, Charset) も使えるようになりました。
ただし、これは大きなファイルに使うとメモリを食ってしまうので要注意です。

Java 8, 9, 10

public static void main(String[] args) throws IOException {
    Path path = Paths.get(args[0]);

    try (Stream<String> stream = Files.lines(path)){
        stream.forEach(line -> /* 処理 */);
    }
}

Stream クラスによって、行の読み込みと処理を反復させることができるようになりました。
これのおかげで、1行ずつ処理をしたいという場合にとてもシンプルに書けるようになりました。

また、Files クラスを使う際に、UTF-8 であれば文字コードの指定を省略できるようになりました。
InputStreamReader などは省略時にシステムのデフォルト文字コードが指定されたものとみなされますが、こちらは UTF-8 を指定したものとみなされる という点には注意が必要です。
ただ、最近のテキストファイルが UTF-8 であることを踏まえると、妥当な判断かなと思います。

Java 11 ~

public static void main(String[] args) throws IOException {
    Path path = Path.of(args[0]);

    try (Stream<String> stream = Files.lines(path)){
        stream.forEach(line -> /* 処理 */);
    }
}

Path.of​(String first, String... more) というメソッドが追加されました。
内部の処理は Paths.get(String first, String... more) とまったく同じですが、このおかげで Path クラスを使うにはどうすればいいんだっけと迷わずに済むようになりました。

----
上記のサンプルでは使っていないのですが…。
なんと、このバージョンで FileReader クラスのコンストラクタで Charset が指定できるようになりました。
Java 1.1 のときに欲しかった…。

なお、InputStreamReader クラスのように文字コードを "UTF-8" のような文字列で指定することはできません。
既存のクラスは互換性のために文字列でも Charset でも指定できるようになっていますが、今後追加されるクラスは Charset のみになるようです。

まとめ

Java はファイルを読み込むだけでもなんて面倒くさいんだと言われていました。
でも、それは昔のお話。

今ではとても簡単になっているんだよ、というのが伝われば幸いです。

関連記事


  1. US-ASCII, ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, UTF-16。これらは、どの Java 実装でも必ずサポートされています。 

  2. Files.newBufferedReader の中で、FileInputStreamInputStreamReaderBufferedReader とラップしてくれています。 

36
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
36
Help us understand the problem. What is going on with this article?