web-dev-qa-db-ja.com

InputStream、InputStreamReader、およびBufferedReaderはJavaでどのように連携しますか?

私はAndroid開発(私は一般的にプログラミングの初心者です)を勉強していて、HTTPネットワーキングについて学び、レッスンでこのコードを見ました:

_private String readFromStream(InputStream inputStream) throws IOException {
  StringBuilder output = new StringBuilder();
  if (inputStream != null) {
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
    BufferedReader reader = new BufferedReader(inputStreamReader);
    String line = reader.readLine();
    while (line != null) {
      output.append(line);
      line = reader.readLine();
    }
  }
  return output.toString();
}
_

InputStream、InputStreamReader、BufferedReaderが何をするのか正確にはわかりません。それらすべてにread()メソッドがあり、BufferedReaderの場合はreadLine()もあります。InputStreamのみを使用したり、InputStreamReaderのみを追加したりできないのはなぜですか。 BufferedReaderを追加する必要があるのはなぜですか?効率と関係があることは知っていますが、その方法がわかりません。

私は調査を続けており、 BufferedReaderのドキュメント はこれを説明しようとしていますが、誰が何をしているのかわかりません。

一般に、リーダーで作成された各読み取り要求により、対応する読み取り要求が基になる文字またはバイトストリームで作成されます。したがって、FileReadersやInputStreamReadersなど、read()操作にコストがかかる可能性のあるReaderの周りにBufferedReaderをラップすることをお勧めします。例えば、

BufferedReader in = new BufferedReader(new FileReader("foo.in"));は、指定されたファイルからの入力をバッファリングします。バッファリングがないと、read()またはreadLine()を呼び出すたびに、バイトがファイルから読み取られ、文字に変換されてから返される可能性があり、これは非常に非効率的です。

したがって、InputStreamは1バイト、InputStreamReaderは1文字、BufferedReaderは1行全体しか読み取れず、効率についても何かを行うことを理解しています。これは私が得られないことです。誰が何をしているのかをよりよく理解して、3つすべてが必要な理由と、1つがないと違いがどうなるかを理解したいと思います。

私はここやウェブ上の他の場所で多くのことを研究しましたが、私が理解できるこれについての説明は見つからないようです。ほとんどすべてのチュートリアルはドキュメント情報を繰り返すだけです。これを説明し始めるかもしれないが、深く掘り下げて私の混乱を解決しないいくつかの関連する質問があります: Q1Q2Q 、- Q4 。システムコールとリターンについてのこの最後の質問の説明と関係があるのではないかと思います。しかし、私はこれらすべてが何を意味するのかを理解したいと思います。

BufferedReaderのreadLine()がInputStreamReaderのread()メソッドを呼び出し、次にInputStreamのread()メソッドを呼び出す可能性がありますか?そして、InputStreamはintに変換されたバイトを返し、一度に1バイトを返します。InputStreamReaderはこれらを十分に読み取って単一の文字を作成し、それをintに変換して一度に単一の文字を返し、BufferedReaderはこれらの文字を十分に読み取ります。行全体を構成する整数として表されますか?そして、行全体を文字列として返し、数回ではなく1回だけ返しますか?わからない、私は物事がどのように機能するかを取得しようとしているだけです。

よろしくお願いします!

8
schv09

この Javaの概念と使用法のストリーム リンクは、非常に素晴らしい説明を提供します。

This

Streams、Readers、Writers、BufferedReader、BufferedWriter –これらはJavaで扱う用語です。 Javaには、入力と出力を操作するためのクラスがあります。これらがどのように関連し、どのように使用されるかを知ることは本当に価値があります。この投稿では、Javaおよびその他の関連クラスのストリームについて詳しく説明します。それでは始めましょう:

これらのそれぞれを高レベルで定義してから、さらに深く掘り下げてみましょう。

ストリーム
バイトレベルのデータを処理するために使用されます

リーダー/ライター
文字レベルを処理するために使用されます。さまざまな文字エンコードにも対応しています。

BufferedReader/BufferedWriter
パフォーマンスを向上させるため。読み取られるデータは、すばやくアクセスできるようにメモリにバッファリングされます。

これらは入力を取得するためのものですが、対応するクラスだけが出力用にも存在します。たとえば、バイトのストリームを読み取ることを目的としたInputStreamがあり、OutputStreamがバイトのストリームの書き込みに役立つ場合です。

InputStreams
Javaが提供するInputStreamには多くの種類があります。それぞれがバイト配列、ファイルなどの個別のデータソースに接続します。

たとえば、FileInputStreamはファイルデータソースに接続し、ファイルからバイトを読み取るために使用できます。一方、ByteArrayInputStreamは、バイト配列を入力ストリームとして扱うために使用できます。

OutputStream
これは、データソースへのバイトの書き込みに役立ちます。ほとんどすべてのInputStreamには、意味のある場所に対応するOutputStreamがあります。


[〜#〜]更新[〜#〜]

バッファストリームとは何ですか?

ここで私は Buffered Streams 、Javaドキュメント(技術的な説明付き)から引用しています:

バッファリングされたストリーム

これまで見てきた例のほとんどは、バッファなしI/Oを使用しています。これは、各読み取りまたは書き込み要求が基盤となるOSによって直接処理されることを意味します。このような要求はそれぞれ、ディスクアクセス、ネットワークアクティビティ、または比較的コストのかかるその他の操作をトリガーすることが多いため、これによりプログラムの効率が大幅に低下する可能性があります。

この種のオーバーヘッドを削減するために、JavaプラットフォームはバッファリングされたI/Oストリームを実装します。バッファリングされた入力ストリームは、バッファと呼ばれるメモリ領域からデータを読み取ります。ネイティブ入力APIは、バッファーが空の場合にのみ呼び出されます。同様に、バッファリングされた出力ストリームはデータをバッファに書き込み、ネイティブ出力APIはバッファがいっぱいになったときにのみ呼び出されます。

技術文書を読んで髪が抜けてしまうことがあります。だから、ここで私はより人道的な説明https://yfain.github.io/Java4Kids/ から引用します:

一般に、ディスクアクセスは、メモリで実行される処理よりもはるかに低速です。そのため、1,000バイトのファイルを読み取るためにディスクに1000回アクセスすることはお勧めできません。ディスクへのアクセス回数を最小限に抑えるために、Javaはデータのリザーバーとして機能するバッファーを提供します。

enter image description here

FileInputStream、次にBufferedInputStreamを使用してFileを読み取る場合、クラスBufferedInputStreamは、FileInputStreamとファイル自体の間の仲介役として機能します。ファイルからメモリ(バッファ)にバイトの大きなチャンクを一度に読み取り、FileInputStreamオブジェクトはそこから1バイトを読み取ります。これは、メモリ間の高速操作です。 BufferedOutputStreamは、クラスFileOutputStreamと同様に機能します。

ここでの主なアイデアは、ディスクアクセスを最小限に抑えることです。バッファリングされたストリームは、元のストリームのタイプを変更するのではなく、読み取りをより効率的にするだけです。プログラムは、パイプが配管で接続されているのと同じように、ストリームチェーン(またはストリーム配管)を実行してストリームを接続します。

  • _InputStream, OutputStream, byte[], ByteBuffer_はバイナリデータ用です。
  • _Reader, Writer, String, char_はtext用で、内部的にUnicodeであるため、世界中のすべてのスクリプトを組み合わせることができます(ギリシャ語とアラビア語など)。

  • InputStreamReaderOutputStreamWriterは、両方の間のブリッジを形成します。 InputStreamがあり、そのバイトが実際には何らかのエンコーディングであるCharsetのテキストであることがわかっている場合は、InputStreamをラップできます。

    _try (InputStreamReader reader =
            new InputStreamReader(stream, StandardCharsets.UTF_8)) {
         ... read text ...
    }
    _

Charsetのないコンストラクターがありますが、デフォルトのプラットフォームエンコーディングを使用しているため、移植性はありません。

Android StandardCharsetが存在しない可能性がある場合は、「UTF-8」を使用してください。

派生クラスFileInputStreamおよびBufferedReaderは、親InputStreamまたはそれぞれに何かを追加します。 Reader

FileInputStreamはFileからの入力用であり、BufferedReaderはメモリバッファーを使用するため、実際の物理的な読み取りは文字単位で読み取られません(非効率的)。 new BufferedReader(otherReader)を使用して、元のリーダーにバッファリングを追加します。

これをすべて理解すると、newBufferedReader(Path, Charset)のようなメソッドを持つユーティリティクラスFilesがあり、簡潔さが増します。

2
Joop Eggen

私はこのトピックに関するたくさんの記事を読みました。これが何らかの形でお役に立てば幸いです。

基本的に、BufferedReaderは内部バッファを維持します。

読み取り操作中に、bulk内のファイルからバイトを読み取り、そのバイトを内部バッファーに格納します。

これで、読み取り操作ごとに、その内部バッファーからプログラムにバイトが渡されます。

これにより、プログラムとファイルまたはディスク間の通信数が減少します。したがって、より効率的です。

0
Iamsudip