Cara idiomatis untuk mengonversi InputStream menjadi String di Scala

111

Saya memiliki fungsi praktis yang telah saya gunakan di Java untuk mengubah InputStream menjadi String. Berikut ini terjemahan langsung ke Scala:

  def inputStreamToString(is: InputStream) = {
    val rd: BufferedReader = new BufferedReader(new InputStreamReader(is, "UTF-8")) 
    val builder = new StringBuilder()    
    try {
      var line = rd.readLine 
      while (line != null) { 
        builder.append(line + "\n")
        line = rd.readLine
      }
    } finally {
      rd.close
    }
    builder.toString
  }

Apakah ada cara idiomatik untuk melakukan ini dalam skala?

bballant
sumber

Jawaban:

197

Untuk Scala> = 2.11

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

Untuk Scala <2.11:

scala.io.Source.fromInputStream(is).getLines().mkString("\n")

melakukan hal yang hampir sama. Tidak yakin mengapa Anda ingin mendapatkan garis dan kemudian merekatkan semuanya kembali. Jika Anda dapat mengasumsikan aliran tidak memblokir, Anda dapat menggunakan .available, membaca semuanya ke dalam array byte, dan membuat string dari itu secara langsung.

Rex Kerr
sumber
2
Salah satu alasan yang mungkin, yang saya gunakan sendiri, adalah menormalkan ujung baris pada sistem operasi yang berbeda.
Kevin Wright
Jawaban Raam juga mengagumkan (dan sedikit lebih ringkas), tetapi menandai jawaban Rex sebagai jawaban THE, karena lebih spesifik seperti contoh. Merekatkan kembali garis-garis itu khusus untuk beberapa kasus, tetapi Anda mengingatkan saya bahwa saya telah menggunakan kode ini di tempat-tempat yang kurang tepat untuk melakukan itu.
bballant
solusinya tidak terlalu aman karena menggunakan getLines (); bagaimana jika aliran masukan tidak memiliki karakter "baris baru"? kemudian semuanya
terhalang
Solusi yang cukup buruk. Bagaimana jika inputstream berisi akhir baris DOS (\ r \ n). Ini akan dihapus dengan metode ini. Juga, meskipun mkString menggunakan buffer, itu pasti akan lebih cepat untuk membaca blok karakter.
Dibbeke
1
@RexKerr Bisakah Anda menunjukkan "bug kinerja" yang Anda sebutkan dalam jawaban Anda. Saya menguji kedua versi dengan beberapa kasus pengujian dasar dan tidak menemukan masalah apa pun.
Sahil Sareen
74

Source.fromInputStream(is).mkString("") juga akan melakukan perbuatan .....

raam
sumber
Poin yang bagus; sumber menciptakan sesuatu yang meluas Iterator[Char].
Rex Kerr
8
Ini umumnya praktik yang baik untuk juga menentukan pengkodean karakter saat melakukan hal semacam ini. Untuk itu: Source.fromInputStream(is)(Codec.UTF8).mkString
Connor Doyle
Ini singkat, tetapi tidak menutup aliran, sedangkan kode java asli menutupnya.
Rich
@ Kaya, fromInputStream()tampaknya menutup aliran, setidaknya di Scala 2.11.
jaco0646
2
@ jaco0646 - itu tidak menutup aliran. Saya baru saja menguji. Berikut adalah kode demo yang membuktikannya: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich
13

Cara lebih cepat untuk melakukan ini:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }
Kamil Lelonek
sumber
"lebih cepat"? Tapi itu memberi saya jawaban tentang bagaimana melakukannya ketika saya hanya punya Readerdan bukan InputStream.
BeepDog
3
Lewati baris pertama, dan teruskan inputStreamReaderke metode.
Kamil Lelonek
1
Ini berpotensi urutan besarnya lebih cepat daripada scala.io.Sumber di Scala 2.11.7. Saya menulis tolok ukur yang sangat mendasar dan sebagian besar waktu, itu sekitar 5% lebih cepat untuk file besar (tes ~ 35 MB file teks) hingga 2.800% lebih cepat untuk file kecil (tes ~ 30 KB) .
Colin Dean
2
Cantik. Sedang berjuang untuk solusi elegan yang membaca masukan besar dari Runtime.exec(). Ini berhasil.
Pavel Lechev
Bagaimana saya menentukan kumpulan karakter yang akan digunakan?
wheeler