Wie lese / konvertiere ich einen InputStream in einen String in Java?


4063

Wenn Sie ein java.io.InputStreamObjekt haben, wie sollten Sie dieses Objekt verarbeiten und ein Objekt erstellen String?


Angenommen, ich habe eine InputStream, die Textdaten enthält, und ich möchte sie in eine konvertieren String, damit ich sie beispielsweise in eine Protokolldatei schreiben kann.

Was ist der einfachste Weg, das zu nehmen InputStreamund es in ein umzuwandeln String?

public String convertStreamToString(InputStream is) {
    // ???
}

36
Die Antworten auf diese Frage funktionieren nur, wenn Sie den Inhalt des Streams vollständig lesen möchten (bis er geschlossen ist). Da dies nicht immer beabsichtigt ist (http-Anforderungen mit einer Keep-Alive-Verbindung werden nicht geschlossen), werden diese Methodenaufrufe blockiert (ohne dass Sie den Inhalt erhalten).
f1sh

21
Sie müssen wissen , und die Zeichenkodierung für den Stream angeben, oder Sie werden Zeichencodierung Fehler haben, da Sie je eine zufällig ausgewählte codiert , wird unter Verwendung von auf welcher Maschine / Betriebssystem / Plattform oder Version davon Code auf ausgeführt wird . Verwenden Sie keine Methoden, die von der Standardcodierung der Plattform abhängen.
Christoffer Hammarström

11
Um Spaß mit meinem eigenen Kommentar von vor 9 Jahren zu haben, verwende ich heutzutage Groovys "String s = new File (" SomeFile.txt "). Text", um eine ganze Datei auf einmal zu lesen, und es funktioniert großartig. Ich bin zufrieden damit, groovy für meinen Nicht-Produktionscode (Scripting) zu verwenden, und - ehrlich gesagt, Sie dazu zu zwingen, mit Codierung und extrem langen Dateien so umzugehen, wie es Java tut, ist sowieso eine wirklich gute Idee für Produktionscode, damit es für seinen Zweck funktioniert. Groovy funktioniert für schnelle Skripte, in denen Java nicht besonders gut ist. Verwenden Sie einfach das richtige Tool für den Job, und alles funktioniert.
Bill K

Nur zur Vereinfachung: ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); for(byte[] b = new byte[512]; 0 < inputStream.read(b); outputBytes.write(b)); return new String(outputBytes.toByteArray(), StandardCharsets.UTF_8);
Felypp Oliveira

@BillK mit Java 11 können Sie verwenden, String s = Files.readString​(Path.of("SomeFile.txt"));was so gut wie eine Sprache ist, die niemals solche magischen Konvertierungen wie die von Ihnen beschriebene unterstützt.
Holger

Antworten:


2530

Ein guter Weg, dies zu tun, ist die Verwendung von Apache Commons, IOUtils um das InputStreamin ein StringWriter... so etwas wie zu kopieren

StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, encoding);
String theString = writer.toString();

oder auch

// NB: does not close inputStream, you'll have to use try-with-resources for that
String theString = IOUtils.toString(inputStream, encoding); 

Alternativ können Sie verwenden, ByteArrayOutputStreamwenn Sie Ihre Streams und Writer nicht mischen möchten


75
Für Android-Entwickler scheint Android nicht mit IOUtils von Apache zu kommen. Sie können sich also auf andere Antworten beziehen.
Chris.Zou

47
Dies ist zu diesem Zeitpunkt eine unglaublich alte Frage (sie wurde 2008 gestellt). Es lohnt sich, modernere Antworten durchzulesen. Einige verwenden native Aufrufe aus der Java 8-Bibliothek.
Shadoninja

36
Diese Antwort ist stark veraltet und sollte in der Lage sein, sie als solche zu markieren (leider ist dies nicht möglich).
Codepleb

7
IOUtils.toString () ist seit langem veraltet. Diese Antwort ist definitiv nicht mehr der empfohlene Weg.
Roshan

7
Bearbeiten Sie es dann, um zu erklären, warum es veraltet ist, um zukünftigen Lesern zu helfen.
Jean-François Fabre

2486

Fassen Sie andere Antworten zusammen. Ich habe 11 Hauptmethoden gefunden, um dies zu tun (siehe unten). Und ich habe einige Leistungstests geschrieben (siehe Ergebnisse unten):

Möglichkeiten zum Konvertieren eines InputStream in einen String:

  1. Verwenden von IOUtils.toString(Apache Utils)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  2. Verwenden von CharStreams(Guave)

    String result = CharStreams.toString(new InputStreamReader(
          inputStream, Charsets.UTF_8));
  3. Verwenden von Scanner(JDK)

    Scanner s = new Scanner(inputStream).useDelimiter("\\A");
    String result = s.hasNext() ? s.next() : "";
  4. Verwenden der Stream-API (Java 8). Warnung : Diese Lösung konvertiert verschiedene Zeilenumbrüche (wie \r\n) in \n.

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
  5. Verwenden der parallelen Stream-API (Java 8). Warnung : Diese Lösung konvertiert verschiedene Zeilenumbrüche (wie \r\n) in \n.

    String result = new BufferedReader(new InputStreamReader(inputStream)).lines()
       .parallel().collect(Collectors.joining("\n"));
  6. Verwenden von InputStreamReaderund StringBuilder(JDK)

    final int bufferSize = 1024;
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
    int charsRead;
    while((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
        out.append(buffer, 0, charsRead);
    }
    return out.toString();
  7. Verwenden von StringWriterund IOUtils.copy(Apache Commons)

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, "UTF-8");
    return writer.toString();
  8. Verwenden von ByteArrayOutputStreamund inputStream.read(JDK)

    ByteArrayOutputStream result = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) != -1) {
        result.write(buffer, 0, length);
    }
    // StandardCharsets.UTF_8.name() > JDK 7
    return result.toString("UTF-8");
  9. Verwenden von BufferedReader(JDK). Warnung: Diese Lösung konvertiert verschiedene Zeilenumbrüche (wie \n\r) in line.separatorSystemeigenschaften (z. B. in Windows in "\ r \ n").

    String newLine = System.getProperty("line.separator");
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    StringBuilder result = new StringBuilder();
    boolean flag = false;
    for (String line; (line = reader.readLine()) != null; ) {
        result.append(flag? newLine: "").append(line);
        flag = true;
    }
    return result.toString();
  10. Verwenden von BufferedInputStreamund ByteArrayOutputStream(JDK)

    BufferedInputStream bis = new BufferedInputStream(inputStream);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();
    while(result != -1) {
        buf.write((byte) result);
        result = bis.read();
    }
    // StandardCharsets.UTF_8.name() > JDK 7
    return buf.toString("UTF-8");
  11. Verwenden von inputStream.read()und StringBuilder(JDK). Warnung : Diese Lösung hat Probleme mit Unicode, z. B. mit russischem Text (funktioniert nur mit Nicht-Unicode-Text korrekt).

    int ch;
    StringBuilder sb = new StringBuilder();
    while((ch = inputStream.read()) != -1)
        sb.append((char)ch);
    reset();
    return sb.toString();

Warnung :

  1. Die Lösungen 4, 5 und 9 konvertieren verschiedene Zeilenumbrüche in einen.

  2. Lösung 11 kann mit Unicode-Text nicht richtig funktionieren

Leistungstests

Leistungstests für kleine String(Länge = 175), URL in Github (Modus = durchschnittliche Zeit, System = Linux, Punktzahl 1.343 ist die beste):

              Benchmark                         Mode  Cnt   Score   Error  Units
 8. ByteArrayOutputStream and read (JDK)        avgt   10   1,343 ± 0,028  us/op
 6. InputStreamReader and StringBuilder (JDK)   avgt   10   6,980 ± 0,404  us/op
10. BufferedInputStream, ByteArrayOutputStream  avgt   10   7,437 ± 0,735  us/op
11. InputStream.read() and StringBuilder (JDK)  avgt   10   8,977 ± 0,328  us/op
 7. StringWriter and IOUtils.copy (Apache)      avgt   10  10,613 ± 0,599  us/op
 1. IOUtils.toString (Apache Utils)             avgt   10  10,605 ± 0,527  us/op
 3. Scanner (JDK)                               avgt   10  12,083 ± 0,293  us/op
 2. CharStreams (guava)                         avgt   10  12,999 ± 0,514  us/op
 4. Stream Api (Java 8)                         avgt   10  15,811 ± 0,605  us/op
 9. BufferedReader (JDK)                        avgt   10  16,038 ± 0,711  us/op
 5. parallel Stream Api (Java 8)                avgt   10  21,544 ± 0,583  us/op

Leistungstests für große String(Länge = 50100), URL in Github (Modus = durchschnittliche Zeit, System = Linux, Punktzahl 200.715 ist die beste):

               Benchmark                        Mode  Cnt   Score        Error  Units
 8. ByteArrayOutputStream and read (JDK)        avgt   10   200,715 ±   18,103  us/op
 1. IOUtils.toString (Apache Utils)             avgt   10   300,019 ±    8,751  us/op
 6. InputStreamReader and StringBuilder (JDK)   avgt   10   347,616 ±  130,348  us/op
 7. StringWriter and IOUtils.copy (Apache)      avgt   10   352,791 ±  105,337  us/op
 2. CharStreams (guava)                         avgt   10   420,137 ±   59,877  us/op
 9. BufferedReader (JDK)                        avgt   10   632,028 ±   17,002  us/op
 5. parallel Stream Api (Java 8)                avgt   10   662,999 ±   46,199  us/op
 4. Stream Api (Java 8)                         avgt   10   701,269 ±   82,296  us/op
10. BufferedInputStream, ByteArrayOutputStream  avgt   10   740,837 ±    5,613  us/op
 3. Scanner (JDK)                               avgt   10   751,417 ±   62,026  us/op
11. InputStream.read() and StringBuilder (JDK)  avgt   10  2919,350 ± 1101,942  us/op

Diagramme (Leistungstests abhängig von der Länge des Eingabestreams im Windows 7-System)
Geben Sie hier die Bildbeschreibung ein

Leistungstest (durchschnittliche Zeit) abhängig von der Länge des Eingabestreams im Windows 7-System:

 length  182    546     1092    3276    9828    29484   58968

 test8  0.38    0.938   1.868   4.448   13.412  36.459  72.708
 test4  2.362   3.609   5.573   12.769  40.74   81.415  159.864
 test5  3.881   5.075   6.904   14.123  50.258  129.937 166.162
 test9  2.237   3.493   5.422   11.977  45.98   89.336  177.39
 test6  1.261   2.12    4.38    10.698  31.821  86.106  186.636
 test7  1.601   2.391   3.646   8.367   38.196  110.221 211.016
 test1  1.529   2.381   3.527   8.411   40.551  105.16  212.573
 test3  3.035   3.934   8.606   20.858  61.571  118.744 235.428
 test2  3.136   6.238   10.508  33.48   43.532  118.044 239.481
 test10 1.593   4.736   7.527   20.557  59.856  162.907 323.147
 test11 3.913   11.506  23.26   68.644  207.591 600.444 1211.545

17
Während Sie die "zusammenfassende Antwort" schreiben, sollten Sie beachten, dass einige Lösungen automatisch verschiedene Zeilenumbrüche (wie \r\n) konvertieren, in \ndie in einigen Fällen unerwünscht sein kann. Es wäre auch schön, den zusätzlichen Speicherbedarf oder zumindest den Zuordnungsdruck zu sehen (zumindest können Sie JMH mit ausführen -prof gc). Für den wirklich coolen Beitrag wäre es großartig, die Grafiken zu sehen (abhängig von der Zeichenfolgenlänge innerhalb derselben Eingabegröße und abhängig von der Eingabegröße innerhalb derselben Zeichenfolgenlänge).
Tagir Valeev

16
Upvoted; Das Lustigste ist, dass die Ergebnisse mehr als erwartet sind: Man sollte standardmäßigen syntaktischen JDK- und / oder Apache Commons-Zucker verwenden.
Aleksei Matiushkin

25
Erstaunlicher Beitrag. Nur eine Sache. Java 8 warnt davor, parallele Streams für Ressourcen zu verwenden, die Sie zum Sperren und Warten zwingen (z. B. diesen Eingabestream), sodass die Option für parallele Streams ziemlich umständlich ist und sich nicht lohnt, nein?
Mangusbruder

10
Behält der parallele Stream tatsächlich die Leitungsreihenfolge bei?
Natix

6
Wofür ist reset()in Beispiel 11?
Rob Stewart

2307

Hier ist eine Möglichkeit, nur die Standard-Java-Bibliothek zu verwenden (beachten Sie, dass der Stream nicht geschlossen ist, Ihr Kilometerstand kann variieren).

static String convertStreamToString(java.io.InputStream is) {
    java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";
}

Ich habe diesen Trick aus dem Artikel "Dumme Scanner-Tricks" gelernt . Der Grund dafür ist, dass der Scanner über Token im Stream iteriert. In diesem Fall trennen wir Token mithilfe des "Beginns der Eingabegrenze" (\ A), sodass wir nur ein Token für den gesamten Inhalt des Streams erhalten.

Hinweis: Wenn Sie die Codierung des Eingabestreams genau Scannerangeben müssen , können Sie dem Konstruktor das zweite Argument bereitstellen, das angibt, welcher Zeichensatz verwendet werden soll (z. B. "UTF-8").

Die Hutspitze geht auch an Jacob , der mich einmal auf den genannten Artikel hingewiesen hat.


8
Vielen Dank, für meine Version habe ich einen finally-Block hinzugefügt, der den Eingabestream schließt, sodass der Benutzer dies nicht tun muss, da Sie die Eingabe gelesen haben. Vereinfacht den Anrufercode erheblich.

4
@PavelRepin @Patrick In meinem Fall verursachte ein leerer inputStream eine NPE während der Scannerkonstruktion. Ich musste if (is == null) return "";gleich zu Beginn der Methode hinzufügen ; Ich glaube, diese Antwort muss aktualisiert werden, um null inputStreams besser verarbeiten zu können.
CFL_Jeff

115
Für Java 7 können Sie in einem Versuch mit schließen: try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
earcam

5
Leider scheint diese Lösung die Ausnahmen zu verlieren, die in meiner zugrunde liegenden Stream-Implementierung ausgelöst wurden.
Taig

11
Zu Ihrer Information , hasNext- Blöcke in Konsoleneingabestreams (siehe hier ). (Ich bin gerade auf dieses Problem gestoßen.) Diese Lösung funktioniert ansonsten einwandfrei ... nur ein Kopf hoch.
Ryan

848

Apache Commons erlaubt:

String myString = IOUtils.toString(myInputStream, "UTF-8");

Natürlich können Sie neben UTF-8 auch andere Zeichenkodierungen wählen.

Siehe auch: ( Dokumentation )


1
Es gibt auch eine Methode, die nur dann ein inputStream-Argument verwendet, wenn Sie mit der Standardcodierung gefunden werden.
Guillaume Coté

13
@ Guillaume Coté Ich denke, die Nachricht hier ist, dass Sie niemals "mit der Standardcodierung in Ordnung sein sollten", da Sie nicht sicher sein können, was es ist, abhängig von der Plattform, auf der der Java-Code ausgeführt wird.
Per Wiklander

7
@Per Wiklander Ich stimme dir nicht zu. Code, der auf einem einzelnen Code funktioniert, kann ziemlich sicher sein, dass die Standardcodierung in Ordnung ist. Für Code, der nur lokale Dateien öffnet, ist es eine vernünftige Option, sie zu bitten, in der Standardcodierung der Plattform codiert zu werden.
Guillaume Coté

39
Um jedem den Ärger des Googelns zu ersparen - <dependency> <groupId> org.apache.commons </ groupId> <artifactId> commons-io </ifactId> <version> 1.3.2 </ version> </ dependency>
Chris

7
Eine kleine Verbesserung wäre auch die Verwendung der Apache io (oder einer anderen) Konstante für die Zeichenkodierung anstelle der Verwendung eines einfachen Zeichenfolgenliteral - z. B.: IOUtils.toString (myInputStream, Charsets.UTF_8);

300

Unter Berücksichtigung der Datei sollte man zuerst eine java.io.ReaderInstanz bekommen. Dies kann dann gelesen und zu a hinzugefügt werden StringBuilder(wir brauchen es nicht, StringBufferwenn wir nicht in mehreren Threads darauf zugreifen, und es StringBuilderist schneller). Der Trick dabei ist, dass wir in Blöcken arbeiten und daher keine anderen Pufferströme benötigen. Die Blockgröße wird zur Laufzeitleistungsoptimierung parametriert.

public static String slurp(final InputStream is, final int bufferSize) {
    final char[] buffer = new char[bufferSize];
    final StringBuilder out = new StringBuilder();
    try (Reader in = new InputStreamReader(is, "UTF-8")) {
        for (;;) {
            int rsz = in.read(buffer, 0, buffer.length);
            if (rsz < 0)
                break;
            out.append(buffer, 0, rsz);
        }
    }
    catch (UnsupportedEncodingException ex) {
        /* ... */
    }
    catch (IOException ex) {
        /* ... */
    }
    return out.toString();
}

8
Diese Lösung verwendet Multibyte-Zeichen. In diesem Beispiel wird die UTF-8-Codierung verwendet, mit der der gesamte Unicode-Bereich (einschließlich Chinesisch) ausgedrückt werden kann. Das Ersetzen von "UTF-8" durch eine andere Codierung würde die Verwendung dieser Codierung ermöglichen.
Paul de Vrieze

27
@ User1 - Ich verwende gerne Bibliotheken in meinem Code, damit ich meine Arbeit schneller erledigen kann. Es ist großartig, wenn Ihre Manager sagen "Wow James! Wie haben Sie das so schnell geschafft?!". Aber wenn wir Zeit damit verbringen müssen, das Rad neu zu erfinden, nur weil wir falsche Vorstellungen über die Aufnahme eines gemeinsamen, wiederverwendbaren, bewährten Dienstprogramms haben, geben wir Zeit auf, die wir für die Förderung der Ziele unseres Projekts aufwenden könnten. Wenn wir das Rad neu erfinden, arbeiten wir doppelt so hart und erreichen die Ziellinie viel später. Sobald wir die Ziellinie erreicht haben, ist niemand da, der uns gratuliert. Wenn Sie ein Haus bauen, bauen Sie nicht auch den Hammer
jmort253

10
Entschuldigung, nachdem ich meinen Kommentar noch einmal gelesen habe, wirkt er etwas arrogant. Ich denke nur, dass es wichtig ist, einen guten Grund zu haben, Bibliotheken zu meiden, und dass der Grund ein gültiger ist, der sehr gut sein könnte :)
jmort253

4
@ jmort253 Wir haben eine Leistungsregression festgestellt, nachdem wir einige Bibliotheken in unserem Produkt mehrmals aktualisiert haben. Glücklicherweise bauen und verkaufen wir unser eigenes Produkt, so dass wir nicht wirklich die sogenannten Fristen haben. Leider bauen wir ein Produkt, das auf vielen JVMs, Datenbanken und App-Servern auf vielen Betriebssystemen verfügbar ist, sodass wir für die Benutzer denken müssen, die schlechte Maschinen verwenden ... Und eine Optimierung der Zeichenfolgenoperation kann die Leistung um 30 bis 40% verbessern. Und eine Lösung: In our product, I even replacedsollte "wir haben sogar ersetzt" sein.
Coolcfan

10
@ jmort253 Wenn Sie bereits Apache Commons verwenden würden, würde ich sagen, machen Sie es. Gleichzeitig ist die Verwendung von Bibliotheken mit echten Kosten verbunden (wie die Abhängigkeitsproliferation in vielen Apache-Java-Bibliotheken zeigt). Wenn dies die einzige Verwendung der Bibliothek wäre, wäre es übertrieben, die Bibliothek zu verwenden. Wenn Sie andererseits Ihre eigene Puffergröße (n) bestimmen, können Sie das Gleichgewicht zwischen Speicher und Prozessornutzung optimieren.
Paul de Vrieze

248

Verwenden:

InputStream in = /* Your InputStream */;
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String read;

while ((read=br.readLine()) != null) {
    //System.out.println(read);
    sb.append(read);
}

br.close();
return sb.toString();

11
Die Sache ist, dass Sie sich zuerst in Zeilen aufteilen und diese dann rückgängig machen. Es ist einfacher und schneller, nur beliebige Puffer zu lesen.
Paul de Vrieze

20
Außerdem unterscheidet readLine nicht zwischen \ n und \ r, sodass Sie den genauen Stream nicht erneut reproduzieren können.
Maria Arias de Reyna Domínguez

2
Sehr ineffizient, da readLineZeichen für Zeichen nach EOL gesucht wird. Auch wenn der Stream keinen Zeilenumbruch enthält, ist dies nicht wirklich sinnvoll.
njzk2

3
@Gops AB: Wenn Sie dies ausprobieren und Ihr Beispiel Zeilenumbrüche enthält, werden Sie feststellen, dass die Art und Weise, wie diese Schleife mit readline () und StringBuilder.append () erstellt wird, die Zeilenumbrüche tatsächlich nicht beibehält.
Russ Bateman

4
Dies ist nicht die beste Antwort, da es sich nicht ausschließlich um Byte für Byte handelt. Der Leser kaut Zeilenumbrüche, daher müssen Sie vorsichtig sein, um sie zu pflegen.
Jeffrey Blattman

173

Wenn Sie Google-Collections / Guava verwenden, können Sie Folgendes tun:

InputStream stream = ...
String content = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
Closeables.closeQuietly(stream);

Beachten Sie, dass der zweite Parameter (dh Charsets.UTF_8) für das InputStreamReadernicht erforderlich ist, aber es ist im Allgemeinen eine gute Idee, die Codierung anzugeben, wenn Sie sie kennen (was Sie sollten!).


2
@harschware: Die Frage lautete: "Wenn Sie ein java.io.InputStream-Objekt haben, wie sollten Sie dieses Objekt verarbeiten und einen String erzeugen?" Ich nahm an, dass in der Situation bereits ein Stream vorhanden ist.
Sakuraba

Sie haben Ihre Antwort nicht sehr gut erklärt und hatten fremde Variablen; user359996 hat dasselbe gesagt wie Sie, aber viel deutlicher.
Uronym

2
+1 für Guave, -1 für die Nichtangabe der Codierung des Eingabestreams. z.B. neuer InputStreamReader (Stream, "UTF-8")
andras

@ Chris Noldus Auf der anderen Seite haben einige Leute, wie ich, bereits Guaven in ihrem Projekt und denken, dass diese Lösung eleganter ist als die Nur-SDK-Version.
CorayThan

@ Vadzim diese Antwort ist die gleiche wie diese - beide verwenden CharStreams.toString
Tom

125

Dies ist die beste reine Java-Lösung, die perfekt für Android und jede andere JVM geeignet ist.

Diese Lösung funktioniert erstaunlich gut ... sie ist einfach, schnell und funktioniert trotzdem bei kleinen und großen Streams !! (siehe Benchmark oben .. Nr. 8 )

public String readFullyAsString(InputStream inputStream, String encoding)
        throws IOException {
    return readFully(inputStream).toString(encoding);
}

public byte[] readFullyAsBytes(InputStream inputStream)
        throws IOException {
    return readFully(inputStream).toByteArray();
}

private ByteArrayOutputStream readFully(InputStream inputStream)
        throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length = 0;
    while ((length = inputStream.read(buffer)) != -1) {
        baos.write(buffer, 0, length);
    }
    return baos;
}

4
Funktioniert gut auf Android im Vergleich zu anderen Antworten, die nur in Enterprise Java funktionieren.
Vortexwolf

In Android mit OutOfMemory-Fehler in der Zeile ".write" jedes Mal für kurze Zeichenfolgen abgestürzt.
Adam

Ich habe die Codierung hinzugefügt. Nur als Randnotiz: Die ursprüngliche readFully-Methode, die ich in meinem Code habe, gibt keinen String zurück, sondern Byte [] für eine allgemeinere Funktionalität. Die Implementierung des neuen Strings (...) mit Codierung liegt in der Verantwortung des Ons, der die API verwendet!
TacB0sS

2
Kurzer Hinweis: Der Speicherbedarf wird maximiert durch 2*n, wobei n die Größe des Streams gemäß dem ByteArrayInputStreamautomatisch wachsenden System ist.
njzk2

3
Verdoppelt unnötig die Speichernutzung, was auf Mobilgeräten sehr wichtig ist. Verwenden Sie besser InputStreamReader und hängen Sie ihn an StringReader an. Die Konvertierung von Byte in Zeichen erfolgt im laufenden Betrieb und nicht in großen Mengen am Ende.
Oliv

84

Der Vollständigkeit halber hier die Java 9- Lösung:

public static String toString(InputStream input) throws IOException {
    return new String(input.readAllBytes(), StandardCharsets.UTF_8);
}

Das readAllBytesbefindet sich derzeit in der JDK 9-Hauptcodebasis, sodass es wahrscheinlich in der Version erscheint. Sie können es jetzt mit den JDK 9-Snapshot-Builds versuchen .


Weist die Methode nicht viel Speicher zum Lesen zu? byte[] buf = new byte[DEFAULT_BUFFER_SIZE];wo MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;was gibt MAX_BUFFER_SIZE = 2147483639. Google sagt, es ist rund 2.147 GB.
Rekin

Entschuldigung, ich habe einen Fehler bei den Berechnungen gemacht. Es ist 2 GB. Ich habe den Kommentar bearbeitet. Also, selbst wenn ich wie eine 4-KB-Datei lese, verwende ich 2 GB Speicher?
Rekin

2
@ChristianHujer, ich sehe es nicht im neuesten jdk8u-Commit . AFAIK-neue Methoden werden in Java-Updates nie eingeführt, nur in Hauptversionen.
Tagir Valeev

4
@ChristianHujer, die Frage ging um InputStream, nicht um Path. Die InputStreamkönnen aus vielen verschiedenen Quellen erstellt werden, nicht nur aus Dateien.
Tagir Valeev

5
Dies wurde vor einem Jahr geschrieben. Um dies zu aktualisieren, bestätige ich, dass diese Methode tatsächlich in der öffentlichen Version JDK 9 enthalten ist. Wenn Ihre Codierung "ISO-Latin-1" ist, ist dies äußerst effizient, da Java 9 Strings jetzt verwendet werden Eine byte[]Implementierung, wenn sich alle Zeichen in den ersten 256 Codepunkten befinden. Dies bedeutet, dass der neue String (Byte [], "ISO-Latin-1") eine einfache Array-Kopie ist.
Klitos Kyriacou

66

Verwenden:

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;

public static String readInputStreamAsString(InputStream in)
    throws IOException {

    BufferedInputStream bis = new BufferedInputStream(in);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    int result = bis.read();
    while(result != -1) {
      byte b = (byte)result;
      buf.write(b);
      result = bis.read();
    }
    return buf.toString();
}

@ DanielDeLeón Nein, tut es nicht. Es ist ein BufferedInputStream. Die zugrunde liegenden Lesevorgänge betragen jeweils 8192 Byte.
user207421

2
@EJP Ich habe festgestellt, dass es langsamer ist als das Verwenden BufferedInputStream und Einlesen in einen Byte-Array-Puffer anstelle von jeweils einem Byte. Beispiel: 200 ms gegenüber 60 ms beim Lesen einer 4,56-MiB-Datei.
jk7

Seltsam, dass hier niemand auf das andere Hauptproblem hingewiesen hat (ja, das byteweise Lesen von Inhalten ist selbst beim Puffern verschwenderisch): Es hängt von der "Standardcodierung" ab - dies ist selten ein guter Weg. Stellen Sie stattdessen sicher, dass Sie die Codierung als Argument an übergeben buf.toString().
StaxMan

@ jk7 Die Zeit zum Lesen einer 4,56-MB-Datei ist so gering, dass die Unterschiede möglicherweise nicht signifikant gewesen sein können.
user207421

63

Hier ist die eleganteste reine Java-Lösung (keine Bibliothek), die ich nach einigen Experimenten gefunden habe:

public static String fromStream(InputStream in) throws IOException
{
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    StringBuilder out = new StringBuilder();
    String newLine = System.getProperty("line.separator");
    String line;
    while ((line = reader.readLine()) != null) {
        out.append(line);
        out.append(newLine);
    }
    return out.toString();
}

8
@TorbenKohlmeier, Leser und Puffer müssen nicht geschlossen werden. Das bereitgestellte InputStreamsollte vom Anrufer geschlossen werden.
Drew Noakes

7
Vergessen Sie nicht zu erwähnen, dass es in InputStreamReader einen bevorzugteren Konstruktor gibt, der ein CharSet akzeptiert.
Jontejj

7
Warum benutzen die Leute weiter readLine? Wenn Sie die Zeilen an sich nicht verwenden, was
nützt

4
Nicht durch Zeilen lesen. Was ist, wenn eine Zeile so lang ist, dass sie nicht in den Haufen passt?
Voho

4
@voho, wenn eine Zeile so lang ist, gibt es sowieso keine Möglichkeit, den Rückgabewert zuzuweisen, der dieser Zeile gleich oder größer sein muss. Wenn Sie mit so großen Dateien arbeiten, sollten Sie sie streamen. Es gibt jedoch viele Anwendungsfälle zum Laden kleiner Textdateien in den Speicher.
Drew Noakes

55

Ich habe hier einen Benchmark für 14 verschiedene Antworten durchgeführt (Entschuldigung, dass ich keine Credits bereitgestellt habe, aber es gibt zu viele Duplikate).

Das Ergebnis ist sehr überraschend. Es stellt sich heraus, dass Apache IOUtils am langsamsten ist undByteArrayOutputStream schnellste Lösung ist:

Hier ist also zuerst die beste Methode:

public String inputStreamToString(InputStream inputStream) throws IOException {
    try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = inputStream.read(buffer)) != -1) {
            result.write(buffer, 0, length);
        }

        return result.toString(UTF_8);
    }
}

Benchmark-Ergebnisse von 20 MB zufälligen Bytes in 20 Zyklen

Zeit in Millisekunden

  • ByteArrayOutputStreamTest: 194
  • NioStream: 198
  • Java9ISTransferTo: 201
  • Java9ISReadAllBytes: 205
  • BufferedInputStreamVsByteArrayOutputStream: 314
  • ApacheStringWriter2: 574
  • GuavaCharStreams: 589
  • ScannerReaderNoNextTest: 614
  • ScannerReader: 633
  • ApacheStringWriter: 1544
  • StreamApi: Fehler
  • ParallelStreamApi: Fehler
  • BufferReaderTest: Fehler
  • InputStreamAndStringBuilder: Fehler

Benchmark-Quellcode

import com.google.common.io.CharStreams;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * Created by Ilya Gazman on 2/13/18.
 */
public class InputStreamToString {


    private static final String UTF_8 = "UTF-8";

    public static void main(String... args) {
        log("App started");
        byte[] bytes = new byte[1024 * 1024];
        new Random().nextBytes(bytes);
        log("Stream is ready\n");

        try {
            test(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void test(byte[] bytes) throws IOException {
        List<Stringify> tests = Arrays.asList(
                new ApacheStringWriter(),
                new ApacheStringWriter2(),
                new NioStream(),
                new ScannerReader(),
                new ScannerReaderNoNextTest(),
                new GuavaCharStreams(),
                new StreamApi(),
                new ParallelStreamApi(),
                new ByteArrayOutputStreamTest(),
                new BufferReaderTest(),
                new BufferedInputStreamVsByteArrayOutputStream(),
                new InputStreamAndStringBuilder(),
                new Java9ISTransferTo(),
                new Java9ISReadAllBytes()
        );

        String solution = new String(bytes, "UTF-8");

        for (Stringify test : tests) {
            try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
                String s = test.inputStreamToString(inputStream);
                if (!s.equals(solution)) {
                    log(test.name() + ": Error");
                    continue;
                }
            }
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < 20; i++) {
                try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
                    test.inputStreamToString(inputStream);
                }
            }
            log(test.name() + ": " + (System.currentTimeMillis() - startTime));
        }
    }

    private static void log(String message) {
        System.out.println(message);
    }

    interface Stringify {
        String inputStreamToString(InputStream inputStream) throws IOException;

        default String name() {
            return this.getClass().getSimpleName();
        }
    }

    static class ApacheStringWriter implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputStream, writer, UTF_8);
            return writer.toString();
        }
    }

    static class ApacheStringWriter2 implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return IOUtils.toString(inputStream, UTF_8);
        }
    }

    static class NioStream implements Stringify {

        @Override
        public String inputStreamToString(InputStream in) throws IOException {
            ReadableByteChannel channel = Channels.newChannel(in);
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 16);
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            WritableByteChannel outChannel = Channels.newChannel(bout);
            while (channel.read(byteBuffer) > 0 || byteBuffer.position() > 0) {
                byteBuffer.flip();  //make buffer ready for write
                outChannel.write(byteBuffer);
                byteBuffer.compact(); //make buffer ready for reading
            }
            channel.close();
            outChannel.close();
            return bout.toString(UTF_8);
        }
    }

    static class ScannerReader implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            return s.hasNext() ? s.next() : "";
        }
    }

    static class ScannerReaderNoNextTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            return s.next();
        }
    }

    static class GuavaCharStreams implements Stringify {

        @Override
        public String inputStreamToString(InputStream is) throws IOException {
            return CharStreams.toString(new InputStreamReader(
                    is, UTF_8));
        }
    }

    static class StreamApi implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new BufferedReader(new InputStreamReader(inputStream))
                    .lines().collect(Collectors.joining("\n"));
        }
    }

    static class ParallelStreamApi implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new BufferedReader(new InputStreamReader(inputStream)).lines()
                    .parallel().collect(Collectors.joining("\n"));
        }
    }

    static class ByteArrayOutputStreamTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            try(ByteArrayOutputStream result = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    result.write(buffer, 0, length);
                }

                return result.toString(UTF_8);
            }
        }
    }

    static class BufferReaderTest implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            String newLine = System.getProperty("line.separator");
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder result = new StringBuilder(UTF_8);
            String line;
            boolean flag = false;
            while ((line = reader.readLine()) != null) {
                result.append(flag ? newLine : "").append(line);
                flag = true;
            }
            return result.toString();
        }
    }

    static class BufferedInputStreamVsByteArrayOutputStream implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            int result = bis.read();
            while (result != -1) {
                buf.write((byte) result);
                result = bis.read();
            }

            return buf.toString(UTF_8);
        }
    }

    static class InputStreamAndStringBuilder implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            int ch;
            StringBuilder sb = new StringBuilder(UTF_8);
            while ((ch = inputStream.read()) != -1)
                sb.append((char) ch);
            return sb.toString();
        }
    }

    static class Java9ISTransferTo implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            inputStream.transferTo(bos);
            return bos.toString(UTF_8);
        }
    }

    static class Java9ISReadAllBytes implements Stringify {

        @Override
        public String inputStreamToString(InputStream inputStream) throws IOException {
            return new String(inputStream.readAllBytes(), UTF_8);
        }
    }

}

Das Erstellen von Benchmarks in Java ist nicht einfach (insbesondere aufgrund von JIT). Nach dem Lesen des Benchmark-Quellcodes bin ich überzeugt, dass diese Werte nicht genau sind und jeder vorsichtig sein sollte, wenn er ihnen glaubt.
Dalibor

@ Dalibor Sie sollten wahrscheinlich mehr Gründe für Ihre Behauptung angeben als nur einen Link.
Ilya Gazman

Ich denke, dass es wirklich bekannt ist, dass es nicht einfach ist, einen eigenen Benchmark zu erstellen. Für diejenigen, die das nicht wissen, gibt es Link;)
Dalibor

@ Dalibor Ich bin vielleicht nicht der Beste, aber ich habe ein gutes Verständnis für Java-Benchmarks. Wenn Sie also nicht auf ein bestimmtes Problem hinweisen können, sind Sie nur irreführend und ich werde das Gespräch mit Ihnen unter diesen Bedingungen nicht fortsetzen.
Ilya Gazman

Meistens stimme ich Dalibor zu. Sie sagen, Sie haben ein "gutes Verständnis für Java-Benchmarks", aber Sie scheinen den naivsten Ansatz implementiert zu haben, ohne die bekannten Probleme dieses Ansatzes zu kennen. Lesen Sie zunächst jeden Beitrag zu dieser Frage: stackoverflow.com/questions/504103/…
DavidS

41

Ich würde einige Java 8-Tricks verwenden.

public static String streamToString(final InputStream inputStream) throws Exception {
    // buffering optional
    try
    (
        final BufferedReader br
           = new BufferedReader(new InputStreamReader(inputStream))
    ) {
        // parallel optional
        return br.lines().parallel().collect(Collectors.joining("\n"));
    } catch (final IOException e) {
        throw new RuntimeException(e);
        // whatever.
    }
}

Im Wesentlichen dasselbe wie einige andere Antworten, außer prägnanter.


5
Würde das return nulljemals genannt werden? Entweder die br.lines...Rückgabe oder eine Ausnahme wird ausgelöst.
Holloway

3
@Khaled A Khunaifer: Ja, ziemlich sicher ... vielleicht sollten Sie hier einen Blick darauf werfen: docs.oracle.com/javase/tutorial/essential/exceptions/… . Was Sie falsch bearbeitet haben, ist eine "Try-with-Resources" -Anweisung.
Jamp

11
Warum rufst du parallel()den Stream an?
Robinst

4
Dies würde nicht zu einer ehrlichen Kopie der Daten führen, wenn der Quelldatenstrom Windows-Zeilenenden verwendet, da alle \r\nin \n... konvertiert würden
Lucas

2
Sie können System.lineSeparator()das entsprechende plattformabhängige Zeilenende verwenden.
Steve K

34

Ich habe einige Timing-Tests durchgeführt, weil die Zeit immer wichtig ist.

Ich habe versucht, die Antwort auf verschiedene Arten in einen String 3 umzuwandeln. (siehe unten)
Ich habe Try / Catch-Blöcke aus Gründen der Lesbarkeit weggelassen.

Um den Kontext anzugeben, ist dies der vorhergehende Code für alle drei Ansätze:

   String response;
   String url = "www.blah.com/path?key=value";
   GetMethod method = new GetMethod(url);
   int status = client.executeMethod(method);

1)

 response = method.getResponseBodyAsString();

2)

InputStream resp = method.getResponseBodyAsStream();
InputStreamReader is=new InputStreamReader(resp);
BufferedReader br=new BufferedReader(is);
String read = null;
StringBuffer sb = new StringBuffer();
while((read = br.readLine()) != null) {
    sb.append(read);
}
response = sb.toString();

3)

InputStream iStream  = method.getResponseBodyAsStream();
StringWriter writer = new StringWriter();
IOUtils.copy(iStream, writer, "UTF-8");
response = writer.toString();

Nach 500 Tests für jeden Ansatz mit denselben Anforderungs- / Antwortdaten sind hier die Zahlen aufgeführt. Dies sind wieder meine Ergebnisse, und Ihre Ergebnisse sind möglicherweise nicht genau dieselben, aber ich habe dies geschrieben, um anderen einen Hinweis auf die Effizienzunterschiede dieser Ansätze zu geben.

Ränge:
Ansatz Nr. 1
Ansatz Nr. 3 - 2,6% langsamer als Nr. 1
Ansatz Nr. 2 - 4,3% langsamer als Nr. 1

Jeder dieser Ansätze ist eine geeignete Lösung, um eine Antwort zu erfassen und daraus einen String zu erstellen.


2
2) enthält einen Fehler, fügt am Ende der Zeichenfolge immer "null" hinzu, da Sie immer einen weiteren Schritt als erforderlich ausführen. Die Leistung wird sowieso die gleiche sein, denke ich. Dies sollte funktionieren: String read = null; StringBuffer sb = new StringBuffer (); while ((read = br.readLine ())! = null) {sb.append (read); }
LukeSolar

Es sollte beachtet werden, dass GetMethod ein Teil von org.apache.commons.httpclient ist, nicht Standard Java
jk7

Ansatz Nr. 2 verbraucht das '\ n', wenn die Datei viele Zeilen enthält. Dies sollte nicht die Antwort sein
Ninja

33

Reine Java-Lösung mit Streams , funktioniert seit Java 8.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

// ...
public static String inputStreamToString(InputStream is) throws IOException {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
        return br.lines().collect(Collectors.joining(System.lineSeparator()));
    }
}

Wie von Christoffer Hammarström unter anderen Antworten erwähnt , ist es sicherer, den Zeichensatz explizit anzugeben . Dh der InputStreamReader-Konstruktor kann wie folgt geändert werden:

new InputStreamReader(is, Charset.forName("UTF-8"))

11
Charset.forName("UTF-8")Verwenden Sie stattdessen StandardCharsets.UTF_8(from java.nio.charset).
Robinst

26

Hier ist mehr oder weniger die Antwort von Sampath, die ein wenig aufgeräumt und als Funktion dargestellt wurde:

String streamToString(InputStream in) throws IOException {
  StringBuilder out = new StringBuilder();
  BufferedReader br = new BufferedReader(new InputStreamReader(in));
  for(String line = br.readLine(); line != null; line = br.readLine()) 
    out.append(line);
  br.close();
  return out.toString();
}


21

Wenn Sie Commons IO (FileUtils / IOUtils / CopyUtils) nicht verwenden können, finden Sie hier ein Beispiel mit einem BufferedReader, um die Datei Zeile für Zeile zu lesen:

public class StringFromFile {
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
        InputStream is = StringFromFile.class.getResourceAsStream("file.txt");
        BufferedReader br = new BufferedReader(new InputStreamReader(is/*, "UTF-8"*/));
        final int CHARS_PER_PAGE = 5000; //counting spaces
        StringBuilder builder = new StringBuilder(CHARS_PER_PAGE);
        try {
            for(String line=br.readLine(); line!=null; line=br.readLine()) {
                builder.append(line);
                builder.append('\n');
            }
        } 
        catch (IOException ignore) { }

        String text = builder.toString();
        System.out.println(text);
    }
}

Oder wenn Sie rohe Geschwindigkeit wünschen, würde ich eine Variation dessen vorschlagen, was Paul de Vrieze vorgeschlagen hat (wodurch die Verwendung eines StringWriter vermieden wird (der intern einen StringBuffer verwendet):

public class StringFromFileFast {
    public static void main(String[] args) /*throws UnsupportedEncodingException*/ {
        InputStream is = StringFromFileFast.class.getResourceAsStream("file.txt");
        InputStreamReader input = new InputStreamReader(is/*, "UTF-8"*/);
        final int CHARS_PER_PAGE = 5000; //counting spaces
        final char[] buffer = new char[CHARS_PER_PAGE];
        StringBuilder output = new StringBuilder(CHARS_PER_PAGE);
        try {
            for(int read = input.read(buffer, 0, buffer.length);
                    read != -1;
                    read = input.read(buffer, 0, buffer.length)) {
                output.append(buffer, 0, read);
            }
        } catch (IOException ignore) { }

        String text = output.toString();
        System.out.println(text);
    }
}

Damit Ihr Code funktioniert, musste ich this.getClass (). GetClassLoader (). GetResourceAsStream () (mit Eclipse mit einem Maven-Projekt) verwenden
greuze

19

Dieser ist schön, weil:

  • Es handhabt sicher den Zeichensatz.
  • Sie steuern die Größe des Lesepuffers.
  • Sie können die Länge des Builders angeben und es muss kein exakter Wert sein.
  • Ist frei von Bibliotheksabhängigkeiten.
  • Ist für Java 7 oder höher.

Wie es geht?

public static String convertStreamToString(InputStream is) throws IOException {
   StringBuilder sb = new StringBuilder(2048); // Define a size if you have an idea of it.
   char[] read = new char[128]; // Your buffer size.
   try (InputStreamReader ir = new InputStreamReader(is, StandardCharsets.UTF_8)) {
     for (int i; -1 != (i = ir.read(read)); sb.append(read, 0, i));
   }
   return sb.toString();
}

Für JDK 9

public static String inputStreamString(InputStream inputStream) throws IOException {
    try (inputStream) {
        return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
    }
}

1
Das catch (Throwable)sollte nicht wirklich leer sein, wenn dies Produktionscode ist.
Christian Hujer

1
Was soll man in diese auffällige Aussage setzen?
Alex

Während die Verwendung von UTF-8 normalerweise sinnvoll ist, sollten Sie nicht davon ausgehen, dass die Zeichen auf diese Weise codiert sind.
Martin

18

Dies ist eine aus dem org.apache.commons.io.IOUtils Quellcode angepasste Antwort für diejenigen, die die Apache-Implementierung haben möchten, aber nicht die gesamte Bibliothek.

private static final int BUFFER_SIZE = 4 * 1024;

public static String inputStreamToString(InputStream inputStream, String charsetName)
        throws IOException {
    StringBuilder builder = new StringBuilder();
    InputStreamReader reader = new InputStreamReader(inputStream, charsetName);
    char[] buffer = new char[BUFFER_SIZE];
    int length;
    while ((length = reader.read(buffer)) != -1) {
        builder.append(buffer, 0, length);
    }
    return builder.toString();
}

18

Stellen Sie sicher, dass Sie die Streams am Ende schließen, wenn Sie Stream Readers verwenden

private String readStream(InputStream iStream) throws IOException {
    //build a Stream Reader, it can read char by char
    InputStreamReader iStreamReader = new InputStreamReader(iStream);
    //build a buffered Reader, so that i can read whole line at once
    BufferedReader bReader = new BufferedReader(iStreamReader);
    String line = null;
    StringBuilder builder = new StringBuilder();
    while((line = bReader.readLine()) != null) {  //Read till end
        builder.append(line);
        builder.append("\n"); // append new line to preserve lines
    }
    bReader.close();         //close all opened stuff
    iStreamReader.close();
    //iStream.close(); //EDIT: Let the creator of the stream close it!
                       // some readers may auto close the inner stream
    return builder.toString();
}

BEARBEITEN: Unter JDK 7+ können Sie das Konstrukt "Try-with-Resources" verwenden.

/**
 * Reads the stream into a string
 * @param iStream the input stream
 * @return the string read from the stream
 * @throws IOException when an IO error occurs
 */
private String readStream(InputStream iStream) throws IOException {

    //Buffered reader allows us to read line by line
    try (BufferedReader bReader =
                 new BufferedReader(new InputStreamReader(iStream))){
        StringBuilder builder = new StringBuilder();
        String line;
        while((line = bReader.readLine()) != null) {  //Read till end
            builder.append(line);
            builder.append("\n"); // append new line to preserve lines
        }
        return builder.toString();
    }
}

2
Sie haben Recht mit dem Schließen von Streams. Die Verantwortung für das Schließen von Streams liegt jedoch normalerweise beim Stream-Konstruktor (beenden Sie, was Sie beginnen). Also, iStreamsollte eigentlich lieber vom Anrufer geschlossen werden, weil der Anrufer erstellt hat iStream. Außerdem sollte das Schließen von Streams in einem finallyBlock oder noch besser in einer Java 7-Try-with-Resources-Anweisung erfolgen. In Ihrem Code bleiben die Streams beim readLine()Werfen IOExceptionoder builder.append()Werfen OutOfMemoryErroroffen.
Christian Hujer

16

Eine weitere für alle Spring-Benutzer:

import java.nio.charset.StandardCharsets;
import org.springframework.util.FileCopyUtils;

public String convertStreamToString(InputStream is) throws IOException { 
    return new String(FileCopyUtils.copyToByteArray(is), StandardCharsets.UTF_8);
}

Die Dienstprogrammmethoden in org.springframework.util.StreamUtilsähneln denen in FileCopyUtils, lassen den Stream jedoch offen, wenn sie fertig sind.


16

Verwenden Sie java.io.InputStream.transferTo (OutputStream), das in Java 9 unterstützt wird, und ByteArrayOutputStream.toString (String), das den Zeichensatznamen verwendet:

public static String gobble(InputStream in, String charsetName) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    in.transferTo(bos);
    return bos.toString(charsetName);
}

Was haben Sie in Ihrem Fall für den Zeichensatznamen übergeben?
Virsha

1
@virsha Sie müssen dies aus der Quelle ermitteln, die den InputStream bereitgestellt hat. Denken Sie
Jmehrens

15

Hier finden Sie die vollständige Methode zum Konvertieren InputStreamin Stringeine Bibliothek eines Drittanbieters. Verwendung StringBuilderfür Single - Threaded - Umgebung anderweitig nutzen StringBuffer.

public static String getString( InputStream is) throws IOException {
    int ch;
    StringBuilder sb = new StringBuilder();
    while((ch = is.read()) != -1)
        sb.append((char)ch);
    return sb.toString();
}

3
Bei dieser Methode wird keine Codierung angewendet. Nehmen wir also an, die vom InputStream empfangenen Daten werden mit UTF-8 codiert. Die Ausgabe ist falsch. Um dies zu beheben, können Sie in = new InputStreamReader(inputStream)und verwenden (char)in.read().
Frederic Leitenberger

2
und auch speichereffizient; Ich glaube, ich habe versucht, dies zuvor für eine große Eingabe zu verwenden, und StringBuilder hat nicht
genügend

1
Es gibt eine andere ähnliche Antwort, die einen char [] -Puffer verwendet, effizienter ist und sich um den Zeichensatz kümmert.
Guillaume Perrot

14

Hier erfahren Sie, wie Sie dies nur mit dem JDK mithilfe von Byte-Array-Puffern tun. So funktionieren eigentlich IOUtils.copy()alle Commons-Io- Methoden. Sie können ersetzen byte[]mit , char[]wenn Sie von einem sind das Kopieren Readerstatt ein InputStream.

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

...

InputStream is = ....
ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);
byte[] buffer = new byte[8192];
int count = 0;
try {
  while ((count = is.read(buffer)) != -1) {
    baos.write(buffer, 0, count);
  }
}
finally {
  try {
    is.close();
  }
  catch (Exception ignore) {
  }
}

String charset = "UTF-8";
String inputStreamAsString = baos.toString(charset);

1
Bitte beschreiben Sie, was Sie erreichen möchten.
Ragunath Jawahar

14

Kotlin-Benutzer tun einfach:

println(InputStreamReader(is).readText())

wohingegen

readText()

ist die integrierte Erweiterungsmethode der Kotlin-Standardbibliothek.


Dies ist eigentlich nicht ganz richtig, weil es den Stream nicht schließt. Ich würde empfehlen is.bufferedReader().use { it.readText() }.
Max

9

Der einfachste Weg in JDK ist mit den folgenden Code-Snipplets.

String convertToString(InputStream in){
    String resource = new Scanner(in).useDelimiter("\\Z").next();
    return resource;
}

7

Hier ist meine Java 8- basierte Lösung, die die neue Stream-API verwendet , um alle Zeilen von einem zu sammeln InputStream:

public static String toString(InputStream inputStream) {
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(inputStream));
    return reader.lines().collect(Collectors.joining(
        System.getProperty("line.separator")));
}

1
Scheint, als hätten Sie nicht alle zuvor veröffentlichten Antworten gelesen. Die Stream-API-Version war bereits mindestens zweimal hier .
Tagir Valeev

Ich habe mir alle Lösungen angesehen, aber keine geeigneten gefunden. Ich finde zwei Zeilen mit einer kurzen Beschreibung präzise dargestellt. Der Try-Catch-Block aus der anderen Lösung wird beispielsweise nie verwendet. Aber du hast recht. Mit so vielen Antworten wechselte ich in den Fast-Skipping-Read-Modus ... :-)
Christian Rädel

1
Sie lesen nicht die Originaldatei, sondern konvertieren die Zeilenenden der Datei in die Zeilenenden des Betriebssystems und ändern möglicherweise den Dateiinhalt.
Christian Hujer

7

In Bezug auf reduceund concatkann es in Java 8 ausgedrückt werden als:

String fromFile = new BufferedReader(new   
InputStreamReader(inputStream)).lines().reduce(String::concat).get();

1
Es wird wahnsinnig langsam sein.
Tagir Valeev

Interessant, warum? Könnten Sie näher darauf eingehen?
Libnull-Dev

1
Weißt du nicht, warum es eine schlechte Idee ist, Strings in einer Schleife zu verketten, anstatt StringBuilder zu verwenden?
Tagir Valeev

Du hast recht. StringBuilderkönnte effizienter sein. Ich werde es überprüfen, aber mein Ziel war es, einen funktionaleren Ansatz mit unveränderlichem Ansatz zu zeigen String.
Libnull-Dev

Funktionale Ansätze sind cool, aber normalerweise sehr ineffizient.
Lluis Martinez

4

JDK 7/8-Antwort, die den Stream schließt und dennoch eine IOException auslöst:

StringBuilder build = new StringBuilder();
byte[] buf = new byte[1024];
int length;
try (InputStream is = getInputStream()) {
  while ((length = is.read(buf)) != -1) {
    build.append(new String(buf, 0, length));
  }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.