工作中需要通过Spark以csv格式导出日志文件。实现功能不太复杂,但是在中文识别上遇到了些问题。
解决csv文件中的乱码最直接的思路就是添加BOM,这样Excel在打开Excel的时候就知道使用什么样的编码来解析这篇文档了。
先写了段测试代码验证了下:
1 2 3 4 5 6 7 |
val bom = Array(0xEF, 0xBB, 0xBF).map(_.toByte) val result = ListBuffer(new String(bom) + "title\n") result += "en-chars\n" result += "中文字符" val w = new FileWriter("/my-file.csv") result.foreach(w.write) w.flush() |
这段代码会生成一个包含中文字符的csv文件。添加的BOM提示了生成的文件使用的是UTF-8编码。经测试使用Excel打开该文件可以识别中文字符。
将相同的逻辑写到Spark程序中,结果却没有预期的那么美好:中文乱码依然存在,并且新添加的BOM也变成了乱码。BOM变成了这几个奇怪又熟悉的字符:
1 |
锟斤拷锟潔 |
最后面那个“潔”还吞了一个英文字符e
。
在十六进制下查看BOM变成了这几个字符:
1 |
EF BF BD EF BF BD EF BF BD |
也就是说BOM中的三个字符每个都被替换成了EF BF BD
。
查了下资料,这三个字符的意义是这样的:
它是很多编程语言以及库中的备胎,即无效的码点值在编码的时候会默认用这个码点值进行替换,即utf-8中的超级「备胎」(REPLACEMENT CHARACTER)
也就是说Spark将我添加的BOM字符串视为了无效字符。
为什么会这样呢?一时陷入了无效的思索和尝试中。
说下最后的解决方案吧,巨简单:在使用BOM数组创建字符串的时候指明使用UTF8编码。就是像下面这样:
1 2 |
val bom = Array(0xEF, 0xBB, 0xBF).map(_.toByte) //添加UTF BOM或者说UTF签名 var result = ListBuffer(new String(bom, StandardCharsets.UTF_8) + title) |
会出现这个问题估计是因为服务器的默认文件编码不是UTF-8导致的。
发表评论