上周遇到的一个问题,代码里有通过scala.io.Source.fromFile
方式读取文件内容,并且没有指定编码格式,开始程序运行是ok的,后来添加了中文,发现在某些情况下会遇到乱码问题,并不是必然发生的,而是不同的人ssh登录到linux的测试环境启动应用可能会有不同的结果。这里记录一下当时的情况。
问题的排查,首先确认Source.fromFile
这个api里面对编码的使用方式:
def fromFile(name: String)(implicit codec: Codec): BufferedSource = fromFile(new JFile(name))(codec)
不指定编码的话,系统使用了一个隐式参数作为默认编码,看一下它是什么:
scala> def f(implicit c: io.Codec) = cf: (implicit c: scala.io.Codec)scala.io.Codecscala> f.nameres6: String = US-ASCII
这台linux上显示的是US-ASCII
,而在我的mac上是UTF-8
,看来是编码设置的问题,这个值应该是jvm的系统属性里编码相关的:
scala> System.getProperties.foreach(s => if (s.toString.indexOf("enc") != -1) println(s) )(file.encoding.pkg,sun.io)(sun.jnu.encoding,ANSI_X3.4-1968)(file.encoding,ANSI_X3.4-1968)(sun.io.unicode.encoding,UnicodeLittle)
测试了一下是file.encoding
这个系统属性,修改这个属性为UTF-8后确实可以正常的。但是另一个同事登录这台linux后运行却跟我的情况不同,他登录后(使用同一账号)直接jvm的file.encoding
默认就是UTF-8,这看起来有些奇怪。
jvm的系统属性,其实也是根据OS的环境变量得到的。虽然这台机器的LANG
和LC_*
等指定了UTF-8,但并不意味jvm的file.encoding
也一样:
$ localeLANG=en_US.UTF-8LC_CTYPE=UTF-8LC_NUMERIC="en_US.UTF-8"LC_TIME="en_US.UTF-8"LC_COLLATE="en_US.UTF-8"LC_MONETARY="en_US.UTF-8"LC_MESSAGES="en_US.UTF-8"LC_PAPER="en_US.UTF-8"LC_NAME="en_US.UTF-8"LC_ADDRESS="en_US.UTF-8"LC_TELEPHONE="en_US.UTF-8"LC_MEASUREMENT="en_US.UTF-8"LC_IDENTIFICATION="en_US.UTF-8"LC_ALL=
要确认file.encoding
属性的话,可以使用locale charmap
命令,我指定的结果显示:
$ locale charmapANSI_X3.4-1968
ANSI_X3.4-1968
是系统默认的编码,而另一个同事显示的是”UTF-8″,又把怀疑落在ssh客户端上。我是在mac下直接通过ssh命令的方式登录,而他则是在windows下使用putty登录。各自打开ssh的 -verbose参数:
$ ssh -v xxx@ip
他的展示信息里出现了:
debug1: Sending env LC_ALL = en_US.UTF-8 debug1: Sending env LANG = zh_CN.UTF-8
而我的ssh客户端则没有这些信息,确实是ssh客户端配置所引起的差异。(关于LC_*
与LC_ALL
等环境变量的关系后续有精力再整理)
原文地址:一次编码问题的排查, 感谢原作者分享。 刺是与生俱来的,上帝在赐予优越感同时捆-绑的附属品;