Modo idiomatico per convertire un InputStream in una stringa in Scala


111

Ho una comoda funzione che ho usato in Java per convertire un InputStream in una stringa. Ecco una traduzione diretta in 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
  }

C'è un modo idiomatico per farlo in scala?

Risposte:


197

Per Scala> = 2.11

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

Per Scala <2.11:

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

fa più o meno la stessa cosa. Non sono sicuro del motivo per cui vuoi ottenere le linee e poi incollarle tutte insieme, però. Se puoi presumere che il flusso non sia bloccato, puoi semplicemente usare .available, leggere l'intera cosa in un array di byte e creare una stringa direttamente da quello.


2
Una possibile ragione per cui ho usato me stesso è normalizzare le terminazioni di riga su diversi sistemi operativi.
Kevin Wright

Anche la risposta di Raam è fantastica (e leggermente più concisa), ma segna Rex come LA risposta, perché è più specificamente come l'esempio. Ricollegare le linee insieme era specifico in alcuni casi, ma mi hai ricordato che ho usato questo codice in luoghi in cui non è del tutto appropriato farlo.
bballant

la soluzione non è molto sicura in quanto utilizza getLines (); cosa succede se il flusso di input non ha caratteri "nuova riga"? poi tutto si blocca
Paul Sabou

Piuttosto una cattiva soluzione. Cosa succede se il flusso di input contiene terminazioni di riga DOS (\ r \ n). Questi verrebbero rimossi con questo metodo. Inoltre, sebbene mkString utilizzi un buffer, sarebbe sicuramente più veloce leggere blocchi di caratteri.
Dibbeke

1
@RexKerr Puoi per favore sottolineare il "bug di prestazioni" che hai menzionato nella tua risposta. Ho testato entrambe le versioni con alcuni casi di test di base e non ho riscontrato alcun problema.
Sahil Sareen

74

Source.fromInputStream(is).mkString("") farà anche l'atto .....


Buon punto; la fonte crea qualcosa che si estende Iterator[Char].
Rex Kerr

8
È generalmente buona norma specificare anche la codifica dei caratteri quando si fa questo genere di cose. A tal fine: Source.fromInputStream(is)(Codec.UTF8).mkString
Connor Doyle

Questo è conciso, ma non chiude il flusso, mentre il codice Java originale lo faceva.
Rich

@Rich, fromInputStream()sembra chiudere lo stream, almeno in Scala 2.11.
jaco0646

2
@ jaco0646 - non chiude lo stream. Ho appena provato. Ecco il codice demo che lo dimostra: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich

13

Modo più veloce per farlo:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }

"Più veloce"? Ma mi ha fornito la risposta su come farlo quando ho solo un Readere non un InputStream.
BeepDog

3
Basta saltare la prima riga e passare inputStreamReadera metodo.
Kamil Lelonek

1
Questo è potenzialmente un ordine di grandezza più veloce di scala.io Fonte in Scala 2.11.7. Ho scritto un benchmark di base e il più delle volte era circa il 5% più veloce per i file di grandi dimensioni (il test era di ~ 35 MB di file di testo) fino al 2.800% più veloce per i file piccoli (il test era di ~ 30 KB) .
Colin Dean

2
Bellissimo. Ho lottato per una soluzione elegante che legge grandi input da Runtime.exec(). Questo lo inchioda.
Pavel Lechev

Come specificare un set di caratteri da utilizzare?
Wheeler
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.