当前位置: 首页 > Java > 正文

设置LANG即可改变Java默认编码的分析

关键字:
1 星2 星3 星4 星5 星 (2 次投票, 评分: 5.00, 总分: 5)
Loading ... Loading ...
baidu_share

问题:为什么在Linux上设置了LANG变量就可改变Java的默认编码(Charset.defaultCharset)。
我只知道可以做到,但为什么则不太清楚。
同事之所以对这个问题表示有疑问的原因在于,JDK源码中从来没读取过LANG这个变量,Charset.defaultCharset是这么读取的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Charset defaultCharset() {
        if (defaultCharset == null) {
    synchronized (Charset.class) {
java.security.PrivilegedAction pa =
    new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null)
    defaultCharset = cs;
                else 
    defaultCharset = forName("UTF-8");
            }
}
return defaultCharset;
}

按照这段代码,意思为当defaultCharset为null时,从file.encoding属性读取,并返回相应的Charset。
而file.encoding属性的设置,除了直接命令行设置的方法外,JDK中的java_props_md.c中也有一段设置的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Determine the language, country, variant, and encoding from the host,
     * and store these in the user.language, user.country, user.variant and
     * file.encoding system properties. */
    setlocale(LC_ALL, "");
    if (ParseLocale(LC_CTYPE,
                    &(sprops.format_language),
                    &(sprops.format_script),
                    &(sprops.format_country),
                    &(sprops.format_variant),
                    &(sprops.encoding))) {
        ParseLocale(LC_MESSAGES,
                    &(sprops.language),
                    &(sprops.script),
                    &(sprops.country),
                    &(sprops.variant),
                    NULL);
    } else {
        sprops.language = "en";
        sprops.encoding = "ISO8859-1";
    }
    sprops.display_language = sprops.language;
    sprops.display_script = sprops.script;
    sprops.display_country = sprops.country;
    sprops.display_variant = sprops.variant;
    sprops.sun_jnu_encoding = sprops.encoding;

从代码的注释可以看到,这段代码的主要作用是从机器上获取语言、国家、编码的信息,并设置到user.language、user.country、user.variant、file.encoding系统属性中,JDK 6的代码会稍有不同,不过意思基本是一样的。

​ParseLocale主要是读取LC_CTYPE来设置file.encoding,对linux语言设置比较熟的人可能会知道Linux主要是通过LC_ALL、LC_*(12个,包括LC_CTYPE等,其中LC_CTYPE是用来标识语言符号及分类)或LANG来设置编码形式,这三者的优先级为LC_ALL > LC_* > LANG,为什么设置LANG就能改变Java的默认编码,说明LC_ALL和LC_*都没设置,所以只要设置LANG即可影响到LC_CTYPE,LC_ALL/LC_*一般在/etc/sysconfig/i18n中设置,也可在/etc/profile(或{$user.home}/.bash_profile)中设置,可通过locale命令来查看当前机器/用户下的编码,可试试如果设置LANG即可改变其中的LC_CTYPE等,则说明LC_ALL/LC_*都未设置,从这个角度来说,标题中写的改变LANG即可改变Java的默认编码其实是不严谨的,应该加上在Linux机器上,且LC_ALL/LC_*未设置。

鉴于默认编码的不可控因素比较多,在Java中编写涉及编码的代码时,还是强制指定编码是最安全的。

感谢一个同事的反馈,上面这段话是错的,-Dfile.encoding是可以生效的,只是在指定的file.encoding没有对应的Charset实现时,会默认为UTF-8,这里要稍微注意的一点是,file.encoding的指定方法和LANG有些不同,LANG通常的语言大集.小集,例如zh_CN.gbk,而file.encoding就直接是语言的小集,例如gbk。

感兴趣的同学可以用下面这段简单代码加export LANG、-Dfile.encoding来做些试验。

1
2
3
4
public static void main(String[] args) throws Exception{
     System.out.println(Charset.defaultCharset().name());
     System.out.println(System.getProperty("file.encoding"));
}

本文固定链接: http://www.chepoo.com/set-lang-java-default-code-analysis.html | IT技术精华网

设置LANG即可改变Java默认编码的分析:等您坐沙发呢!

发表评论