BufferedReaderのガイド

BufferedReaderのガイド

1. 概要

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

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

2. BufferedReaderを使用する場合

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

簡単に言えば、it enables us to minimize the number of I/O operations by reading chunks of characters and storing them in an internal buffer.バッファにデータがある間、リーダーは基になるストリームから直接ではなく、バッファから読み取ります。

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

ほとんどのJavaI / Oクラスと同様に、BufferedReader implementsDecorator pattern, meaning it expects a Reader in its constructor.このようにして、バッファリング機能を使用してReader実装のインスタンスを柔軟に拡張できます。

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

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

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

バッファリングに加えて、BufferedReader also provides some nice helper functions for reading files line-by-line。 したがって、FileReader を直接使用する方が簡単に見えるかもしれませんが、BufferedReaderは大きな助けになります。

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

一般に、we can configure BufferedReader to take any kind of input stream as an underlying sourceInputStreamReaderを使用して、コンストラクターでラップすることでそれを行うことができます。

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

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

2.3. BufferedReaderとスキャナー

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

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

  • BufferedReaderは同期されます(スレッドセーフ)が、スキャナーは同期されません

  • Scanner canは、正規表現を使用してプリミティブ型と文字列を解析します

  • BufferedReaderを使用すると、スキャナーのバッファーサイズを固定しながら、バッファーのサイズを変更できます。

  • BufferedReaderのデフォルトのバッファサイズが大きくなっています

  • ScannerIOExceptionを非表示にし、BufferedReaderはそれを処理するように強制します

  • BufferedReaderは通常、データを解析せずに読み取るだけなので、Scannerよりも高速です。

これらを念頭に置いて、if we are parsing individual tokens in a file, then Scanner will feel a bit more natural than BufferedReader. But, just reading a line at a time is where BufferedReader shines.

必要に応じて、a guide on Scannerもあります。

3. BufferedReaderでテキストを読む

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

3.1. BufferedReaderの初期化

まず、let’s create a BufferedReader using its BufferedReader(Reader) constructor

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

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

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

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

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

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

ほとんどのハードウェアデバイスのブロックサイズは2の累乗であるため、バッファサイズとして2の累乗を使用することをお勧めします。

最後に、java.nioAPI:からのthere is one more handy way to create a BufferedReader using the Files helper class

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();
}

We can do the same thing as above using the lines method introduced in Java 8はもう少し簡単です:

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文字を読み取ることができます。 ストリームが終了するまで、コンテンツ全体を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およびreset

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文字先のマークを記憶します。 It’s handy here because, once we see our first non-whitespace character, we can go back and re-read that character without needing to reprocess the whole stream. Without having a mark, we’d lose the L in our final string.

mark()UnsupportedOperationExceptionをスローする可能性があるため、markSupported()mark(). を呼び出すコードに関連付けることはかなり一般的ですが、ここでは実際には必要ありません。 That’s because markSupported() always returns true for BufferedReader.

もちろん、他の方法で上記をもう少しエレガントに行うことができるかもしれません。実際、mark resetはあまり一般的な方法ではありません。 They certainly come in handy, though, when there is a need to look ahead

5. 結論

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

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