1. 概要

BufferedReader は、文字入力ストリームからのテキストの読み取りを簡素化するクラスです。 テキストデータの効率的な読み取りを可能にするために、文字をバッファリングします。

このチュートリアルでは、 BufferedReader classの使用方法を見ていきます。

2. BufferedReaderを使用する場合

一般に、 BufferedReader は、ファイル、ソケット、その他のあらゆる種類の入力ソースからテキストを読み取りたい場合に便利です。

簡単に言うと、文字のチャンクを読み取り、それらを内部バッファーに格納することで、I / O操作の数を最小限に抑えることができます。バッファーにデータがある間、リーダーは直接ではなく、そこから読み取ります。基になるストリーム。

2.1. 別のリーダーのバッファリング

ほとんどのJavaI/ Oクラスと同様に、 BufferedReaderは実装しますデコレータパターン、 つまり、コンストラクターにReaderが必要です。 このようにして、インスタンスを柔軟に拡張できます。 読者バッファリング機能を備えた実装:

BufferedReader reader = 
  new BufferedReader(new FileReader("src/main/resources/input.txt"));

ただし、バッファリングが重要でない場合は、FileReaderを直接使用できます。

FileReader reader = 
  new FileReader("src/main/resources/input.txt");

バッファリングに加えて、 BufferedReaderは、ファイルを1行ずつ読み取るための優れたヘルパー関数も提供します。 したがって、 FileReader を直接使用する方が簡単に見えるかもしれませんが、BufferedReaderは大きな助けになります。

2.2. ストリームのバッファリング

一般に、基になるソースとして任意の種類の入力ストリームを取得するようにBufferedReaderを構成できます InputStreamReader を使用して、コンストラクターでラップすることができます。

BufferedReader reader = 
  new BufferedReader(new InputStreamReader(System.in));

上記の例では、 System.in から読み取っています。これは通常、キーボードからの入力に対応します。 同様に、ソケット、ファイル、または考えられるあらゆるタイプのテキスト入力から読み取るための入力ストリームを渡すことができます。 唯一の前提条件は、それに適したInputStream実装があることです。

2.3. BufferedReaderとスキャナー

別の方法として、 Scanner クラスを使用して、BufferedReader。と同じ機能を実現することもできます。

ただし、これら2つのクラスには大きな違いがあり、ユースケースに応じて、これらのクラスを多かれ少なかれ便利にすることができます。

  • BufferedReader は同期されます(スレッドセーフ)が、スキャナーは同期されません
  • スキャナーは、正規表現を使用してプリミティブ型と文字列を解析できます
  • BufferedReader は、スキャナーのバッファーサイズが固定されているときに、バッファーのサイズを変更できます。
  • BufferedReaderのデフォルトのバッファサイズが大きくなっています
  • スキャナーIOExceptionを非表示にし、BufferedReaderはそれを処理するように強制します
  • BufferedReader は通常、 Scanner よりも高速です。これは、データを解析せずに読み取るだけだからです。

これらを念頭に置いて、 ファイル内の個々のトークンを解析している場合、ScannerはBufferedReaderよりも少し自然に感じます。 ただし、一度に1行ずつ読み取るだけで、BufferedReaderが優れています。

必要に応じて、スキャナーに関するガイドも用意しています。

3. BufferedReaderでテキストを読む

BufferReader を適切に構築、使用、破棄してテキストファイルから読み取るプロセス全体を見ていきましょう。

3.1. BufferedReaderの初期化

まず、 BufferedReader(Reader)コンストラクターを使用してBufferedReaderを作成しましょう。

BufferedReader reader = 
  new BufferedReader(new FileReader("src/main/resources/input.txt"));

FileReader をこのようにラップすることは、他のリーダーにアスペクトとしてバッファリングを追加するための優れた方法です。

デフォルトでは、これは8KBのバッファーを使用します。 ただし、小さいブロックまたは大きいブロックをバッファリングする場合は、 BufferedReader(Reader、int)コンストラクターを使用できます。

BufferedReader reader = 
  new BufferedReader(new FileReader("src/main/resources/input.txt")), 16384);

これにより、バッファサイズが16384バイト(16 KB)に設定されます。

最適なバッファサイズは、入力ストリームのタイプやコードが実行されているハードウェアなどの要因によって異なります。 このため、理想的なバッファサイズを実現するには、実験して自分でバッファサイズを見つける必要があります。

ほとんどのハードウェアデバイスはブロックサイズとして2の累乗を持っているため、バッファサイズとして2の累乗を使用するのが最適です。

最後に、 java.nio API:のファイルヘルパークラスを使用してBufferedReaderを作成するもう1つの便利な方法があります。

BufferedReader reader = 
  Files.newBufferedReader(Paths.get("src/main/resources/input.txt"))

をこのように作成すると、最初に FileReader を手動で作成してからラップする必要がないため、ファイルを読み取りたい場合にバッファリングするのに適した方法です。

3.2. 行ごとに読む

次に、readLineメソッドを使用してファイルの内容を読み取ります。

public String readAllLines(BufferedReader reader) throws IOException {
    StringBuilder content = new StringBuilder();
    String line;
    
    while ((line = reader.readLine()) != null) {
        content.append(line);
        content.append(System.lineSeparator());
    }

    return content.toString();
}

Java 8 で導入されたlinesメソッドを使用して、上記と同じことをもう少し簡単に行うことができます。

public String readAllLinesWithStream(BufferedReader reader) {
    return reader.lines()
      .collect(Collectors.joining(System.lineSeparator()));
}

3.3. ストリームを閉じる

BufferedReader を使用した後、その close()メソッドを呼び出して、関連するシステムリソースを解放する必要があります。 try-with-resources ブロックを使用すると、これは自動的に行われます。

try (BufferedReader reader = 
       new BufferedReader(new FileReader("src/main/resources/input.txt"))) {
    return readAllLines(reader);
}

4. その他の便利な方法

それでは、BufferedReader。で利用できるさまざまな便利なメソッドに焦点を当てましょう。

4.1. 単一の文字を読む

read()メソッドを使用して、単一の文字を読み取ることができます。 ストリームの最後まで、コンテンツ全体を1文字ずつ読みましょう。

public String readAllCharsOneByOne(BufferedReader reader) throws IOException {
    StringBuilder content = new StringBuilder();
        
    int value;
    while ((value = reader.read()) != -1) {
        content.append((char) value);
    }
        
    return content.toString();
}

これにより、文字(ASCII値として返される)が読み取られ、 char にキャストされ、結果に追加されます。 read()メソッドからの応答値-1で示されるストリームの終わりまで、これを繰り返します。

4.2. 複数の文字を読む

一度に複数の文字を読み取りたい場合は、メソッド read(char [] cbuf、int off、int len)を使用できます。

public String readMultipleChars(BufferedReader reader) throws IOException {
    int length;
    char[] chars = new char[length];
    int charsRead = reader.read(chars, 0, length);

    String result;
    if (charsRead != -1) {
        result = new String(chars, 0, charsRead);
    } else {
        result = "";
    }

    return result;
}

上記のコード例では、最大5文字をchar配列に読み込み、そこから文字列を作成します。 読み取りの試行で文字が読み取られなかった場合(つまり、 ストリームの最後に到達しました)、空の文字列を返すだけです。

4.3. 文字をスキップする

skip(long n)メソッドを呼び出すことで、指定された文字数をスキップすることもできます。

@Test
public void givenBufferedReader_whensSkipChars_thenOk() throws IOException {
    StringBuilder result = new StringBuilder();

    try (BufferedReader reader = 
           new BufferedReader(new StringReader("1__2__3__4__5"))) {
        int value;
        while ((value = reader.read()) != -1) {
            result.append((char) value);
            reader.skip(2L);
        }
    }

    assertEquals("12345", result);
}

上記の例では、2つのアンダースコアで区切られた数値を含む入力文字列から読み取ります。 数字のみを含む文字列を作成するために、skipメソッドを呼び出して下線をスキップしています。

4.4. マークおよびリセット

mark(int readAheadLimit)および reset()メソッドを使用して、ストリーム内のある位置をマークし、後で戻ることができます。 やや不自然な例として、 mark() reset()を使用して、ストリームの先頭にあるすべての空白を無視してみましょう。

@Test
public void givenBufferedReader_whenSkipsWhitespacesAtBeginning_thenOk() 
  throws IOException {
    String result;

    try (BufferedReader reader = 
           new BufferedReader(new StringReader("    Lorem ipsum dolor sit amet."))) {
        do {
            reader.mark(1);
        } while(Character.isWhitespace(reader.read()))

        reader.reset();
        result = reader.readLine();
    }

    assertEquals("Lorem ipsum dolor sit amet.", result);
}

上記の例では、 mark()メソッドを使用して、今読んだ位置をマークします。 値を1にすると、コードだけが1文字先のマークを記憶することを意味します。 ここでは、最初の空白以外の文字を確認したら、ストリーム全体を再処理しなくても、戻ってその文字を読み直すことができるので便利です。 マークがないと、最後の文字列のLが失われます。

なぜならマーク() 投げることができます UnsupportedOperationException 、関連付けることはかなり一般的です markSupported() を呼び出すコードでマーク ()。 ただし、ここでは実際には必要ありません。 これは、markSupported()がBufferedReaderに対して常にtrueを返すためです。

もちろん、他の方法で上記をもう少しエレガントに行うことができるかもしれません。実際、マークリセットはあまり一般的な方法ではありません。 ただし、先を見越す必要がある場合は、確かに便利です

5. 結論

このクイックチュートリアルでは、 BufferedReader を使用して、実際の例で文字入力ストリームを読み取る方法を学習しました。

最後に、例のソースコードは、Githubから入手できます。