Jika Anda memiliki java.io.InputStream
objek, bagaimana Anda memproses objek itu dan menghasilkan String
?
Misalkan saya punya InputStream
yang berisi data teks, dan saya ingin mengonversinya menjadi String
, jadi misalnya saya bisa menulis itu ke file log.
Apa cara termudah untuk mengambil InputStream
dan mengubahnya menjadi String
?
public String convertStreamToString(InputStream is) {
// ???
}
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);
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.Jawaban:
Cara yang bagus untuk melakukan ini adalah menggunakan Apache commons
IOUtils
untuk menyalinInputStream
keStringWriter
... sesuatu sepertiatau bahkan
Atau, Anda dapat menggunakan
ByteArrayOutputStream
jika Anda tidak ingin mencampur Streaming dan Penulis Andasumber
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:
Menggunakan
IOUtils.toString
(Apache Utils)Menggunakan
CharStreams
(jambu biji)Menggunakan
Scanner
(JDK)Menggunakan Stream API (Java 8). Peringatan : Solusi ini mengubah jeda baris yang berbeda (seperti
\r\n
) menjadi\n
.Menggunakan paralel Stream API (Java 8). Peringatan : Solusi ini mengubah jeda baris yang berbeda (seperti
\r\n
) menjadi\n
.Menggunakan
InputStreamReader
danStringBuilder
(JDK)Menggunakan
StringWriter
danIOUtils.copy
(Apache Commons)Menggunakan
ByteArrayOutputStream
daninputStream.read
(JDK)Menggunakan
BufferedReader
(JDK). Peringatan: Solusi ini mengubah jeda baris yang berbeda (seperti\n\r
) keline.separator
properti sistem (misalnya, di Windows menjadi "\ r \ n").Menggunakan
BufferedInputStream
danByteArrayOutputStream
(JDK)Menggunakan
inputStream.read()
danStringBuilder
(JDK). Peringatan : Solusi ini memiliki masalah dengan Unicode, misalnya dengan teks Rusia (berfungsi dengan benar hanya dengan teks non-Unicode)Peringatan :
Solusi 4, 5 dan 9 mengubah jeda baris yang berbeda menjadi satu.
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):Tes kinerja untuk big
String
(length = 50100), url in github (mode = Rata-Rata Waktu, sistem = Linux, skor 200.715 adalah yang terbaik):Grafik (tes kinerja tergantung pada panjang Input Stream di sistem Windows 7)
Tes kinerja (Waktu Rata-Rata) tergantung pada panjang Input Stream di sistem Windows 7:
sumber
\r\n
)\n
yang 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).reset()
dalam contoh 11?Berikut cara menggunakan hanya pustaka Java standar (perhatikan bahwa streaming tidak ditutup, jarak tempuh Anda mungkin beragam).
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
Scanner
konstruktor yang menunjukkan karakter apa yang akan digunakan (mis. "UTF-8").Tip hat juga berlaku untuk Jacob , yang pernah menunjuk saya ke artikel tersebut.
sumber
if (is == null) return "";
tepat di awal metode; Saya percaya jawaban ini perlu diperbarui untuk lebih menangani null inputStreams.try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
Apache Commons memungkinkan:
Tentu saja, Anda dapat memilih pengkodean karakter lain selain UTF-8.
Lihat juga: ( dokumentasi )
sumber
Memperhatikan file akun seseorang harus terlebih dahulu mendapatkan
java.io.Reader
instance. Ini kemudian dapat dibaca dan ditambahkan keStringBuilder
(kita tidak perluStringBuffer
jika kita tidak mengaksesnya di banyak utas, danStringBuilder
lebih 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.sumber
In our product, I even replaced
harus 'kita bahkan diganti'.Menggunakan:
sumber
readLine
karakter dibaca oleh karakter untuk mencari EOL. Juga, jika tidak ada jeda baris dalam aliran, ini tidak benar-benar masuk akal.Jika Anda menggunakan Google-Koleksi / Jambu Anda dapat melakukan hal berikut:
Perhatikan bahwa parameter kedua (yaitu Charsets.UTF_8) untuk yang
InputStreamReader
tidak perlu, tetapi umumnya merupakan ide yang baik untuk menentukan pengkodean jika Anda mengetahuinya (yang seharusnya!)sumber
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 )
sumber
2*n
, di mana n adalah ukuran stream, sesuai denganByteArrayInputStream
sistem penumbuhan otomatis.Untuk kelengkapan di sini adalah solusi Java 9 :
Saat
readAllBytes
ini di JDK 9 basis kode utama, sehingga kemungkinan akan muncul dalam rilis. Anda dapat mencobanya sekarang menggunakan JDK 9 snapshot build .sumber
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
manaMAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
yang memberiMAX_BUFFER_SIZE = 2147483639
. Google mengatakan sekitar 2,147 GB.InputStream
, bukan tentangPath
. TheInputStream
dapat dibuat dari berbagai sumber, tidak hanya file.byte[]
implementasi jika semua karakter dalam pertama 256 poin kode. Ini berarti String baru (byte [], "ISO-Latin-1") akan menjadi salinan array sederhana.Menggunakan:
sumber
BufferedInputStream
. Bacaan yang mendasarinya adalah 8192 byte pada suatu waktu.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.buf.toString()
.Inilah solusi paling elegan, murni-Jawa (tanpa perpustakaan) yang saya buat setelah beberapa eksperimen:
sumber
InputStream
harus ditutup oleh penelepon.readLine
? jika Anda tidak menggunakan garis per se, apa gunanya (kecuali menjadi sangat lambat?)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
ByteArrayOutputStream
merupakan solusi tercepat:Jadi yang pertama di sini adalah metode terbaik:
Hasil benchmark, sebesar 20 MB byte acak dalam 20 siklus
Waktu dalam milidetik
Kode sumber patokan
sumber
Saya akan menggunakan beberapa trik Java 8.
Pada dasarnya sama dengan beberapa jawaban lain kecuali lebih ringkas.
sumber
return null
pernah dipanggil? Entahbr.lines...
pengembalian atau pengecualian dilemparkan.parallel()
streaming?\r\n
akan berakhir dikonversi menjadi\n
...System.lineSeparator()
untuk menggunakan akhir baris yang sesuai dengan platform.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:
1)
2)
3)
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.
sumber
Solusi Java murni menggunakan Stream s, bekerja sejak Java 8.
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:
sumber
Charset.forName("UTF-8")
, gunakanStandardCharsets.UTF_8
(darijava.nio.charset
).Berikut jawaban kurang lebih sampath, dibersihkan sedikit dan direpresentasikan sebagai fungsi:
sumber
Jika Anda merasa ingin bertualang, Anda bisa mencampur Scala dan Java dan berakhir dengan ini:
Mencampur kode Java dan Scala dan perpustakaan memiliki manfaatnya.
Lihat deskripsi lengkap di sini: Cara idiomatis untuk mengonversi InputStream ke String di Scala
sumber
Source.fromInputStream(...).mkString
Jika Anda tidak dapat menggunakan Commons IO (FileUtils / IOUtils / CopyUtils), berikut adalah contoh menggunakan BufferedReader untuk membaca file baris demi baris:
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):
sumber
Yang ini bagus karena:
Bagaimana cara melakukannya?
Untuk JDK 9
sumber
catch (Throwable)
seharusnya tidak benar-benar kosong jika ini adalah kode produksi.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.sumber
Pastikan untuk menutup aliran di akhir jika Anda menggunakan Pembaca Stream
EDIT: Pada JDK 7+, Anda dapat menggunakan konstruk coba-dengan-sumber daya.
sumber
iStream
sebaiknya lebih suka ditutup oleh si penelepon karena si penelepon dibuatiStream
. Selain itu, menutup aliran harus dilakukan dalamfinally
blok, atau bahkan lebih baik dalam pernyataan coba-dengan sumber daya Java 7. Dalam kode Anda, saatreadLine()
melemparIOException
, ataubuilder.append()
melemparOutOfMemoryError
, stream akan tetap terbuka.Satu lagi, untuk semua pengguna Spring:
Metode utilitas
org.springframework.util.StreamUtils
mirip dengan yang ada diFileCopyUtils
, tetapi mereka membiarkan aliran terbuka ketika selesai.sumber
Gunakan java.io.InputStream.transferTo (OutputStream) yang didukung di Java 9 dan ByteArrayOutputStream.toString (String) yang menggunakan nama charset:
sumber
Berikut adalah metode lengkap untuk mengubahnya
InputStream
menjadiString
tanpa menggunakan perpustakaan pihak ketiga mana pun. GunakanStringBuilder
untuk lingkungan berulir tunggal jika tidak gunakanStringBuffer
.sumber
in = new InputStreamReader(inputStream)
dan(char)in.read()
.Berikut ini cara melakukannya hanya menggunakan JDK menggunakan buffer array byte. Inilah sebenarnya cara kerja bersama yang umum
IOUtils.copy()
. Anda dapat menggantibyte[]
denganchar[]
jika Anda menyalin dariReader
bukanInputStream
.sumber
Pengguna Kotlin cukup lakukan:
sedangkan
adalah metode ekstensi bawaan Kotlin perpustakaan standar.
sumber
is.bufferedReader().use { it.readText() }
.Cara termudah di JDK adalah dengan snipplet kode berikut.
sumber
Inilah solusi berbasis Java 8 saya , yang menggunakan Stream API baru untuk mengumpulkan semua baris dari
InputStream
:sumber
Dalam hal
reduce
, danconcat
itu dapat dinyatakan dalam Java 8 sebagai:sumber
StringBuilder
mungkin lebih efisien. Saya akan periksa, tetapi poin saya adalah menunjukkan pendekatan yang lebih fungsional dengan kekalString
.JDK 7/8 jawaban yang menutup aliran dan masih melempar IOException:
sumber