Manera idiomática de convertir un InputStream en un String en Scala

111

Tengo una función útil que he usado en Java para convertir un InputStream en un String. Aquí hay una traducción directa a 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
  }

¿Hay alguna forma idiomática de hacer esto en scala?

bballante
fuente

Respuestas:

197

Para Scala> = 2.11

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

Para Scala <2.11:

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

hace prácticamente lo mismo. Sin embargo, no estoy seguro de por qué desea obtener líneas y luego pegarlas todas. Si puede asumir que la transmisión no se bloquea, puede usar .available, leer todo en una matriz de bytes y crear una cadena a partir de eso directamente.

Rex Kerr
fuente
2
Una posible razón, por la que me he usado, es normalizar los finales de línea en diferentes sistemas operativos.
Kevin Wright
La respuesta de Raam también es asombrosa (y un poco más concisa), pero marca la de Rex como LA respuesta, porque es más específicamente como el ejemplo. Pegar las líneas nuevamente fue específico en algunos casos, pero me recordó que he usado este código en lugares donde no es muy apropiado hacerlo.
bballant
la solución no es muy segura ya que usa getLines (); ¿Qué pasa si el flujo de entrada no tiene caracteres de "nueva línea"? entonces todo se bloquea
Paul Sabou
Bastante mala solución. ¿Qué pasa si el flujo de entrada contiene finales de línea DOS (\ r \ n). Estos serían eliminados por este método. Además, aunque mkString usa un búfer, seguramente sería más rápido leer bloques de caracteres.
Dibbeke
1
@RexKerr ¿Puede señalar el "error de rendimiento" que mencionó en su respuesta? Probé ambas versiones con algunos casos de prueba básicos y no encontré ningún problema.
Sahil Sareen
74

Source.fromInputStream(is).mkString("") también hará la escritura .....

raam
fuente
Buen punto; la fuente crea algo que se extiende Iterator[Char].
Rex Kerr
8
En general, es una buena práctica especificar también la codificación de caracteres al hacer este tipo de cosas. Con ese fin: Source.fromInputStream(is)(Codec.UTF8).mkString
Connor Doyle
Esto es conciso, pero no cierra la secuencia, mientras que el código original de Java lo hizo.
Rich
@Rich, fromInputStream()parece cerrar la transmisión, al menos en Scala 2.11.
jaco0646
2
@ jaco0646 - no cierra el flujo. Solo lo probé. Aquí hay un código de demostración que lo prueba: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich
13

Manera más rápida de hacer esto:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }
Kamil Lelonek
fuente
"Más rápido"? Pero me proporcionó la respuesta sobre cómo hacerlo cuando solo tengo un Readery no un InputStream.
BeepDog
3
Simplemente omita la primera línea y pase inputStreamReaderal método.
Kamil Lelonek
1
Esto es potencialmente un orden de magnitud más rápido que scala.io.Source en Scala 2.11.7. Escribí un punto de referencia realmente básico y la mayoría de las veces, fue aproximadamente un 5% más rápido para archivos grandes (la prueba fue de ~ 35 MB de archivo de texto) hasta un 2,800% más rápido para archivos pequeños (la prueba fue de ~ 30 KB) .
Colin Dean
2
Hermoso. He estado luchando por una solución elegante que lea grandes entradas de Runtime.exec(). Esto lo clava.
Pavel Lechev
¿Cómo especificaría un juego de caracteres para usar?
Wheeler