一次编码问题的排查

上周遇到的一个问题,代码里有通过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) = c
f: (implicit c: scala.io.Codec)scala.io.Codec

scala> f.name
res6: 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的环境变量得到的。虽然这台机器的LANGLC_*等指定了UTF-8,但并不意味jvm的file.encoding也一样:

$ locale
LANG=en_US.UTF-8
LC_CTYPE=UTF-8
LC_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 charmap
ANSI_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等环境变量的关系后续有精力再整理)

一次编码问题的排查》上有2条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注