天天看点

java和linux的编码

最近要使用中科院计算所的关键词工具NLPIR,用java调用,在windows下测试后放到linux下跑,就发现会有乱码。

windows下默认是GBK,linux下是utf-8,因此在意料之中(尽管最后发现其实不是平台的问题)。

除此之外对于编码问题,一直不清楚,不知道这是工具问题还是平台问题,因此楼主一点一点排查,学到了一些编码的知识,备忘一下~

设置linux下的中文编码包

首先,查看linux设置的默认编码方式,看看是不是因为缺少中文编码包。

[root]# locale

locale是什么? 引自 http://hi.baidu.com/dd_taiyangxue/item/84b85007814bdcd51ff04652

一、locale的五脏六腑

1、 语言符号及其分类(LC_CTYPE)

2、 数字(LC_NUMERIC)

3、 比较和排序习惯(LC_COLLATE)

4、 时间显示格式(LC_TIME)

5、 货币单位(LC_MONETARY)

6、 信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES)

7、 姓名书写方式(LC_NAME)

8、 地址书写方式(LC_ADDRESS)

9、 电话号码书写方式(LC_TELEPHONE)

10、度量衡表达方式(LC_MEASUREMENT)

11、默认纸张尺寸大小(LC_PAPER)

12、对locale自身包含信息的概述(LC_IDENTIFICATION)。

二、理解locale的设置

设定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了简便起见,还有两个变量:LC_ALL和LANG。

它们之间有一个优先级的关系:LC_ALL > LC_* > LANG

可以这么说,LC_ALL是最上级设定或者强制设定,而LANG是默认设定值。

查看当前编码设置,发现当前的设置是en_US.UTF-8。

因此先通过apt-get install locales命令安装locales包。

[root]# apt-get install locales

安装完成locales包后,系统会自动进行locale配置,你只要选择所需的locale,可以多选。最后指定一个系统默认的locale。这样系统就会帮你自动生成相应的locale和配置好系统的locale。但是通过export LC_ALL=zh_CN.UTF-8发现还是报错找不到包。

因此增加新的locale包。

[root]# dpkg-reconfigure locales

ok之后出现选择列表,上下方向键找到zh_CN.UTF-8, zh_CN.GBK等中文编码包,空格选中,回车ok,就开始下载。此时系统中有了中文编码包。

再设置系统locale。

[root]# export LC_ALL=zh_CN.UTF-8

查看locale。

发现当前的locale是zh_CN.UTF-8。系统遇到中文就不会出现乱码了。

在linux下跑NLPIR为什么还是会有乱码呢?只能是使用工具时没有注意编码。

NLPIR的编码设置

NLPIR的编码设置在NLPIR_init里面。有两个版本。

一种只有2个参数:NLPIR_init(bytes[] text,int encoding)。第2个参数为0时表示GBK编码,为1时表示UTF-8编码。

一种有3个参数:NLPIR_init(bytes[] text, int encoding,int liscense)。第3个参数是为了商用我不用管。但是encoding的意义我没弄清出是怎么对映的(文档里有说UTF8_CODE等,但是这个枚举量在java里是失效的),设置成1时好像还是不是UTF-8,我猜这个版本的是不支持UTF-8的。

(希望NLPIR工具的开发者注意要写得一手好文档啊!!!也不写清楚encoding具体的对应关系)

不幸的是,我在linux里面跑的是第二个版本的,因此不清楚这个encoding参数应该怎么设置才是utf-8。怎么办呢?

这时我发现java与平台无关在编码上的体现了。

java编码格式的总结。引自 http://cai555.iteye.com/blog/661191

java中的String永远都是unicode编码的,以它作为中间结果转化成各种不同的编码格式,比如:

        String str = "中文";

        byte[] utf8b = str.getBytes("UTF-8");

        byte[] gbkb = str.getBytes("GBK");

        // 没有乱码

        System.out.println(new String(utf8b, "UTF-8"));

        System.out.println(new String(gbkb, "GBK"));

        // 有乱码

        System.out.println(new String(gbkb, "UTF-8"));

        System.out.println(new String(utf8b, "GBK"));

byte数组utf8b和gbkb是不一样的,是转化成各自编码格式后的二进制数组,而new String(utf8b, "UTF-8")与new String(gbkb, "GBK")是一样的都是以Unicode编码保存。

我们可以来分析一下:

1. 当执行以下时:

Java代码  

java和linux的编码
  1. byte[] utf8b = str.getBytes("UTF-8");  

jvm实际上是做了这样的转化 UNICODE => UTF-8,就是将Jvm内存中的unicode编码二进制码转化成UTF-8格式的二进制码然后赋值给byte[] utf8b 。这个转化的过程我们不用管,jvm会根据一个编码格式对照表来转化。

2. 当执行后面代码:

java和linux的编码
  1. new String(utf8b, "UTF-8")  
实际上第二个参数"UTF-8"告诉jvm:“当前utf8b的编码格式是"UTF-8",你就以这个格式转化成unicode吧!”。也就是将utf8b转化成unicode再存入Jvm的内存,utf8=>unicode。(这个参数应该是为了告诉jvm使用“UTF-8”的编码格式对照表来转化)

因此我用工具处理即便是gb2312,也能通过Java的String的unicode编码进行中间过渡。

byte[] nativeBytes = testNLP.NLPIR_GetKeyWords(fileContent.getBytes("gb2312"), 50, true);
String nativeStr = new String(nativeBytes,0,nativeBytes.length,"GB2312");      

这时nativeStr是unicode编码,输出的时候在不同平台会自动转换。

唉,兜了一个大圈子,不过终于了解编码转换应该怎么处理了。