Scala读取CSV文件时抛出java.nio.charset.MalformedInputException异常
解决Scala读取CSV时的MalformedInputException问题
这个问题我之前处理过好几次!你遇到的java.nio.charset.MalformedInputException本质是CSV文件的编码和Scala默认使用的字符集不匹配导致的。
为什么会出现这个异常?
Scala的io.Source.fromFile方法默认会使用系统默认的字符集(比如Windows系统通常是GBK,Linux/macOS则是UTF-8)。如果你的CSV文件是用其他编码保存的(比如UTF-8带BOM、GB2312、ISO-8859-1等),解码器遇到无法解析的字节时就会抛出这个异常。
解决方案
1. 指定正确的编码读取文件
最直接的方法是在读取时明确指定CSV文件的实际编码。你可以先用文本编辑器(比如Notepad++、VS Code)查看文件的编码,然后在代码中指定:
// 假设文件是UTF-8编码,直接指定 val bufferedSource = io.Source.fromFile(destFile, "UTF-8") // 如果是GBK编码则替换为"GBK" // val bufferedSource = io.Source.fromFile(destFile, "GBK") try { for (line <- bufferedSource.getLines) { rows += line.split(",").map(_.trim) } } finally { bufferedSource.close() // 别忘了关闭资源! }
注意:一定要记得关闭
bufferedSource,避免资源泄漏,用try-finally块或者Scala 2.13+的Using语句更安全。
2. 处理UTF-8带BOM的特殊情况
有些Windows环境下生成的UTF-8文件会带有BOM(字节顺序标记),直接用UTF-8编码读取时,第一行开头会有一个不可见的特殊字符,也可能引发解析异常。这种情况可以手动跳过BOM:
val bufferedSource = io.Source.fromFile(destFile, "UTF-8") val lines = bufferedSource.getLines() val actualLines = if (lines.hasNext) { val firstLine = lines.next() // 判断是否带BOM标记,是的话截取掉开头的BOM if (firstLine.startsWith("\uFEFF")) { Iterator(firstLine.substring(1)) ++ lines } else { Iterator(firstLine) ++ lines } } else { lines } try { for (line <- actualLines) { rows += line.split(",").map(_.trim) } } finally { bufferedSource.close() }
3. 使用专业的CSV库(推荐)
手动用split(",")处理CSV存在很多隐患,比如字段内容包含逗号(通常会用双引号包裹)时,split会直接把字段拆断。推荐使用专门的CSV处理库,比如scala-csv,它能自动处理编码、引号包裹的字段等复杂情况:
首先在sbt中添加依赖:
libraryDependencies += "com.github.tototoshi" %% "scala-csv" % "1.3.10"
然后编写读取代码:
import com.github.tototoshi.csv._ // 指定编码打开文件,自动处理各种CSV格式问题 val reader = CSVReader.open(new java.io.File(destFile), "UTF-8") try { // 直接获取所有行,每行是一个字符串数组 val rows = reader.all() } finally { reader.close() }
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

