Scala中Source.fromInputStream未抛出MalformedInputException问题排查
我来帮你拆解这个问题——核心原因是Scala的Source默认采用了“容错”的UTF-8解码策略,不会因为无效字节直接抛出异常,而是悄悄替换成占位符。
问题根源:默认的编码错误处理策略
Scala的Source.fromInputStream底层依赖Java的InputStreamReader,而Java的CharsetDecoder(UTF-8解码器)默认的错误处理行为是CodingErrorAction.REPLACE:遇到无效的UTF-8字节时,会用�这个替换字符代替错误字节,程序继续执行,不会抛出MalformedInputException。
你代码里加的128.toByte(也就是0x80)是一个无效的UTF-8起始字节,按照默认策略,它会被替换成占位符,而不是触发异常。你可以尝试输出读取到的内容,会看到开头多了一个�,比如�hellö wörld,这就证明了无效字节被处理掉了。
如何让它抛出预期的异常?
如果需要严格校验UTF-8的有效性,必须显式修改解码器的错误处理策略为CodingErrorAction.REPORT,这样遇到无效字节时就会抛出异常。这里是修改后的完整代码:
import java.io.ByteArrayInputStream import java.nio.charset.{CharsetDecoder, CodingErrorAction, StandardCharsets} import scala.io.Source val stringSourceAsBytes = "hellö wörld".getBytes(StandardCharsets.UTF_8) val messedUpUTF8 = 128.toByte +: stringSourceAsBytes val linesIterator: Iterator[String] = try { val input = new ByteArrayInputStream(messedUpUTF8) // 自定义UTF-8解码器,开启错误报告模式 val decoder: CharsetDecoder = StandardCharsets.UTF_8.newDecoder() .onMalformedInput(CodingErrorAction.REPORT) .onUnmappableCharacter(CodingErrorAction.REPORT) // 使用自定义解码器创建Source Source.fromInputStream(input)(decoder).getLines() } catch { case e: java.nio.charset.MalformedInputException => println(s"捕获到预期的异常:${e.getMessage}") Iterator.empty }
这段代码中,我们手动创建了UTF-8解码器,把onMalformedInput和onUnmappableCharacter都设置为REPORT,这样解码器遇到无效字节时就会抛出MalformedInputException,和你预期的行为一致。
补充说明
默认的REPLACE策略是为了让程序在遇到脏数据时尽量继续运行,避免直接崩溃。但如果你的场景需要严格校验输入的UTF-8合法性,就必须手动修改这个策略。
内容的提问来源于stack exchange,提问作者humbletrader

