4

Ich studiere Android Entwicklung (ich ein Anfänger in der Programmierung im Allgemeinen bin) und das Lernen über HTTP Vernetzung und sah diesen Code in der Lektion:Wie funktionieren InputStream, InputStreamReader und BufferedReader in Java zusammen?

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

Ich verstehe nicht genau das, was INPUTSTREAM- Input und BufferedReader machen. Alle haben eine read() Methode und auch readLine() im Falle des BufferedReaders.Warum kann ich nicht nur den InputStream benutzen oder nur den InputStreamReader hinzufügen? Warum muss ich den BufferedReader hinzufügen? Ich weiß, dass es mit Effizienz zu tun hat, aber ich verstehe nicht wie.

Ich habe untersucht, und die documentation for the BufferedReader versucht, dies zu erklären, aber ich immer noch nicht, wer was tut:

Im Allgemeinen las jede Anforderung eines Reader eine entsprechende Leseanforderung verursacht aus dem zugrunde liegenden Zeichen oder Bytestrom gemacht werden. Es ist daher empfehlenswert, einen BufferedReader um jeden Reader zu wickeln, dessen read() - Operationen kostspielig sein können, wie FileReaders und InputStreamReaders. Beispiel:

BufferedReader in = new BufferedReader(new FileReader("foo.in")); puffert die Eingabe aus der angegebenen Datei. Ohne Pufferung könnte jeder Aufruf von read() oder readLine() dazu führen, dass Bytes aus der Datei gelesen, in Zeichen konvertiert und dann zurückgegeben werden. Dies kann sehr ineffizient sein.

Also, ich verstehe, dass die Input nur ein Byte lesen, die Input ein einzelnes Zeichen, und die BufferedReader eine ganze Linie und dass es auch etwas über Effizienz tut das, was ich nicht bekommen. Ich hätte gerne ein besseres Verständnis davon, wer was macht, um zu verstehen, warum ich alle drei brauche und was der Unterschied ohne einen von ihnen wäre.

Ich habe viel hier und anderswo im Web recherchiert und finde keine Erklärung dafür, die ich verstehen kann, fast alle Tutorials wiederholen einfach die Dokumentationsinfos. Hier sind einige verwandte Fragen, die vielleicht beginnen, dies zu erklären, aber gehen Sie nicht tiefer und lösen Sie meine Verwirrung: Q1, Q2, Q3, Q4. Ich denke, es könnte mit der Erklärung dieser letzten Frage über Systemaufrufe und die Rückkehr zu tun haben. Aber ich möchte verstehen, was damit gemeint ist.

Könnte es sein, dass die readLine() des BufferedReaders die read() -Methode des InputStreamReader aufruft, die wiederum die read() -Methode des InputStream aufruft? Und der InputStream gibt Bytes zurück, die in int konvertiert wurden, wobei ein einzelnes Byte nach dem anderen zurückgegeben wird. Der InputStreamReader liest genug von diesen Zeichen, um ein einzelnes Zeichen zu erzeugen, konvertiert es in int und gibt jeweils ein einzelnes Zeichen zurück, und der BufferedReader liest genügend dieser Zeichen als ganze Zahlen dargestellt, um eine ganze Zeile zu bilden? Und gibt die ganze Zeile als String zurück und kehrt nur einmal statt mehrmals zurück? Ich weiß nicht, ich versuche nur zu verstehen, wie die Dinge funktionieren.

Vielen Dank im Voraus!

+3

http://stackoverflow.com/questions/32175221/what-is-the-relation-between-inputstream-bufferedinputstream-inputstreamreade?rq=1? –

+0

Danke für den Vorschlag @RC. Ich hatte diese Frage bereits gesehen und sie in meiner eigenen Frage erwähnt. Ich suche nach etwas genauerem, was zwischen den dreien passiert. – schv09

Antwort

10

Dieser Streams in Java concepts and usage Link, geben Sie eine sehr schöne Erklärungen.

This

Bäche, Leser, Autoren, BufferedReader, BufferedWriter - das sind die Terminologien Sie mit in Java beschäftigen.Es gibt die Klassen, die in Java zur Verfügung stehen, um mit Eingabe und Ausgabe zu arbeiten. Es ist wirklich wert zu wissen, wie diese verwandt sind und wie sie verwendet werden. In diesem Post werden die Streams in Java und anderen verwandten Klassen ausführlich beschrieben. Also lassen Sie uns beginnen:

Lassen Sie uns jede dieser in hoher Ebene definieren dann tiefer graben.

Streams
Verwendet mit Byte-Ebene Daten

Reader/Writer
gebraucht, um mit Charakter Ebene zu behandeln. Es unterstützt auch verschiedene Zeichencodierungen.

BufferedReader/BufferedWriter
Leistung zu erhöhen. Die zu lesenden Daten werden für einen schnellen Zugriff zwischengespeichert.

Während diese für die Eingabe sind, existieren nur die entsprechenden Klassen für die Ausgabe. Zum Beispiel, wenn es einen InputStream gibt, der Stream von Byte lesen soll, und OutputStream hilft beim Schreiben von Bytes.

Inputstreams
Es gibt viele Arten von Inputstreams Java zur Verfügung stellt. Jeder stellt eine Verbindung zu verschiedenen Datenquellen wie Byte-Array, Datei usw. her.

Zum Beispiel verbindet sich FileInputStream mit einer Datei Datenquelle und könnte verwendet werden, um Bytes aus einer Datei zu lesen. ByteArrayInputStream kann verwendet werden, um Byte-Array als Eingabestream zu behandeln.

Output
Dies hilft, zu einer Datenquelle schriftlich Bytes. Für fast jeden InputStream gibt es einen entsprechenden OutputStream, wo immer es Sinn macht.


UPDATE

Was Stream-Buffered ist?

Hier ich zitiere aus Buffered Streams, Java-Dokumentation (mit einer technischen Erklärung):

Buffered Streams

Die meisten der Beispiele, die wir bisher ungepufferte ich gesehen habe/O . Das bedeutet jede Lese- oder Schreibanforderung wird direkt vom zugrunde liegenden Betriebssystem bearbeitet. Dies kann ein Programm viel weniger effizient machen, da jede solche Anfrage häufig Datenträgerzugriff, Netzwerkaktivität oder eine andere Operation auslöst, die relativ teuer ist.

Um diese Art von Overhead zu reduzieren, implementiert die Java-Plattform gepufferte I/O-Streams. Gepufferte Eingangsströme lesen Daten aus einem bekannten Speicherbereich als Puffer; Die native Eingabe-API wird nur aufgerufen, wenn der Puffer leer ist. In ähnlicher Weise schreiben gepufferte Ausgabeströme Daten in einen Puffer, und Die native Ausgabe-API wird nur aufgerufen, wenn der Puffer voll ist.

Manchmal verliere ich meine Haare lesen eine technische Dokumentation. Also, hier zitiere ich die humanere Erklärung von https://yfain.github.io/Java4Kids/:

Im Allgemeinen Plattenzugriff ist wesentlich langsamer als die Verarbeitung im Speicher ausgeführt wird; deshalb ist es keine gute Idee, tausendmal auf die Festplatte zuzugreifen, um eine Datei mit 1.000 Bytes zu lesen. Um die Anzahl der Zeiten zu minimieren, auf die auf die Festplatte zugegriffen wird, stellt Java Puffer bereit, die als Datenspeicher dienen.

enter image description here

Datei mit Fileinputstream dann BufferedInputStream, die Klasse BufferedInputStream arbeitet als Vermittler zwischen Fileinputstream und die Datei selbst Bei der Lektüre. Es liest einen großen Teil der Bytes aus einer Datei in Speicher (ein Puffer) in einem Schuss, und das FileInputStream -Objekt liest einzelne Bytes von dort, die schnelle Speicher-to-Memory Operationen sind. BufferedOutputStream funktioniert ähnlich wie die Klasse FileOutputStream.

Die Grundidee hier ist, den Festplattenzugriff zu minimieren. Gepufferte Streams sind nicht den Typ der ursprünglichen Streams ändern - sie machen nur Lesen effizienter. Ein Programm führt eine Stream-Verkettung (oder Stream-Verrohrung) durch, um Streams zu verbinden, genau wie Pipes in der Installation verbunden sind.

+2

Dies ist eine großartige Antwort. Ich wollte nur den Punkt unterstreichen, dass Java COULD nur eine wirklich einfache Methode wie String Text = new File ("data.txt"), getText() zum Lesen des gesamten Textes bereitgestellt hat. Java 8 bietet tatsächlich so etwas, aber im Allgemeinen gibt Java Ihnen nur die Teile und lässt Sie sie beliebig zusammenfügen (Näher an Pythons Methode, einen einzigen Weg zur Lösung eines Problems zu bieten, als Rubys "Make the common" Ding einfach und die seltene Sache möglich "- was oft zu mehreren Möglichkeiten führt, um Probleme zu lösen.) –

+0

Danke, @ ששש אאבב אאךך. Ich verstehe die ersten beiden Teile des Diagramms, der InputStreamReader liest die vom InputStream gelesenen Bytes und codiert sie in Zeichen. Aber ich bekomme nicht die Pufferung, wie hilft der BufferedReader? Was bedeutet diese Zeile: "Um die Leistung zu erhöhen. Die zu lesenden Daten werden für einen schnellen Zugriff zwischengespeichert." Was macht es in weniger technischen Worten? – schv09

+1

Großartiges Update, @ ששש אאבב אאךך! Danke, ich würde nur noch eine Sache hinzufügen, in Bezug darauf, wie diese 3 Klassen spezifisch zusammenarbeiten. Beim weiteren Lesen und Überfliegen des OpenJDK-Quellcodes auf hoher Ebene geschieht Folgendes: BufferedReader ist dafür zuständig, ein char-Array zu verwalten und aufzufüllen. Seine read() - oder readLine() -Methoden werden von hier Zeichen erhalten. Es wird gefüllt, indem die Methode read (char [], int, int) von InputStreamReader aufgerufen wird. InputStreamReader ist mit einem StreamDecoder verknüpft, wenn es gelesen (char [], int, int) aufgerufen wird, ruft es den StreamDecoder auf (char [], int, int). (1/2) – schv09

1
  • InputStream, OutputStream, byte[], ByteBuffer sind für binäre Daten.
  • Reader, Writer, String, char sind für Text, intern Unicode, so dass alle Skripte in der Welt kombiniert werden können (sagen, Griechisch und Arabisch).

  • InputStreamReader und OutputStreamWriter bilden eine Brücke zwischen beiden. Wenn Sie einige Input haben und wissen, dass seine Bytes tatsächlich Text in irgendeiner Codierung ist, Charset, dann können Sie die Input wickeln:

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

Es ist ein Konstruktor ohne Charset, aber das ist nicht tragbar, wie Es verwendet die standardmäßige Plattformcodierung.

Auf Android StandardCharset möglicherweise nicht vorhanden, verwenden Sie "UTF-8".

Die abgeleiteten Klassen FileInputStream und BufferedReader fügen etwas zum übergeordneten InputStream hinzu. Reader.

Ein FileInputStream ist für die Eingabe aus einer Datei, und BufferedReader verwendet einen Speicherpuffer, so dass das tatsächliche physische Lesen nicht character weise (ineffizient) liest. Mit new BufferedReader(otherReader) fügen Sie Puffer zu Ihrem ursprünglichen Leser hinzu.

All dies verstanden, gibt es die Nutzungsklasse Files mit Methoden wie newBufferedReader(Path, Charset), die zusätzliche Kürze hinzufügen.

+0

Danke, @Joop, ich habe immer noch nicht den letzten Teil bekommen. Wie hilft der BufferReader? Ich verstehe diese Zeile aus der Dokumentation nicht: Der BufferReader "puffert die Eingabe von der angegebenen Datei. Ohne Pufferung könnte jeder Aufruf von read() oder readLine() dazu führen, dass Bytes aus der Datei gelesen, in Zeichen umgewandelt werden. und dann zurückgegeben, was sehr ineffizient sein kann. " Der Code dafür ist im Zitat in meiner Frage. Was ist der Unterschied bei der Verwendung eines BufferedReaders? – schv09

+1

Ich habe ein bisschen von Ihrem Kommentar zu der Antwort für andere hinzugefügt. Erklären Sagen wir, der BufferedReader liest endlich aus einer Datei. Ein normaler Leser würde das Betriebssystem bitten, wiederholt ein Zeichen/Byte zu lesen und in einer Schleife auf Dateiende zu prüfen. Das Lesen von Puffern beschleunigt dies, da nicht "int read()", sondern "int read (byte [], int, int)" intern für das Betriebssystem verwendet wird. So ist es oft _faster_, obwohl der Anwendungsfall abhängt. Es sollte keinen Unterschied in der Funktionalität geben. –

Verwandte Themen