scala.io.Source.fromInputStream不会进一步抛出MalformedInputException

时间:2018-04-04 21:06:46

标签: scala java-io

我有以下scala代码,它接受一个字符串,与UTF-8字符混淆,然后尝试通过Source.fromInputStream读取它:

import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets.UTF_8

import scala.io.Source

val stringSourceAsBytes = "hellö wörld".getBytes(UTF_8)

val messedUpUTF8 = 128.toByte +: stringSourceAsBytes

val linesIterator : Iterator[String] =
try {
  val input = new ByteArrayInputStream(messedUpUTF8)
  Source.fromInputStream(input).getLines()
}catch{
  case exc: Throwable => println(" This is an exception !")
  Iterator()
}

linesIterator.mkString("\n")

我不应该看到"这是一个例外!"信息 ?因为我没有看到它。 事实上,我得到了一个印刷的堆栈跟踪,但我无法捕捉异常并正确处理它......

BTW:我的控制台显示:

java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(IO.sc:277)
   at sun.nio.cs.StreamDecoder.implRead(IO.sc:335)
   at sun.nio.cs.StreamDecoder.read(IO.sc:174)
   at java.io.InputStreamReader.read(IO.sc:181)
   at java.io.BufferedReader.fill(IO.sc:157)
   at java.io.BufferedReader.readLine(IO.sc:322)
   at java.io.BufferedReader.readLine(IO.sc:388)
   at scala.io.BufferedSource$BufferedLineIterator.hasNext(IO.sc:66)
   at scala.collection.Iterator.toString(IO.sc:1409)
   at scala.collection.Iterator.toString$(IO.sc:1409)
   at scala.collection.AbstractIterator.toString(IO.sc:1413)
   at #worksheet#.#worksheet#(IO.sc:53)

1 个答案:

答案 0 :(得分:1)

这里有两件有趣的事情发生在这里:

  • 懒惰迭代器和try-catch - 块
  • 的经典错误
  • REPL的奇怪行为,在尝试用户友好时自我吹嘘。

您没有看到"This is an exception !" - 消息,因为实例化延迟迭代器不会尝试从流中读取单个字节。这个try-catch块成功并愉快地返回滴答作响的定时炸弹,实际错误发生在try-catch之外。

但是,如果强制迭代器获取所有字节,例如通过附加.mkString

import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets.UTF_8

import scala.io.Source

val stringSourceAsBytes = "hellö wörld".getBytes(UTF_8)
val messedUpUTF8 = 128.toByte +: stringSourceAsBytes

val streamContent =
try {
  val input = new ByteArrayInputStream(messedUpUTF8)
  Source.fromInputStream(input).getLines().mkString("\n")
}catch{
  case exc: Throwable => println(" This is an exception !")
}

然后你得到输出:

 This is an exception !

正如所料。您的堆栈跟踪似乎来自其他地方,请再次检查确切的行号。

问题编辑后更新

要在更新的代码中查看" This is an exception !"消息,您必须捕获抛出异常的位置,而不是定义惰性迭代器的位置:

import java.io.ByteArrayInputStream 
import java.nio.charset.StandardCharsets.UTF_8 
import scala.io.Source 

val stringSourceAsBytes = "hellö wörld".getBytes(UTF_8) 
val messedUpUTF8 = 128.toByte +: stringSourceAsBytes

// building the exception-bomb is harmless
val linesIterator: Iterator[String] = { 
  val input = new ByteArrayInputStream(messedUpUTF8) 
  Source.fromInputStream(input).getLines() 
} 


val combinedLines: String = try {
  // detonating the exception-bomb should be surrounded by try-catch
  linesIterator.mkString("\n")
} catch { 
  case exc: Throwable => {
    println(" This is an exception !") 
    ""
  }
} 

再次打印This is an exception !并将combinedLines设置为空字符串。

EDIT-2:REPL

如果由于某种原因坚持在repl中运行它,那么你就不能让“中毒”迭代器逃到范围内,因为Repl因某些原因无法处理它,并且自我吹嘘。

这在repl中有效,但这与第一个解决方案基本相同:

import java.io.ByteArrayInputStream 
import java.nio.charset.StandardCharsets.UTF_8 
import scala.io.Source 

val stringSourceAsBytes = "hellö wörld".getBytes(UTF_8) 
val messedUpUTF8 = 128.toByte +: stringSourceAsBytes 

val combinedLines: String = try {
  val linesIterator: Iterator[String] = { 
    val input = new ByteArrayInputStream(messedUpUTF8) 
    Source.fromInputStream(input).getLines() 
  } 
  linesIterator.mkString("\n")
} catch { 
  case exc: Throwable => {
    println(" This is an exception !") 
    ""
  }
}

REPL无法处理即将抛出异常的迭代器。原因是它在迭代器上调用hasNext(它为有效迭代器打印non-empty iterator描述,因此它必须调用hasNext一次)。但是,当您调用hasNext时,您的流会抛出异常,如下面的代码段所示:

scala> val it = try { 
  Source.fromInputStream(
    new ByteArrayInputStream(messedUpUTF8)).getLines().hasNext 
} catch { case t: Throwable => 
  println("yes, hasNext blows up the REPL")  
}

结果:

yes, hasNext blows up the REPL

以脚本运行(或者尝试粘贴模式),然后按预期工作。