Bagaimana cara membaca / mengonversi InputStream menjadi sebuah String di Java?

4063

Jika Anda memiliki java.io.InputStreamobjek, bagaimana Anda memproses objek itu dan menghasilkan String?


Misalkan saya punya InputStreamyang berisi data teks, dan saya ingin mengonversinya menjadi String, jadi misalnya saya bisa menulis itu ke file log.

Apa cara termudah untuk mengambil InputStreamdan mengubahnya menjadi String?

public String convertStreamToString(InputStream is) {
    // ???
}
Johnny Maelstrom
sumber
36
Jawaban atas pertanyaan ini hanya berfungsi jika Anda ingin membaca konten aliran sepenuhnya (sampai ditutup). Karena itu tidak selalu dimaksudkan (permintaan http dengan koneksi tetap-hidup tidak akan ditutup), metode ini memanggil blokir (tidak memberi Anda konten).
f1sh
21
Anda perlu mengetahui dan menentukan pengkodean karakter untuk streaming, atau Anda akan memiliki bug pengodean karakter, karena Anda akan menggunakan pengkodean yang dipilih secara acak tergantung pada mesin / sistem operasi / platform atau versi dari mana kode Anda dijalankan. Artinya, jangan tidak menggunakan metode yang bergantung pada encoding platform standar.
Christoffer Hammarström
11
Hanya untuk bersenang-senang dengan komentar saya sendiri dari 9 tahun yang lalu, hari ini saya menggunakan Groovy's "String s = new File (" SomeFile.txt "). Text" untuk membaca seluruh file sekaligus dan bekerja dengan baik. Saya senang menggunakan groovy untuk kode non-produksi (scripting) saya dan - sejujurnya memaksa Anda untuk berurusan dengan penyandian dan file yang sangat panjang cara java lakukan adalah ide yang sangat bagus untuk kode produksi tetap jadi itu bekerja untuk tujuan itu, Groovy bekerja untuk skrip cepat yang java tidak hebat - Cukup gunakan alat yang tepat untuk pekerjaan itu dan semuanya berhasil.
Bill K
Hanya menyederhanakan: 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 dengan Java 11, Anda dapat menggunakan String s = Files.readString​(Path.of("SomeFile.txt"));yang sebagus bahasa yang bisa didapat, yang tidak akan pernah mendukung konversi jenis ajaib seperti yang Anda jelaskan.
Holger

Jawaban:

2530

Cara yang bagus untuk melakukan ini adalah menggunakan Apache commons IOUtils untuk menyalin InputStreamke StringWriter... sesuatu seperti

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

atau bahkan

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

Atau, Anda dapat menggunakan ByteArrayOutputStreamjika Anda tidak ingin mencampur Streaming dan Penulis Anda

Harry Lime
sumber
75
Untuk pengembang android, sepertinya android tidak datang dengan IOUtils dari Apache. Jadi, Anda dapat mempertimbangkan merujuk ke jawaban lain.
Chris.Zou
47
Ini adalah pertanyaan yang sangat lama pada saat ini (ditanyakan pada tahun 2008). Sebaiknya Anda membaca jawaban yang lebih modern. Beberapa menggunakan panggilan asli dari perpustakaan Java 8.
Shadoninja
36
Jawaban ini sudah sangat ketinggalan zaman dan orang harus dapat menandainya (sayangnya ini tidak mungkin dilakukan).
codepleb
7
IOUtils.toString () telah lama ditinggalkan. Jawaban ini jelas bukan cara yang disarankan lagi.
Roshan
7
lalu edit untuk menjelaskan mengapa itu tidak digunakan lagi untuk membantu pembaca di masa mendatang.
Jean-François Fabre
2487

Ringkas jawaban lain, saya menemukan 11 cara utama untuk melakukan ini (lihat di bawah). Dan saya menulis beberapa tes kinerja (lihat hasil di bawah):

Cara untuk mengonversi InputStream ke String:

  1. Menggunakan IOUtils.toString(Apache Utils)

    String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
  2. Menggunakan CharStreams(jambu biji)

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

    Scanner s = new Scanner(inputStream).useDelimiter("\\A");
    String result = s.hasNext() ? s.next() : "";
  4. Menggunakan Stream API (Java 8). Peringatan : Solusi ini mengubah jeda baris yang berbeda (seperti \r\n) menjadi \n.

    String result = new BufferedReader(new InputStreamReader(inputStream))
      .lines().collect(Collectors.joining("\n"));
  5. Menggunakan paralel Stream API (Java 8). Peringatan : Solusi ini mengubah jeda baris yang berbeda (seperti \r\n) menjadi \n.

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

    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, "UTF-8");
    return writer.toString();
  8. Menggunakan ByteArrayOutputStreamdan 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. Menggunakan BufferedReader(JDK). Peringatan: Solusi ini mengubah jeda baris yang berbeda (seperti \n\r) ke line.separatorproperti sistem (misalnya, di Windows menjadi "\ 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. Menggunakan BufferedInputStreamdan 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. Menggunakan inputStream.read()dan StringBuilder(JDK). Peringatan : Solusi ini memiliki masalah dengan Unicode, misalnya dengan teks Rusia (berfungsi dengan benar hanya dengan teks non-Unicode)

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

Peringatan :

  1. Solusi 4, 5 dan 9 mengubah jeda baris yang berbeda menjadi satu.

  2. Solusi 11 tidak dapat bekerja dengan benar dengan teks Unicode

Tes kinerja

Tes kinerja untuk kecil String(panjang = 175), url di github (mode = Rata-rata Waktu, sistem = Linux, skor 1,343 adalah yang terbaik):

              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

Tes kinerja untuk big String(length = 50100), url in github (mode = Rata-Rata Waktu, sistem = Linux, skor 200.715 adalah yang terbaik):

               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

Grafik (tes kinerja tergantung pada panjang Input Stream di sistem Windows 7)
masukkan deskripsi gambar di sini

Tes kinerja (Waktu Rata-Rata) tergantung pada panjang Input Stream di sistem 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
Viacheslav Vedenin
sumber
17
Saat Anda menulis "jawaban ringkasan", Anda harus mencatat bahwa beberapa solusi secara otomatis mengkonversi berbagai linebreak (seperti \r\n) \nyang mungkin tidak diinginkan dalam beberapa kasus. Juga akan menyenangkan untuk melihat memori tambahan yang diperlukan atau setidaknya tekanan alokasi (setidaknya Anda dapat menjalankan JMH dengan -prof gc). Untuk posting yang benar-benar keren, akan sangat bagus untuk melihat grafik (tergantung pada panjang string dalam ukuran input yang sama dan tergantung pada ukuran input dalam panjang string yang sama).
Tagir Valeev
16
Dipilih terbalik; hal yang paling lucu adalah bahwa hasilnya lebih dari yang diharapkan: orang harus menggunakan gula sintaksis standar JDK dan / atau Apache Commons.
Aleksei Matiushkin
25
Pos luar biasa. Hanya satu hal. Java 8 memperingatkan agar tidak menggunakan aliran paralel pada sumber daya yang akan memaksa Anda untuk mengunci dan menunggu (seperti aliran input ini) sehingga opsi aliran paralel agak rumit dan tidak layak?
kakak beranak
10
Apakah aliran paralel benar-benar mempertahankan urutan garis?
Natix
6
Untuk apa reset()dalam contoh 11?
Rob Stewart
2307

Berikut cara menggunakan hanya pustaka Java standar (perhatikan bahwa streaming tidak ditutup, jarak tempuh Anda mungkin beragam).

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

Saya belajar trik ini dari artikel "Trupid Scanner tricks" . Alasan kerjanya adalah karena Pemindai mengulangi token dalam aliran, dan dalam kasus ini kami memisahkan token menggunakan "awal batas input" (\ A), sehingga memberi kami hanya satu token untuk seluruh konten aliran.

Catatan, jika Anda harus spesifik tentang pengkodean aliran input, Anda bisa memberikan argumen kedua ke Scannerkonstruktor yang menunjukkan karakter apa yang akan digunakan (mis. "UTF-8").

Tip hat juga berlaku untuk Jacob , yang pernah menunjuk saya ke artikel tersebut.

Pavel Repin
sumber
8
Terima kasih, untuk versi saya ini, saya menambahkan blok akhirnya yang menutup aliran input, sehingga pengguna tidak harus sejak Anda selesai membaca input. Menyederhanakan kode pemanggil.
4
@PavelRepin @ Patrick dalam kasus saya, inputStream kosong menyebabkan NPE selama konstruksi Scanner. Saya harus menambahkan if (is == null) return "";tepat di awal metode; Saya percaya jawaban ini perlu diperbarui untuk lebih menangani null inputStreams.
CFL_Jeff
115
Untuk Java 7 Anda dapat menutupnya dalam try-with: try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
earcam
5
Sayangnya solusi ini sepertinya hilang dan tidak ada pengecualian yang dilemparkan dalam implementasi aliran yang mendasarinya.
Taig
11
FYI, blok hasNext pada stream input konsol (lihat di sini ). (Baru saja mengalami masalah ini sekarang.) Solusi ini berfungsi dengan baik jika tidak ... hanya kepala saja.
Ryan
848

Apache Commons memungkinkan:

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

Tentu saja, Anda dapat memilih pengkodean karakter lain selain UTF-8.

Lihat juga: ( dokumentasi )

Chinnery
sumber
1
Juga, ada metode yang hanya mengambil argumen inputStream, jika Anda menemukan dengan pengkodean default.
Guillaume Coté
13
@ Guillaume Coté Saya kira pesannya di sini adalah Anda tidak boleh "baik-baik saja dengan pengkodean default", karena Anda tidak dapat memastikan apa itu, tergantung pada platform tempat kode java dijalankan.
Per Wiklander
7
@Per Wiklander, saya tidak setuju dengan Anda. Kode yang akan berfungsi pada satu bisa sangat yakin bahwa pengkodean default akan baik-baik saja. Untuk kode yang hanya membuka file lokal, itu adalah pilihan yang masuk akal untuk meminta mereka untuk dikodekan dalam pengkodean default platform.
Guillaume Coté
39
Untuk menyelamatkan siapa pun dari masalah Googling - <dependensi> <groupId> org.apache.commons </groupId> <artifactId> commons-io </artifactId> <version> 1.3.2 </version> </dependency>
Chris
7
Juga sedikit peningkatan akan menggunakan apache io (atau lainnya) konstan untuk pengkodean karakter daripada menggunakan string string literal - misalnya: IOUtils.toString (myInputStream, Charsets.UTF_8);
300

Memperhatikan file akun seseorang harus terlebih dahulu mendapatkan java.io.Readerinstance. Ini kemudian dapat dibaca dan ditambahkan ke StringBuilder(kita tidak perlu StringBufferjika kita tidak mengaksesnya di banyak utas, dan StringBuilderlebih cepat). Kuncinya di sini adalah bahwa kita bekerja dalam blok, dan karena itu tidak memerlukan aliran buffering lainnya. Ukuran blok parameter untuk optimasi kinerja run-time.

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
sumber
8
Solusi ini menggunakan karakter multibyte. Contoh ini menggunakan pengkodean UTF-8 yang memungkinkan ekspresi rentang unicode penuh (Termasuk bahasa Cina). Mengganti "UTF-8" dengan pengkodean lain akan memungkinkan pengkodean itu digunakan.
Paul de Vrieze
27
@ User1 - Saya suka menggunakan perpustakaan dalam kode saya sehingga saya bisa menyelesaikan pekerjaan lebih cepat. Sungguh luar biasa ketika manajer Anda mengatakan, "Wow James! Bagaimana Anda bisa menyelesaikannya begitu cepat ?!". Tetapi ketika kita harus menghabiskan waktu untuk menemukan kembali roda hanya karena kita telah salah menempatkan ide tentang memasukkan utilitas yang umum, dapat digunakan kembali, dicoba dan diuji, kita memberikan waktu kita dapat menghabiskan waktu untuk melanjutkan tujuan proyek kita. Ketika kami menemukan kembali roda, kami bekerja dua kali lebih keras namun sampai ke garis finish jauh di kemudian hari. Begitu kita berada di garis finish, tidak ada orang di sana untuk memberi selamat kepada kita. Saat membangun rumah, jangan membangun palu juga
jmort253
10
Maaf, setelah membaca kembali komentar saya, itu menjadi sedikit sombong. Saya hanya berpikir penting untuk memiliki alasan yang baik untuk menghindari perpustakaan dan bahwa alasannya adalah yang valid, yang mungkin ada :)
jmort253
4
@ jmort253 Kami melihat regresi kinerja setelah memperbarui beberapa perpustakaan di produk kami beberapa kali. Untungnya kami sedang membangun dan menjual produk kami sendiri sehingga kami tidak benar-benar memiliki tenggat waktu yang disebut. Sayangnya kami sedang membangun produk yang tersedia di banyak JVM, database, dan server aplikasi pada banyak sistem operasi, jadi kami harus berpikir untuk para pengguna yang menggunakan mesin yang buruk ... Dan pengoptimalan operasi string dapat meningkatkan perf sebesar 30 ~ 40%. Dan perbaikannya: In our product, I even replacedharus 'kita bahkan diganti'.
coolcfan
10
@ jmort253 Jika Anda sudah menggunakan apache commons, saya katakan, lakukan saja. Pada saat yang sama, ada biaya nyata untuk menggunakan perpustakaan (seperti proliferasi ketergantungan di banyak perpustakaan java apache menunjukkan). Jika ini akan menjadi satu-satunya penggunaan perpustakaan, itu akan berlebihan untuk menggunakan perpustakaan. Di sisi lain, menentukan ukuran buffer Anda sendiri, Anda dapat mengatur saldo penggunaan memori / prosesor Anda.
Paul de Vrieze
248

Menggunakan:

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
sumber
11
Masalahnya adalah, Anda pertama-tama membelah menjadi beberapa baris, dan kemudian membatalkannya. Lebih mudah dan lebih cepat untuk hanya membaca buffer sembarang.
Paul de Vrieze
20
Juga, readLine tidak membedakan antara \ n dan \ r, jadi Anda tidak dapat mereproduksi aliran yang tepat lagi.
María Arias de Reyna Domínguez
2
sangat tidak efisien, karena readLinekarakter dibaca oleh karakter untuk mencari EOL. Juga, jika tidak ada jeda baris dalam aliran, ini tidak benar-benar masuk akal.
njzk2
3
@ Gops AB: Jika Anda mencoba ini, dan sampel Anda memiliki baris baru di dalamnya, Anda akan melihat bahwa cara loop ini dibangun menggunakan readline () dan StringBuilder.append () sebenarnya tidak mempertahankan baris baru.
Russ Bateman
4
Ini bukan jawaban terbaik karena tidak sepenuhnya byte dalam byte keluar. Pembaca mengumpulkan baris baru, jadi Anda harus berhati-hati untuk memeliharanya.
Jeffrey Blattman
173

Jika Anda menggunakan Google-Koleksi / Jambu Anda dapat melakukan hal berikut:

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

Perhatikan bahwa parameter kedua (yaitu Charsets.UTF_8) untuk yang InputStreamReadertidak perlu, tetapi umumnya merupakan ide yang baik untuk menentukan pengkodean jika Anda mengetahuinya (yang seharusnya!)

Sakuraba
sumber
2
@harschware: Mengingat pertanyaannya adalah: "Jika Anda memiliki objek java.io.InputStream bagaimana Anda memproses objek itu dan menghasilkan sebuah String?" Saya berasumsi bahwa aliran sudah ada dalam situasi tersebut.
Sakuraba
Anda tidak menjelaskan jawaban Anda dengan sangat baik, dan memiliki variabel asing; user359996 mengatakan hal yang sama seperti Anda, tetapi jauh lebih jelas.
Uronym
2
+1 untuk jambu biji, -1 untuk tidak menentukan pengkodean aliran input. misalnya. InputStreamReader baru (streaming, "UTF-8")
andras
@ Chris Noldus Di sisi lain, beberapa orang sudah memiliki jambu biji dalam proyek mereka, seperti saya, dan berpikir solusi ini lebih elegan daripada versi sdk-only.
CorayThan
@ Vadzim jawaban itu sama dengan yang ini - keduanya menggunakan CharStreams.toString
Tom
125

Ini adalah solusi Java murni terbaik yang sangat cocok untuk Android dan JVM lainnya.

Solusi ini bekerja sangat baik ... sederhana, cepat, dan bekerja pada aliran kecil dan besar sama saja !! (lihat patokan di atas .. No. 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
sumber
4
Bekerja dengan baik di Android dibandingkan dengan jawaban lain yang hanya berfungsi di java perusahaan.
vortexwolf
Hancur di Android dengan kesalahan OutOfMemory pada baris ".write", setiap saat, untuk string pendek.
Adam
Saya telah menambahkan encoding. seperti catatan tambahan, metode readFully asli yang saya miliki di kode saya tidak mengembalikan String, ia mengembalikan byte [] untuk fungsionalitas tujuan yang lebih umum. Menerapkan String baru (...) dengan pengkodean adalah tanggung jawab atas yang menggunakan API!
TacB0sS
2
Catatan cepat: Jejak memori ini dimaksimalkan oleh 2*n, di mana n adalah ukuran stream, sesuai dengan ByteArrayInputStreamsistem penumbuhan otomatis.
njzk2
3
Penggunaan memori yang tidak perlu menggandakan, yang sangat berharga pada perangkat seluler. Anda sebaiknya menggunakan InputStreamReader dan menambahkan ke StringReader, konversi byte ke char akan dilakukan dengan cepat, tidak secara massal pada akhirnya.
Oliv
84

Untuk kelengkapan di sini adalah solusi Java 9 :

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

Saat readAllBytesini di JDK 9 basis kode utama, sehingga kemungkinan akan muncul dalam rilis. Anda dapat mencobanya sekarang menggunakan JDK 9 snapshot build .

Tagir Valeev
sumber
Bukankah metode ini mengalokasikan banyak memori untuk membaca? byte[] buf = new byte[DEFAULT_BUFFER_SIZE];mana MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;yang memberi MAX_BUFFER_SIZE = 2147483639. Google mengatakan sekitar 2,147 GB.
Rekin
Maaf, saya membuat kesalahan dalam perhitungan. Ini 2 GB. Saya sudah mengedit komentar. Jadi, bahkan jika saya membaca seperti file 4kb saya menggunakan memori 2gb?
Rekin
2
@ChristianHujer, saya tidak melihatnya di commit jdk8u terbaru . Metode baru AFAIK tidak pernah diperkenalkan di pembaruan Java, hanya dalam rilis utama.
Tagir Valeev
4
@ChristianHujer, pertanyaannya adalah tentang InputStream, bukan tentang Path. The InputStreamdapat dibuat dari berbagai sumber, tidak hanya file.
Tagir Valeev
5
Ini ditulis setahun yang lalu, jadi untuk memperbarui, saya mengkonfirmasi bahwa metode ini memang dalam rilis publik JDK 9. Selanjutnya, jika pengkodean Anda adalah "ISO-Latin-1" maka ini akan sangat efisien karena Java 9 Strings sekarang menggunakan sebuah byte[]implementasi jika semua karakter dalam pertama 256 poin kode. Ini berarti String baru (byte [], "ISO-Latin-1") akan menjadi salinan array sederhana.
Klitos Kyriacou
66

Menggunakan:

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
sumber
@ DanielDeLeón Tidak. Itu adalah BufferedInputStream. Bacaan yang mendasarinya adalah 8192 byte pada suatu waktu.
Marquis of Lorne
2
@ EJP Saya menemukan ini lebih lambat daripada menggunakan BufferedInputStream dan membaca ke dalam buffer array byte alih-alih satu byte pada suatu waktu. Contoh: 200ms vs 60ms saat membaca file 4,56 MiB.
jk7
Aneh bahwa tidak ada yang menunjuk masalah utama lainnya di sini (ya, membaca konten byte-by-byte boros bahkan dengan buffering): itu bergantung pada apa pun yang terjadi menjadi "pengkodean default" - ini jarang cara yang baik. Sebagai gantinya, pastikan untuk meneruskan pengkodean sebagai argumen buf.toString().
StaxMan
@ jk7 Waktu untuk membaca file 4,56MB sangat kecil sehingga perbedaannya tidak mungkin signifikan.
Marquis of Lorne
63

Inilah solusi paling elegan, murni-Jawa (tanpa perpustakaan) yang saya buat setelah beberapa eksperimen:

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
sumber
8
@TorbenKohlmeier, pembaca dan buffer tidak perlu ditutup. Yang disediakan InputStreamharus ditutup oleh penelepon.
Drew Noakes
7
Jangan lupa untuk menyebutkan bahwa ada konstruktor yang lebih disukai di InputStreamReader yang menggunakan CharSet.
jontejj
7
mengapa orang tetap menggunakan readLine? jika Anda tidak menggunakan garis per se, apa gunanya (kecuali menjadi sangat lambat?)
njzk2
4
Jangan membaca garis. Bagaimana jika satu baris begitu panjang sehingga tidak masuk dalam tumpukan?
voho
4
@ voho, jika satu baris sepanjang itu, maka tidak ada cara untuk mengalokasikan nilai kembali yang harus sama atau lebih besar ukurannya ke garis itu. Jika Anda berurusan dengan file yang besar, Anda harus mengalirkannya. Ada banyak kasus penggunaan untuk memuat file teks kecil ke dalam memori.
Drew Noakes
55

Saya melakukan patokan pada 14 jawaban berbeda di sini (maaf karena tidak memberikan kredit tetapi ada terlalu banyak duplikat).

Hasilnya sangat mengejutkan. Ternyata Apache IOUtils adalah yang paling lambat dan ByteArrayOutputStreammerupakan solusi tercepat:

Jadi yang pertama di sini adalah metode terbaik:

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

Hasil benchmark, sebesar 20 MB byte acak dalam 20 siklus

Waktu dalam milidetik

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

Kode sumber patokan

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
sumber
Membuat tolok ukur di Jawa tidak mudah (terutama karena JIT). Setelah membaca kode sumber Benchmark, saya yakin bahwa nilai-nilai di atas tidak tepat dan semua orang harus berhati-hati dengan memercayainya.
Dalibor
@ Dalibor Anda mungkin harus memberikan lebih banyak alasan untuk klaim Anda daripada hanya sebuah tautan.
Ilya Gazman
Saya pikir itu adalah fakta yang diketahui bahwa tidak mudah untuk membuat benchmark sendiri. Bagi mereka yang tidak tahu itu, ada tautan;)
Dalibor
@ Dalibor Saya mungkin bukan yang terbaik, tetapi saya memiliki pemahaman yang baik tentang tolok ukur Java, jadi kecuali Anda dapat menunjukkan masalah tertentu, Anda hanya menyesatkan, dan saya tidak akan melanjutkan percakapan dengan Anda dalam kondisi seperti itu.
Ilya Gazman
Kebanyakan saya setuju dengan Dalibor. Anda mengatakan Anda memiliki "pemahaman yang baik tentang tolok ukur Java", tetapi Anda tampaknya telah menerapkan pendekatan yang paling naif sementara tampaknya tidak mengetahui masalah yang dikenal dari pendekatan ini. Sebagai permulaan, baca setiap posting tentang pertanyaan ini: stackoverflow.com/questions/504103/…
DavidS
41

Saya akan menggunakan beberapa trik 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.
    }
}

Pada dasarnya sama dengan beberapa jawaban lain kecuali lebih ringkas.

Simon Kuang
sumber
5
Apakah itu return nullpernah dipanggil? Entah br.lines...pengembalian atau pengecualian dilemparkan.
Holloway
3
@Khaled A Khunaifer: ya, cukup yakin ... mungkin Anda harus melihatnya di sini: docs.oracle.com/javase/tutorial/essential/exceptions/… . Apa yang salah diedit adalah pernyataan "coba-pakai-sumber daya".
jamp
11
Mengapa Anda memanggil parallel()streaming?
robinst
4
Ini tidak akan menghasilkan salinan data yang jujur jika aliran sumber menggunakan ujung baris windows karena semuanya \r\nakan berakhir dikonversi menjadi \n...
Lucas
2
Anda bisa menggunakan System.lineSeparator()untuk menggunakan akhir baris yang sesuai dengan platform.
Steve K
34

Saya menjalankan beberapa tes waktu karena waktu penting, selalu.

Saya berusaha untuk mendapatkan respons ke dalam String 3 cara yang berbeda. (diperlihatkan di bawah)
Saya tidak lagi mencoba / menangkap blok demi pembacaan demi.

Untuk memberikan konteks, ini adalah kode sebelumnya untuk ketiga pendekatan:

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

Jadi, setelah menjalankan 500 tes pada setiap pendekatan dengan data permintaan / respons yang sama, inilah angkanya. Sekali lagi, ini adalah temuan saya dan temuan Anda mungkin tidak persis sama, tetapi saya menulis ini untuk memberikan beberapa indikasi kepada orang lain tentang perbedaan efisiensi dari pendekatan ini.

Peringkat:
Pendekatan # 1
Pendekatan # 3 - 2,6% lebih lambat dari # 1
Pendekatan # 2 - 4,3% lebih lambat dari # 1

Salah satu dari pendekatan ini adalah solusi yang tepat untuk meraih respons dan membuat sebuah String darinya.

Brett Holt
sumber
2
2) mengandung kesalahan, ia selalu menambahkan "null" di akhir string karena Anda selalu membuat satu langkah lagi yang diperlukan. Kinerja akan sama pula menurut saya. Ini seharusnya berfungsi: String read = null; StringBuffer sb = new StringBuffer (); while ((baca = br.readLine ())! = null) {sb.append (baca); }
LukeSolar
Perlu dicatat bahwa GetMethod adalah bagian dari org.apache.commons.httpclient, bukan Java standar
jk7
Pendekatan # 2 akan mengkonsumsi '\ n' jika file memiliki banyak baris, ini seharusnya bukan jawabannya
Ninja
33

Solusi Java murni menggunakan Stream s, bekerja sejak 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()));
    }
}

Seperti yang disebutkan oleh Christoffer Hammarström di bawah jawaban lain , lebih aman untuk menentukan Charset secara eksplisit . Yaitu, konstruktor InputStreamReader dapat diubah sebagai berikut:

new InputStreamReader(is, Charset.forName("UTF-8"))
czerny
sumber
11
Alih-alih melakukan Charset.forName("UTF-8"), gunakan StandardCharsets.UTF_8(dari java.nio.charset).
robinst
26

Berikut jawaban kurang lebih sampath, dibersihkan sedikit dan direpresentasikan sebagai fungsi:

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
sumber
24

Jika Anda merasa ingin bertualang, Anda bisa mencampur Scala dan Java dan berakhir dengan ini:

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

Mencampur kode Java dan Scala dan perpustakaan memiliki manfaatnya.

Lihat deskripsi lengkap di sini: Cara idiomatis untuk mengonversi InputStream ke String di Scala

Mendongkrak
sumber
3
Saat ini sederhana ini berfungsi dengan baik:Source.fromInputStream(...).mkString
KajMagnus
21

Jika Anda tidak dapat menggunakan Commons IO (FileUtils / IOUtils / CopyUtils), berikut adalah contoh menggunakan BufferedReader untuk membaca file baris demi baris:

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

Atau jika Anda ingin kecepatan mentah, saya akan mengusulkan variasi pada apa yang disarankan Paul de Vrieze (yang menghindari menggunakan StringWriter (yang menggunakan StringBuffer secara internal):

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
sumber
Untuk membuat kode Anda berfungsi, saya harus menggunakan this.getClass (). GetClassLoader (). GetResourceAsStream () (menggunakan Eclipse dengan proyek maven)
greuze
19

Yang ini bagus karena:

  • Itu aman menangani Charset.
  • Anda mengontrol ukuran buffer baca.
  • Anda dapat menentukan panjang pembuat dan tidak harus memiliki nilai yang tepat.
  • Bebas dari ketergantungan perpustakaan.
  • Apakah untuk Java 7 atau lebih tinggi.

Bagaimana cara melakukannya?

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

Untuk JDK 9

public static String inputStreamString(InputStream inputStream) throws IOException {
    try (inputStream) {
        return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
    }
}
Daniel De León
sumber
1
The catch (Throwable)seharusnya tidak benar-benar kosong jika ini adalah kode produksi.
Christian Hujer
1
apa yang harus dimasukkan dalam pernyataan catch-throwable ini?
alex
Meskipun menggunakan UTF-8 biasanya masuk akal, Anda tidak boleh menganggap karakter dikodekan seperti itu.
Martin
18

Ini adalah jawaban yang diadaptasi dari org.apache.commons.io.IOUtils kode sumber , bagi mereka yang ingin memiliki implementasi apache tetapi tidak ingin seluruh perpustakaan.

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
sumber
18

Pastikan untuk menutup aliran di akhir jika Anda menggunakan Pembaca Stream

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

EDIT: Pada JDK 7+, Anda dapat menggunakan konstruk coba-dengan-sumber daya.

/**
 * 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
sumber
2
Anda benar tentang menutup aliran, namun, tanggung jawab untuk menutup aliran biasanya dengan konstruktor aliran (selesaikan apa yang Anda mulai). Jadi, iStreamsebaiknya lebih suka ditutup oleh si penelepon karena si penelepon dibuat iStream. Selain itu, menutup aliran harus dilakukan dalam finallyblok, atau bahkan lebih baik dalam pernyataan coba-dengan sumber daya Java 7. Dalam kode Anda, saat readLine()melempar IOException, atau builder.append()melempar OutOfMemoryError, stream akan tetap terbuka.
Christian Hujer
16

Satu lagi, untuk semua pengguna 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);
}

Metode utilitas org.springframework.util.StreamUtilsmirip dengan yang ada di FileCopyUtils, tetapi mereka membiarkan aliran terbuka ketika selesai.

James
sumber
16

Gunakan java.io.InputStream.transferTo (OutputStream) yang didukung di Java 9 dan ByteArrayOutputStream.toString (String) yang menggunakan nama charset:

public static String gobble(InputStream in, String charsetName) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    in.transferTo(bos);
    return bos.toString(charsetName);
}
jmehrens
sumber
Apa yang Anda berikan untuk nama charset dalam kasus Anda?
virsha
1
@virsha Anda harus menentukannya dari sumber yang menyediakan InputStream. Ingatlah bahwa tidak memiliki string tanpa mengetahui encoding apa yang digunakannya.
jmehrens
15

Berikut adalah metode lengkap untuk mengubahnya InputStreammenjadi Stringtanpa menggunakan perpustakaan pihak ketiga mana pun. Gunakan StringBuilderuntuk lingkungan berulir tunggal jika tidak gunakan 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
sumber
3
Dalam metode ini tidak ada pengkodean yang diterapkan. Jadi katakanlah data yang diterima dari InputStream dikodekan menggunakan UTF-8 hasilnya akan salah. Untuk memperbaiki ini, Anda dapat menggunakan in = new InputStreamReader(inputStream)dan (char)in.read().
Frederic Leitenberger
2
dan memori-tidak efisien juga; Saya percaya saya mencoba menggunakan ini sebelumnya pada input besar dan StringBuilder kehabisan memori
gengkev
1
Ada jawaban lain yang serupa yang menggunakan buffer char [] dan lebih efisien dan merawat charset.
Guillaume Perrot
14

Berikut ini cara melakukannya hanya menggunakan JDK menggunakan buffer array byte. Inilah sebenarnya cara kerja bersama yang umum IOUtils.copy(). Anda dapat mengganti byte[]dengan char[]jika Anda menyalin dari Readerbukan 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
sumber
1
Tolong beri deskripsi tentang apa yang ingin Anda capai.
Ragunath Jawahar
14

Pengguna Kotlin cukup lakukan:

println(InputStreamReader(is).readText())

sedangkan

readText()

adalah metode ekstensi bawaan Kotlin perpustakaan standar.

Alex
sumber
Ini sebenarnya tidak sepenuhnya benar karena tidak menutup aliran. Saya akan merekomendasikan is.bufferedReader().use { it.readText() }.
Maks
9

Cara termudah di JDK adalah dengan snipplet kode berikut.

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

Inilah solusi berbasis Java 8 saya , yang menggunakan Stream API baru untuk mengumpulkan semua baris dari 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
sumber
1
Sepertinya Anda tidak benar-benar membaca semua jawaban yang diposting sebelumnya. Versi Stream API sudah ada di sini setidaknya dua kali .
Tagir Valeev
Saya telah melihat semua solusi, tetapi tidak menemukan yang cocok. Saya menemukan dua baris dengan deskripsi singkat yang disajikan dengan tepat. Try-catch-block dari solusi lain misalnya tidak pernah digunakan. Tapi kamu benar. Dengan begitu banyak jawaban, saya beralih ke mode membaca cepat ... :-)
Christian Rädel
1
Anda tidak membaca file asli, Anda mengonversi baris akhir apa pun yang dimiliki file ke baris akhir apa pun yang dimiliki OS, mungkin mengubah konten file.
Christian Hujer
7

Dalam hal reduce, dan concatitu dapat dinyatakan dalam Java 8 sebagai:

String fromFile = new BufferedReader(new   
InputStreamReader(inputStream)).lines().reduce(String::concat).get();
libnull-dev
sumber
1
Ini akan sangat lambat.
Tagir Valeev
Menarik, kenapa? Bisakah Anda menguraikan?
libnull-dev
1
Apakah Anda tidak tahu mengapa merangkai string dalam lingkaran daripada menggunakan StringBuilder adalah ide yang buruk?
Tagir Valeev
Kamu benar. StringBuildermungkin lebih efisien. Saya akan periksa, tetapi poin saya adalah menunjukkan pendekatan yang lebih fungsional dengan kekal String.
libnull-dev
Pendekatan fungsional memang keren tetapi biasanya sangat tidak efisien.
Lluis Martinez
4

JDK 7/8 jawaban yang menutup aliran dan masih melempar 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
sumber