Jak odczytać / przekonwertować InputStream na String w Javie?

4063

Jeśli masz java.io.InputStreamobiekt, jak go przetworzyć i wygenerować String?


Załóżmy, że mam plik InputStreamtekstowy i chcę go przekonwertować na plik String, aby na przykład zapisać go w pliku dziennika.

Jaki jest najłatwiejszy sposób, aby wziąć InputStreami przekonwertować go na String?

public String convertStreamToString(InputStream is) {
    // ???
}
Johnny Maelstrom
źródło
36
Odpowiedzi na to pytanie działają tylko wtedy, gdy chcesz w pełni przeczytać zawartość strumienia (dopóki nie zostanie zamknięty). Ponieważ nie zawsze jest to zamierzone (żądania HTTP z utrzymywanym połączeniem nie zostaną zamknięte), te metody wywołują blok (nie dając ci treści).
f1sh
21
Ci muszą poznać i określić kodowanie znaków w strumieniu, albo będzie mieć błędy kodowania znaków, ponieważ będzie używany losowo wybrany kodowania w zależności od maszyna / systemu operacyjnego / wersja platformy lub jego kod jest uruchamiany. Oznacza to, że nie należy używać metod zależnych od domyślnego kodowania platformy.
Christoffer Hammarström,
11
Żeby się dobrze bawić z moim własnym komentarzem sprzed 9 lat, obecnie używam Groovy'ego „String s = new File („ SomeFile.txt ”). Text”, aby odczytać cały plik naraz i działa świetnie. Cieszę się, że używam groovy do mojego kodu nieprodukcyjnego (skryptowego) i - szczerze mówiąc, zmuszając cię do radzenia sobie z kodowaniem i wyjątkowo długimi plikami, tak jak robi to Java, to naprawdę dobry pomysł na kod produkcyjny, więc działa zgodnie ze swoim przeznaczeniem, Groovy działa na szybkie skrypty, w których Java nie jest świetna - po prostu użyj odpowiedniego narzędzia do pracy i wszystko się uda.
Bill K
Upraszczając: 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 z Javą 11, możesz używać języka, String s = Files.readString​(Path.of("SomeFile.txt"));który jest tak dobry, jak tylko język, który nigdy nie będzie obsługiwał konwersji typu magicznego, takich jak opisany.
Holger

Odpowiedzi:

2530

Dobrym sposobem na to jest użycie wspólnego Apache IOUtils do skopiowania InputStreamdo StringWriter... czegoś podobnego

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

lub nawet

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

Alternatywnie możesz użyć, ByteArrayOutputStreamjeśli nie chcesz mieszać swoich strumieni i pisarzy

Harry Lime
źródło
75
Dla programistów Androida wydaje się, że Android nie jest dostarczany z IOUtils z Apache. Możesz więc rozważyć skorzystanie z innych odpowiedzi.
Chris.Zou
47
To jest niezwykle stare pytanie w tym momencie (zadano je w 2008 r.). Warto poświęcić czas na przeczytanie bardziej nowoczesnych odpowiedzi. Niektóre używają rodzimych wywołań z biblioteki Java 8.
Shadoninja
36
Ta odpowiedź jest mocno przestarzała i należy ją oznaczyć jako taką (niestety nie jest to możliwe).
codepleb
7
IOUtils.toString () od dawna jest przestarzałe. Ta odpowiedź zdecydowanie nie jest już zalecana.
Roshan
7
następnie edytuj go, aby wyjaśnić, dlaczego jest przestarzały, aby pomóc przyszłym czytelnikom.
Jean-François Fabre
2486

Podsumuj inne odpowiedzi Znalazłem 11 głównych sposobów, aby to zrobić (patrz poniżej). I napisałem kilka testów wydajności (patrz wyniki poniżej):

Sposoby konwersji InputStream na ciąg znaków:

  1. Korzystanie IOUtils.toString(Apache Utils)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  2. Korzystanie CharStreams(Guava)

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

    Scanner s = new Scanner(inputStream).useDelimiter("\\A");
    String result = s.hasNext() ? s.next() : "";
  4. Korzystanie ze Stream API (Java 8). Ostrzeżenie : To rozwiązanie przekształca różne podziały linii (jak \r\n) na \n.

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
  5. Korzystanie z interfejsu API Stream równoległego (Java 8). Ostrzeżenie : To rozwiązanie przekształca różne podziały linii (jak \r\n) na \n.

    String result = new BufferedReader(new InputStreamReader(inputStream)).lines()
       .parallel().collect(Collectors.joining("\n"));
  6. Korzystanie InputStreamReaderi 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. Korzystanie StringWriteri IOUtils.copy(Apache Commons)

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, "UTF-8");
    return writer.toString();
  8. Korzystanie ByteArrayOutputStreami 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. Korzystanie BufferedReader(JDK). Ostrzeżenie: to rozwiązanie przekształca różne podziały wiersza (jak \n\r) we line.separatorwłaściwość systemową (na przykład w systemie Windows na „\ 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. Korzystanie BufferedInputStreami 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. Używanie inputStream.read()i StringBuilder(JDK). Ostrzeżenie : to rozwiązanie ma problemy z Unicode, na przykład z tekstem rosyjskim (działa poprawnie tylko z tekstem innym niż Unicode)

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

Ostrzeżenie :

  1. Rozwiązania 4, 5 i 9 przekształcają różne podziały linii na jeden.

  2. Rozwiązanie 11 nie działa poprawnie z tekstem Unicode

Testy wydajności

Testy wydajności dla małych String(długość = 175), URL w github (tryb = średni czas, system = Linux, wynik 1,343 jest najlepszy):

              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

Testy wydajności dla big String(długość = 50100), url w github (tryb = średni czas, system = Linux, wynik 200,715 jest najlepszy):

               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

Wykresy (testy wydajności w zależności od długości strumienia wejściowego w systemie Windows 7)
wprowadź opis zdjęcia tutaj

Test wydajności (średni czas) w zależności od długości strumienia wejściowego w systemie Windows 7:

 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
Wiaczesław Wedenin
źródło
17
Pisząc „odpowiedź podsumowującą”, należy pamiętać, że niektóre rozwiązania automatycznie konwertują różne łamania linii (np. \r\n), Na \nktóre w niektórych przypadkach mogą być niepożądane. Byłoby również miło zobaczyć wymaganą dodatkową pamięć lub przynajmniej presję alokacji (przynajmniej możesz uruchomić JMH -prof gc). W przypadku naprawdę fajnego postu byłoby dobrze zobaczyć wykresy (w zależności od długości łańcucha w tym samym rozmiarze wejściowym i zależnie od rozmiaru wejściowego w tej samej długości łańcucha).
Tagir Valeev
16
Pozytywne; najśmieszniejsze jest to, że wyniki są więcej niż oczekiwano: należy użyć standardowego cukru syntaktycznego JDK i / lub Apache Commons.
Aleksei Matiushkin 15.04.16
25
Niesamowity post. Jeszcze jedna rzecz. Java 8 ostrzega przed używaniem równoległych strumieni w zasobach, które zmuszą cię do zablokowania i oczekiwania (takich jak ten strumień wejściowy), więc opcja równoległego strumienia jest raczej uciążliwa i nie warto?
mangusbrother
10
Czy strumień równoległy faktycznie utrzymuje porządek linii?
Natix
6
Co jest reset()na przykład w 11?
Rob Stewart
2307

Oto sposób korzystania tylko ze standardowej biblioteki Java (pamiętaj, że strumień nie jest zamknięty, twój przebieg może się różnić).

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

Nauczyłem się tej sztuczki z artykułu „Triki z głupiego skanera” . Powodem tego jest to, że skaner dokonuje iteracji po tokenach w strumieniu. W tym przypadku tokenów oddzielamy za pomocą „początku granicy wejściowej” (\ A), co daje nam tylko jeden token dla całej zawartości strumienia.

Uwaga: jeśli musisz sprecyzować kodowanie strumienia wejściowego, możesz podać drugi Scannerkonstruktor, który wskazuje, jakiego zestawu znaków użyć (np. „UTF-8”).

Wskazówka kapelusza trafia także do Jakuba , który kiedyś wskazał mi ten artykuł.

Pavel Repin
źródło
8
Dzięki, dla mojej wersji tego dodałem wreszcie blok, który zamyka strumień wejściowy, więc użytkownik nie musi, odkąd skończyłeś czytać dane wejściowe. Znacznie upraszcza kod dzwoniącego.
4
@PavelRepin @Patrick W moim przypadku pusty inputStream spowodował NPE podczas budowy skanera. Musiałem dodać if (is == null) return "";na samym początku metody; Uważam, że ta odpowiedź musi zostać zaktualizowana, aby lepiej obsługiwać strumienie wejściowe o wartości zerowej.
CFL_Jeff,
115
W przypadku Java 7 możesz spróbować w try-with: try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
kamera douszna
5
Niestety wydaje się, że to rozwiązanie nie działa i nie ma wyjątków zgłoszonych w mojej podstawowej implementacji strumienia.
Taig,
11
Do Twojej wiadomości, bloki hasNext w strumieniach wejściowych konsoli (patrz tutaj ). (Właśnie natknąłem się teraz na ten problem.) To rozwiązanie działa dobrze, w przeciwnym razie ... tylko jeden na jednego.
Ryan
848

Apache Commons pozwala:

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

Oczywiście możesz wybrać inne kodowanie znaków oprócz UTF-8.

Zobacz także: ( dokumentacja )

Chinnery
źródło
1
Istnieje również metoda, która pobiera argument inputStream tylko w przypadku domyślnego kodowania.
Guillaume Coté
13
@Guillaume Coté Myślę, że komunikat jest taki, że nigdy nie powinno być „w porządku z domyślnym kodowaniem”, ponieważ nie możesz być pewien, co to jest, w zależności od platformy, na której działa kod Java.
Per Wiklander
7
@Per Wiklander Nie zgadzam się z tobą. Kod, który będzie działał na jednym, może być całkiem pewien, że domyślne kodowanie będzie w porządku. W przypadku kodu, który otwiera tylko plik lokalny, rozsądną opcją jest poproszenie go o zakodowanie w domyślnym kodowaniu platformy.
Guillaume Coté
39
Aby ocalić każdego, kto ma kłopot z Googlingiem - <dependency> <groupId> org.apache.commons </groupId> <artifactId> commons-io </artifactId> <version> 1.3.2 </version> </dependency>
Chris
7
Niewielkim ulepszeniem byłoby także użycie stałej apache io (lub innej) do kodowania znaków zamiast literału zwykłego - np .: IOUtils.toString (myInputStream, Charsets.UTF_8);
300

Biorąc pod uwagę plik należy najpierw uzyskać java.io.Readerinstancję. Można to następnie odczytać i dodać do StringBuilder(nie potrzebujemy, StringBufferjeśli nie uzyskujemy do niego dostępu w wielu wątkach i StringBuilderjest to szybsze). Sztuczka polega na tym, że pracujemy w blokach i jako takie nie potrzebujemy innych strumieni buforujących. Rozmiar bloku jest parametryzowany w celu optymalizacji wydajności w czasie wykonywania.

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();
}
Paul de Vrieze
źródło
8
To rozwiązanie wykorzystuje znaki wielobajtowe. W przykładzie użyto kodowania UTF-8, które umożliwia wyrażenie pełnego zakresu Unicode (w tym chińskiego). Zastąpienie „UTF-8” innym kodowaniem pozwoliłoby na zastosowanie tego kodowania.
Paul de Vrieze,
27
@ Użytkownik1 - Lubię używać bibliotek w kodzie, aby szybciej wykonywać swoją pracę. To niesamowite, gdy Twoi menedżerowie mówią: „Wow James! Jak to zrobiliście tak szybko ?!”. Ale kiedy musimy spędzić czas na nowo opracowując koło tylko dlatego, że zgubiliśmy pomysły na włączenie wspólnej, wielokrotnego użytku, wypróbowanej i przetestowanej użyteczności, tracimy czas, abyśmy mogli poświęcić na realizację celów naszego projektu. Kiedy odkrywamy koło, pracujemy dwa razy ciężej, ale znacznie później docieramy do mety. Gdy jesteśmy na mecie, nie ma nikogo, kto mógłby nam pogratulować. Budując dom, nie buduj też młota
jmort253
10
Przepraszam, po ponownym przeczytaniu mojego komentarza jest trochę arogancki. Po prostu uważam, że ważne jest, aby mieć dobry powód, aby unikać bibliotek, i że powód jest prawidłowy, co bardzo dobrze może być :)
jmort253
4
@ jmort253 Zauważyliśmy regresję wydajności po kilkukrotnej aktualizacji biblioteki w naszym produkcie. Na szczęście budujemy i sprzedajemy własny produkt, więc tak naprawdę nie mamy tak zwanych terminów. Niestety budujemy produkt, który jest dostępny na wielu maszynach JVM, bazach danych i serwerach aplikacji na wielu systemach operacyjnych, więc musimy pomyśleć o użytkownikach korzystających ze złych maszyn ... A optymalizacja operacji na łańcuchach może poprawić perf o 30 ~ 40%. I poprawka: In our product, I even replacedpowinno być „nawet zastąpiliśmy”.
coolcfan
10
@ jmort253 Jeśli już używałbyś wspólnego apache, powiedziałbym, idź. Jednocześnie korzystanie z bibliotek wiąże się z realnym kosztem (jak pokazuje proliferacja zależności w wielu bibliotekach Apache Java). Jeśli byłoby to jedyne użycie biblioteki, korzystanie z biblioteki byłoby nadmierną umiejętnością. Z drugiej strony, określając własne rozmiary buforów, możesz dostroić bilans wykorzystania pamięci / procesora.
Paul de Vrieze
248

Posługiwać się:

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();
sampathpremarathna
źródło
11
Chodzi o to, że najpierw dzielisz się na linie, a następnie cofasz. Łatwiej i szybciej jest po prostu czytać dowolne bufory.
Paul de Vrieze,
20
Ponadto readLine nie rozróżnia \ n i \ r, więc nie można ponownie odtworzyć dokładnego strumienia.
María Arias de Reyna Domínguez,
2
bardzo nieefektywne, ponieważ readLineodczytuje znak po znaku, aby wyszukać EOL. Ponadto, jeśli w strumieniu nie ma podziału linii, nie ma to naprawdę sensu.
njzk2
3
@Gops AB: Jeśli to wypróbujesz, a twoja próbka zawiera nowe znaki, zobaczysz, że sposób, w jaki ta pętla jest konstruowana przy użyciu readline () i StringBuilder.append (), tak naprawdę nie zachowuje nowych linii.
Russ Bateman
4
To nie jest najlepsza odpowiedź, ponieważ nie jest ściśle bajt po bajcie. Czytelnik gryzie nowe wiersze, więc musisz uważać, aby je utrzymać.
Jeffrey Blattman
173

Jeśli korzystasz z Google-Collections / Guava, możesz wykonać następujące czynności:

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

Zauważ, że drugi parametr (tj. Charsets.UTF_8) dla parametru InputStreamReadernie jest konieczny, ale ogólnie dobrze jest podać kodowanie, jeśli go znasz (co powinieneś!)

Sakuraba
źródło
2
@harschware: Biorąc pod uwagę pytanie: „Jeśli masz obiekt java.io.InputStream, w jaki sposób należy przetworzyć ten obiekt i wygenerować ciąg?” Zakładałem, że strumień jest już obecny w tej sytuacji.
Sakuraba
Nie wyjaśniłeś swojej odpowiedzi zbyt dobrze i miałeś zmienne obce; user359996 powiedział to samo co ty, ale o wiele wyraźniej.
Uronym
2
+1 dla guawy, -1 za brak określenia kodowania strumienia wejściowego. na przykład. nowy InputStreamReader (strumień, „UTF-8”)
andras,
@Chris Noldus Z drugiej strony, niektórzy ludzie już mają guawa w swoim projekcie, jak ja, i sądzą, że to rozwiązanie jest bardziej eleganckie niż wersja tylko dla sdk.
CorayThan
@Vadzim, ta odpowiedź jest taka sama jak ta - oba używają CharStreams.toString
Tom
125

To najlepsze czyste oprogramowanie Java, które idealnie pasuje do Androida i każdej innej maszyny JVM.

To rozwiązanie działa niesamowicie dobrze ... jest proste, szybkie i działa tak samo na małych i dużych strumieniach !! (patrz punkt odniesienia powyżej .. 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;
}
TacB0sS
źródło
4
Działa dobrze na Androidzie w porównaniu z innymi odpowiedziami, które działają tylko w Javie korporacyjnej.
vortexwolf
Awaria w Androidzie z błędem OutOfMemory w wierszu „.write” za każdym razem w przypadku krótkich ciągów znaków.
Adam
Dodałem kodowanie. tak na marginesie, oryginalna metoda readFully, którą mam w kodzie, nie zwraca String, zwraca byte [] dla bardziej ogólnej funkcjonalności. Za wdrożenie nowego ciągu (...) z kodowaniem odpowiada ten, który korzysta z interfejsu API!
TacB0sS,
2
Szybka uwaga: ślad tej pamięci jest maksymalny o 2*n, gdzie n jest rozmiarem strumienia, zgodnie z systemem ByteArrayInputStreamautomatycznego wzrostu.
njzk2
3
Niepotrzebnie podwaja użycie pamięci, co jest cenne na urządzeniach mobilnych. Lepiej użyj InputStreamReader i dołącz do StringReader, konwersja bajtów na char będzie wykonywana w locie, a nie na końcu luzem.
Oliv
84

Dla kompletności oto rozwiązanie Java 9 :

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

readAllBytesJest obecnie w JDK 9 głównym kodzie, więc jest prawdopodobne, aby pojawić się w komunikacie. Możesz wypróbować teraz, korzystając z kompilacji migawek JDK 9 .

Tagir Valeev
źródło
Czy metoda nie przydziela całej pamięci do odczytu? byte[] buf = new byte[DEFAULT_BUFFER_SIZE];gdzie MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;co daje MAX_BUFFER_SIZE = 2147483639. Google twierdzi, że ma około 2.147 GB.
Rekin
Przepraszam, popełniłem błąd w obliczeniach. To jest 2 GB. Zredagowałem komentarz. Więc nawet jeśli czytam jak plik 4kb, używam 2 GB pamięci?
Rekin
2
@ChristianHujer, nie widzę tego w ostatnim zatwierdzeniu jdk8u . Nowe metody AFAIK nigdy nie są wprowadzane w aktualizacjach Java, tylko w głównych wydaniach.
Tagir Valeev
4
@ChristianHujer, pytanie dotyczyło InputStream, a nie o Path. InputStreamMogą być tworzone z wielu różnych źródeł, a nie tylko pliki.
Tagir Valeev
5
Zostało to napisane rok temu, więc aby zaktualizować, potwierdzam, że ta metoda jest rzeczywiście w publicznej wersji JDK 9. Ponadto, jeśli twoje kodowanie to „ISO-Latin-1”, będzie to niezwykle wydajne, ponieważ teraz Java 9 Strings używa teraz byte[]wdrożenie jeśli wszystkie znaki są w pierwszych 256 punktów kodowych. Oznacza to, że nowy ciąg (bajt [], „ISO-Latin-1”) będzie prostą kopią tablicy.
Klitos Kyriacou
66

Posługiwać się:

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();
}
Jon Moore
źródło
@ DanielDeLeón Nie, nie ma. Jest to BufferedInputStream. Podstawowe odczyty to 8192 bajtów naraz.
user207421,
2
@EJP Stwierdziłem, że jest wolniejszy niż używanie BufferedInputStream i czytanie w buforze tablicy bajtów zamiast jednego bajtu na raz. Przykład: 200 ms w porównaniu do 60 ms podczas czytania pliku 4.56 MiB.
jk7
Dziwne, że nikt nie wskazał tutaj drugiego ważnego problemu (tak, czytanie zawartości bajt po bajcie jest marnotrawstwem nawet przy buforowaniu): polega na tym, co dzieje się jako „domyślne kodowanie” - rzadko jest to dobry sposób. Zamiast tego należy przekazać kodowanie jako argument do buf.toString().
StaxMan
@ jk7 Czas na odczytanie pliku 4,56 MB jest tak mały, że różnice nie mogły być znaczące.
user207421,
63

Oto najbardziej eleganckie rozwiązanie w czystej Javie (bez biblioteki), które wymyśliłem po kilku eksperymentach:

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();
}
Drew Noakes
źródło
8
@TorbenKohlmeier, czytniki i bufory nie muszą być zamknięte. Podany InputStreampowinien zostać zamknięty przez dzwoniącego.
Drew Noakes
7
Nie zapomnij wspomnieć, że w InputStreamReader jest bardziej preferowany konstruktor, który pobiera CharSet.
jontejj
7
dlaczego ludzie nadal korzystają readLine? jeśli nie używasz linii per se, co to za
korzyść
4
Nie czytaj liniami. Co jeśli jedna linia jest tak długa, że ​​nie zmieści się w kupie?
voho
4
@ voho, jeśli jedna linia jest tak długa, to i tak nie ma możliwości przypisania wartości zwracanej, która musi być równa lub większa dla tej linii. Jeśli masz do czynienia z tak dużymi plikami, powinieneś je przesyłać strumieniowo. Istnieje jednak wiele przypadków użycia do ładowania małych plików tekstowych do pamięci.
Drew Noakes,
55

Zrobiłem punkt odniesienia na podstawie 14 różnych odpowiedzi tutaj (przepraszam, że nie dostałem kredytów, ale jest zbyt wiele duplikatów).

Wynik jest bardzo zaskakujący. Okazuje się, że Apache IOUtils jest najwolniejszym i ByteArrayOutputStreamnajszybszym rozwiązaniem:

Więc najpierw jest najlepsza metoda:

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

Wyniki testu, z 20 MB losowych bajtów w 20 cyklach

Czas w milisekundach

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

Kod źródłowy testu porównawczego

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

}
Ilya Gazman
źródło
Wykonywanie testów porównawczych w Javie nie jest łatwe (szczególnie z powodu JIT). Po przeczytaniu kodu źródłowego testu porównawczego jestem przekonany, że powyższe wartości nie są precyzyjne i wszyscy powinni być ostrożni, wierząc im.
Dalibor
@Dalibor prawdopodobnie powinieneś podać więcej uzasadnienia swojego roszczenia niż tylko link.
Ilya Gazman
Myślę, że naprawdę znany jest fakt, że nie jest łatwo stworzyć własny punkt odniesienia. Dla tych, którzy tego nie wiedzą, jest link;)
Dalibor
@Dalibor Być może nie jestem najlepszy, ale dobrze rozumiem testy porównawcze Java, więc jeśli nie możesz wskazać konkretnego problemu, to tylko wprowadzasz w błąd i nie będę kontynuować rozmowy z tobą w tych warunkach.
Ilya Gazman
Przeważnie zgadzam się z Daliborem. Mówisz, że masz „dobre rozumienie testów porównawczych Java”, ale wydaje się, że wdrożyłeś najbardziej naiwne podejście, jednocześnie najwyraźniej nieświadomy dobrze znanych problemów tego podejścia. Na początek przeczytaj każdy post na to pytanie: stackoverflow.com/questions/504103/…
DavidS
41

Użyłbym kilku sztuczek Java 8.

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.
    }
}

Zasadniczo takie same jak niektóre inne odpowiedzi, z wyjątkiem bardziej zwięzłych.

Simon Kuang
źródło
5
Czy return nullkiedykolwiek to się nazywa? Zgłaszane są br.lines...zwroty lub wyjątek.
Holloway,
3
@Khaled A Khunaifer: tak, całkiem pewny ... może powinieneś zajrzeć tutaj: docs.oracle.com/javase/tutorial/essential/exceptions/… . To, co źle edytowałeś, to stwierdzenie „spróbuj z zasobami”.
jamp
11
Dlaczego dzwonisz parallel()w strumieniu?
robinst
4
Nie spowodowałoby to uczciwej kopii danych, gdyby strumień źródłowy używał zakończeń linii systemu Windows, ponieważ wszystkie \r\nzostałyby przekonwertowane na \n...
Lucas,
2
Można użyć, System.lineSeparator()aby użyć odpowiedniego zakończenia linii zależnego od platformy.
Steve K
34

Przeprowadziłem testy czasowe, ponieważ czas zawsze ma znaczenie.

Próbowałem uzyskać odpowiedź na ciąg 3 różnymi sposobami. (pokazano poniżej)
Pominąłem bloki try / catch ze względu na czytelność.

Aby podać kontekst, jest to poprzedni kod dla wszystkich 3 podejść:

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

Po przeprowadzeniu 500 testów dla każdego podejścia z tymi samymi danymi żądania / odpowiedzi, oto liczby. Ponownie, są to moje ustalenia i twoje odkrycia mogą nie być dokładnie takie same, ale napisałem to, aby dać innym wskazówkę na temat różnic w wydajności tych podejść.

Rangi:
Podejście nr 1
Podejście nr 3 - 2,6% wolniej niż nr 1
Podejście nr 2 - 4,3% wolniej niż nr 1

Każde z tych podejść jest odpowiednim rozwiązaniem do pobrania odpowiedzi i utworzenia z niej ciągu.

Brett Holt
źródło
2
2) zawiera błąd, dodaje zawsze „null” na końcu łańcucha, ponieważ zawsze wykonujesz jeszcze jeden krok, niż to konieczne. Myślę, że i tak wydajność będzie taka sama. To powinno działać: String read = null; StringBuffer sb = new StringBuffer (); while ((read = br.readLine ())! = null) {sb.append (read); }
LukeSolar
Należy zauważyć, że GetMethod jest częścią org.apache.commons.httpclient, a nie standardowej Java
jk7
Podejście nr 2 zużyje „\ n”, jeśli plik ma wiele wierszy, to nie powinna być odpowiedź
Ninja
33

Rozwiązanie Pure Java wykorzystujące strumień Stream działa od wersji 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()));
    }
}

Jak wspomniał Christoffer Hammarström poniżej innej odpowiedzi , bezpieczniej jest wyraźnie określić zestaw znaków . Tzn. Konstruktor InputStreamReader można zmieniać w następujący sposób:

new InputStreamReader(is, Charset.forName("UTF-8"))
czerny
źródło
11
Zamiast tego Charset.forName("UTF-8")użyj StandardCharsets.UTF_8(od java.nio.charset).
robinst
26

Oto mniej więcej odpowiedź sampath, nieco oczyszczona i przedstawiona jako funkcja:

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();
}
TKH
źródło
24

Jeśli masz ochotę na przygodę, możesz połączyć Scalę i Javę i skończyć z tym:

scala.io.Source.fromInputStream(is).mkString("")

Mieszanie kodu Java i Scali z bibliotekami ma swoje zalety.

Zobacz pełny opis tutaj: Idiomatyczny sposób konwersji InputStream na ciąg znaków w Scali

Jacek
źródło
3
W dzisiejszych czasach po prostu działa to dobrze:Source.fromInputStream(...).mkString
KajMagnus
21

Jeśli nie możesz użyć Commons IO (FileUtils / IOUtils / CopyUtils), oto przykład użycia BufferedReadera do odczytu pliku linia po linii:

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

Lub jeśli chcesz surowej prędkości, zaproponowałbym odmianę tego, co zasugerował Paul de Vrieze (która unika korzystania z StringWriter (który korzysta z StringBuffer wewnętrznie):

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);
    }
}
DJDaveMark
źródło
Aby twój kod działał, musiałem użyć this.getClass (). GetClassLoader (). GetResourceAsStream () (używając Eclipse z projektem maven)
greuze
19

Ten jest miły, ponieważ:

  • Bezpiecznie obsługuje zestaw znaków.
  • Kontrolujesz rozmiar bufora odczytu.
  • Możesz podać długość konstruktora i nie musi to być dokładna wartość.
  • Jest wolny od zależności bibliotek.
  • Jest dla Java 7 lub wyższej.

Jak to zrobić?

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

Dla JDK 9

public static String inputStreamString(InputStream inputStream) throws IOException {
    try (inputStream) {
        return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
    }
}
Daniel De León
źródło
1
catch (Throwable)Naprawdę nie powinno być puste, jeśli jest to kod produkcji.
Christian Hujer
1
co włożyć w ten haczyk?
alex
Podczas gdy używanie UTF-8 jest zwykle rozsądne, nie należy zakładać, że znaki są zakodowane w ten sposób.
Martin
18

Jest to odpowiedź zaadaptowana z org.apache.commons.io.IOUtils kodu źródłowego dla tych, którzy chcą mieć implementację apache, ale nie chcą całej biblioteki.

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();
}
Hai Zhang
źródło
18

Pamiętaj, aby zamknąć strumienie na końcu, jeśli używasz czytników strumieniowych

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

EDYCJA: W JDK 7+ można użyć konstrukcji try-with-resources.

/**
 * 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();
    }
}
Thamme Gowda
źródło
2
Masz rację co do zamykania strumieni, jednak odpowiedzialność za zamykanie strumieni zwykle spoczywa na konstruktorze strumieni (zakończ to, co zaczynasz). Tak więc iStreampowinien raczej zostać zamknięty przez dzwoniącego, ponieważ dzwoniący został utworzony iStream. Poza tym zamykanie strumieni powinno odbywać się w finallybloku, a jeszcze lepiej w instrukcji try-with-resources Java 7. W twoim kodzie, gdy readLine()rzuca IOExceptionlub builder.append()rzuca OutOfMemoryError, strumienie pozostaną otwarte.
Christian Hujer
16

Kolejny, dla wszystkich użytkowników Spring:

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

Metody narzędzi w org.springframework.util.StreamUtilssą podobne do tych w FileCopyUtils, ale po zakończeniu pozostawiają strumień otwarty.

James
źródło
16

Użyj java.io.InputStream.transferTo (OutputStream) obsługiwanego w Javie 9 i ByteArrayOutputStream.toString (String), który przyjmuje nazwę zestawu znaków:

public static String gobble(InputStream in, String charsetName) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    in.transferTo(bos);
    return bos.toString(charsetName);
}
jmehrens
źródło
Co w swoim przypadku podałeś dla nazwy zestawu znaków?
virsha
1
@virsha Musisz to ustalić ze źródła, które dostarczyło InputStream. Pamiętaj, że nie ma sensu mieć łańcucha bez znajomości używanego kodowania.
jmehrens,
15

Oto pełna metoda konwersji InputStreamna Stringbez korzystania z biblioteki innej firmy. Użyj StringBuilderdo środowiska jednowątkowego, w przeciwnym razie użyj 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();
}
laksys
źródło
3
W tej metodzie nie stosuje się kodowania. Powiedzmy, że dane otrzymane z InputStream są kodowane za pomocą UTF-8, wyjście będzie niepoprawne. Aby to naprawić, możesz użyć in = new InputStreamReader(inputStream)i (char)in.read().
Frederic Leitenberger
2
i nieefektywna pamięć;
Wydaje
1
Istnieje inna podobna odpowiedź, która wykorzystuje bufor char [] i jest bardziej wydajna i zajmuje się charset.
Guillaume Perrot
14

Oto jak to zrobić, używając tylko JDK przy użyciu buforów tablicy bajtów. Tak właśnie IOUtils.copy()działają wszystkie metody commons-io . Można wymienić byte[]ze char[]jeśli kopiowania z Readerzamiast 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);
Matt Shannon
źródło
1
Podaj opis tego, co próbujesz osiągnąć.
Ragunath Jawahar
14

Użytkownicy Kotlin po prostu:

println(InputStreamReader(is).readText())

natomiast

readText()

jest wbudowaną metodą rozszerzenia biblioteki standardowej Kotlin.

Alex
źródło
To nie jest właściwie poprawne, ponieważ nie zamyka strumienia. Polecam is.bufferedReader().use { it.readText() }.
Max
9

Najprostszym sposobem w JDK jest użycie następujących fragmentów kodu.

String convertToString(InputStream in){
    String resource = new Scanner(in).useDelimiter("\\Z").next();
    return resource;
}
Raghu K Nair
źródło
7

Oto moje rozwiązanie oparte na Javie 8 , które korzysta z nowego API Stream do zbierania wszystkich linii z InputStream:

public static String toString(InputStream inputStream) {
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(inputStream));
    return reader.lines().collect(Collectors.joining(
        System.getProperty("line.separator")));
}
Christian Rädel
źródło
1
Wygląda na to, że tak naprawdę nie przeczytałeś wcześniej wszystkich odpowiedzi. Wersja Stream API była już tutaj co najmniej dwa razy .
Tagir Valeev
Przejrzałem wszystkie rozwiązania, ale nie znalazłem odpowiedniego. Uważam, że dwie linie z krótkim opisem są precyzyjnie przedstawione. Blok try-catch z drugiego rozwiązania nie jest na przykład nigdy używany. Ale masz rację. Przy tak wielu odpowiedziach przełączyłem się na tryb szybkiego przeskakiwania-odczytu ... :-)
Christian Rädel
1
Nie czytasz oryginalnego pliku, konwertujesz wszystkie zakończenia linii w pliku na dowolne zakończenia linii w systemie operacyjnym, prawdopodobnie zmieniając zawartość pliku.
Christian Hujer
7

Pod względem reducei concatmożna to wyrazić w Javie 8 jako:

String fromFile = new BufferedReader(new   
InputStreamReader(inputStream)).lines().reduce(String::concat).get();
libnull-dev
źródło
1
To będzie szalenie powolne.
Tagir Valeev
Ciekawe dlaczego? Czy mógłbyś opracować?
libnull-dev
1
nie wiesz, dlaczego łączenie łańcuchów w pętli zamiast korzystania z StringBuilder to zły pomysł?
Tagir Valeev
Masz rację. StringBuildermoże być bardziej wydajny. Sprawdzę, ale moim celem było pokazanie bardziej funkcjonalnego podejścia z niezmiennymString .
libnull-dev
Podejścia funkcjonalne są fajne, ale zwykle bardzo nieefektywne.
Lluis Martinez
4

Odpowiedź JDK 7/8, która zamyka strumień i nadal zgłasza wyjątek IOException:

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));
  }
}
Brian Pontarelli
źródło