Maneira idiomática de converter InputStream em String no Scala

111

Tenho uma função útil que usei em Java para converter um InputStream em String. Aqui está uma tradução direta para 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
  }

Existe uma maneira idiomática de fazer isso no scala?

bballant
fonte

Respostas:

197

Para Scala> = 2,11

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

Para Scala <2.11:

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

faz praticamente a mesma coisa. Não sei por que você deseja obter linhas e, em seguida, colá-las todas juntas, no entanto. Se você pode assumir que o fluxo não é bloqueador, pode simplesmente usar .available, ler tudo em uma matriz de bytes e criar uma string diretamente a partir dela.

Rex Kerr
fonte
2
Uma possível razão, que usei sozinho, é normalizar terminações de linha em diferentes sistemas operacionais.
Kevin Wright,
A resposta de Raam também é incrível (e um pouco mais concisa), mas marcando a de Rex como A resposta, porque é mais especificamente como o exemplo. Colar as linhas de volta foi específico em alguns casos, mas você me lembrou que usei esse código em lugares onde não é muito apropriado fazer isso.
bballant
a solução não é muito segura, pois usa getLines (); e se o fluxo de entrada não tiver caracteres de "nova linha"? então tudo
fica bloqueado
Uma solução bastante ruim. E se o fluxo de entrada contiver terminações de linha DOS (\ r \ n). Eles seriam removidos por este método. Além disso, embora mkString use um buffer, certamente seria mais rápido ler blocos de caracteres.
Dibbeke
1
@RexKerr Você pode apontar o "bug de desempenho" que mencionou em sua resposta. Eu testei as duas versões com alguns casos de teste básicos e não encontrei nenhum problema.
Sahil Sareen de
74

Source.fromInputStream(is).mkString("") também fará a ação .....

raam
fonte
Bom ponto; a fonte cria algo que se estende Iterator[Char].
Rex Kerr
8
Geralmente, é uma boa prática especificar a codificação de caracteres ao fazer esse tipo de coisa. Para esse fim: Source.fromInputStream(is)(Codec.UTF8).mkString
Connor Doyle
Isso é conciso, mas não fecha o fluxo, enquanto o código Java original o fazia.
Rich
@Rich, fromInputStream()parece fechar o fluxo, pelo menos em Scala 2.11.
jaco0646
2
@ jaco0646 - não fecha o fluxo. Acabei de testar. Aqui está o código de demonstração que prova isso: gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c
Rich
13

Maneira mais rápida de fazer isso:

    private def inputStreamToString(is: InputStream) = {
        val inputStreamReader = new InputStreamReader(is)
        val bufferedReader = new BufferedReader(inputStreamReader)
        Iterator continually bufferedReader.readLine takeWhile (_ != null) mkString
    }
Kamil Lelonek
fonte
"Mais rápido"? Mas me deu a resposta de como fazer quando eu só tenho um Readere não um InputStream.
BeepDog
3
Basta pular a primeira linha e passar inputStreamReaderpara o método.
Kamil Lelonek
1
Isso é potencialmente uma ordem de magnitude mais rápida do que scala.io.Source no Scala 2.11.7. Eu escrevi um benchmark realmente básico dele e na maioria das vezes, foi cerca de 5% mais rápido para arquivos grandes (o teste foi de arquivo de texto de ~ 35 MB) até 2.800% mais rápido para arquivos pequenos (o teste foi de ~ 30 KB) .
Colin Dean
2
Lindo. Tenho lutado por uma solução elegante para ler grandes entradas de Runtime.exec(). Isso acerta.
Pavel Lechev
Como especificaria um conjunto de caracteres a ser usado?
wheeler