在Scala中将InputStream转换为String的惯用方法

118

我有一个在Java中用来将InputStream转换为String的便利函数。这里是直接转换成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
  }

在Scala中有没有惯用的方法来做到这一点?

3个回答

213

对于Scala >= 2.11版本

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

对于Scala < 2.11版本:

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

这段代码基本上做的是相同的事情。不过我不确定你为什么想要获取每行文本,然后将它们重新组合。如果你可以假设流是非阻塞的,那么你可以直接使用.available,将整个流读入一个字节数组中,然后直接从中创建字符串。


2
一个可能的原因,我自己也用过,是为了在不同操作系统上规范化行尾。 - Kevin Wright
Raam的回答也很棒(而且稍微更加简洁),但我选择将Rex的标记为“THE”答案,因为它更像例子。将这些行粘合在一起只是特定情况下的做法,但你提醒了我,在一些不太合适的地方我也使用了这段代码。 - bballant
解决方案并不是很安全,因为它使用了getLines(); 如果输入流中没有“换行符”,那么整个过程就会阻塞。 - Paul Sabou
相当糟糕的解决方案。如果输入流包含DOS行尾(\r\n),则这些行尾将被此方法删除。此外,虽然mkString使用缓冲区,但读取字符块肯定会更快。 - Dibbeke
@Dibbeke - 尽管不完全准确,但这是原帖中所说的输出结果。不管怎样,现在有一个更好的选择,我已经添加了。 - Rex Kerr
1
@RexKerr,您能否指出您在答案中提到的“性能缺陷”?我使用了一些基本测试用例测试了两个版本,并没有遇到任何问题。 - Sahil Sareen

75

Source.fromInputStream(is).mkString("")也可以完成该任务...


好的观点; 源代码创建了扩展Iterator[Char]的东西。 - Rex Kerr
10
这种情况下通常最好指定字符编码。为此,可以使用以下代码:Source.fromInputStream(is)(Codec.UTF8).mkString这行代码的作用是读取输入流并将其转换为字符串,使用的字符编码是UTF-8。 - Connor Doyle
1
这很简洁,但它没有关闭流,而原始的Java代码则关闭了。 - Rich
@Rich,fromInputStream() 在 Scala 2.11 中似乎会关闭流。 - jaco0646
4
@jaco0646 - 它不会关闭流。我刚测试了一下。这里是证明的演示代码:https://gist.github.com/RichardBradley/bcd1a5e61fcc83e4e59f8b9b0bc2301c - Rich
1
@Rich,你说得对。我再次查看了方法实现。它将InputStreamclose()方法包装在BufferedSourceclose()方法中,以便关闭源时关闭流。客户端仍然有责任关闭源。 - jaco0646

16

更快的方法是:

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

“更快”?但是它告诉我如何在只有Reader而没有InputStream的情况下进行操作。 - BeepDog
3
跳过第一行,将inputStreamReader传递给方法。 - Kamil Lelonek
1
这可能比Scala 2.11.7中的scala.io.Source快一个数量级。我写了一个非常基本的基准测试,大多数情况下,对于大文件(测试为35 MB文本文件),它大约快5%,对于小文件(测试为30 KB),它甚至可以快2800%。 - Colin Dean
2
太好了。一直在苦苦寻找一个优雅的解决方案,以从 Runtime.exec() 中读取大量输入。这个方法非常完美。 - Pavel Lechev
我应该如何指定要使用的字符集? - wheeler
多年后,我发现这里有一个错误:它会破坏行尾标记\n\r\r\n - Colin Dean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接