Java中的String

Java是完全支持utf-8的。但是,新手在使用Java的时候却总是遇到各种各样的乱码问题。其实根源只有一个,byte[]到char[]的转换和char[]到byte[]的转换。

下文中所有的“字符串”均代表逻辑意义上的字符串,而非String类或String类的实例。

char类型占用两个字节,永远用utf-8编码来表示一个字符,而byte类型则对应代表一个字节。因此,仅仅一个byte[]是无法代表一个字符串的,因为我们还不知道它的编码方式。有可能是utf-8,也有可能是gb2312、iso-8859-1。

在任何数据传输中,都是对应于byte进行的,包括网络IO、文件IO、控制台IO等等。此时,所有的字符串都得用某种编码方式用byte[]表示出来。对于String类来说,就是它的方法getBytes(charset)。调用这个方法,就完成了从char[]到byte[]的转换。

如何完成byte[]到char[]的转换呢?根据上文我们可以知道,这需要明确的知道byte[]是用什么编码来表示这个字符串。然后,使用String(bytes, charset)的构造器,它会根据你所指明的编码方式,把byte[]转换成char[]。

看起来很简单,但实际情况比这个复杂一些。

第一,IO的时候通常只有一端在你的程序中,另外一端,完全可能是C代码,例如MySQL。这时的问题就集中在弄清对方使用的编码方式,并在自己的程序中使用正确的构造器和getBytes(charset)方法。

第二,有时候我们借用一些库来进行IO,或者IO是某个框架的一部分。由于老外的失误,没有考虑编码的问题。因此,它给你的字符串是用操作系统默认的字符集来解释的。此时,我们可以按这个错误的字符集重新调用一次getBytes(charset),把它还原为byte[],再调用正确的String(bytes, charset)构造器。

第三,其实最麻烦的是控制台IO。如果控制台的编码方式不对的话,很难在屏幕上显示出中文来,因为System.out是用native方法构建的。给Java虚拟机设置一些启动参数可以解决这个问题。

最后说一下OutputStream和Writer的关系。OutputStream比Writer底层一点,因为前面也说了,所有的IO最终都要转换到byte[]。Writer的操作对象是char、char[]、String等,他们都会完成一个类似于String到byte[]的转换,并把转换结果发送给OutputStream1。使用中,为了避免编码出现问题,不应当使用FileWriter类。而是FileOutputStream + OutputStreamWriter + PrintWriter。这是因为OutputStreamWriter类的构造器是可以带charset参数的。所有可以直接用OutputStream直接构造的其它Writer也应该在中间塞一层OutputStreamWriter并指明编码集。这样,utf-8的String可以转换到任何编码集输出。同理,InputStream和Reader也是如此。

注1:这并非必然的,毕竟OutputStream和Writer只是两个接口,具体怎么实现还是要看自己。要是哪天ISO规范了字符串的传输标准,或者有个诡异的硬件可以帮助Java直接传输字符串,到时候Writer和OutputStream也脱钩了。不过,在这之前,它们在逻辑上是紧紧关联的,如果你负责这些库的编码的话,体现这种现实关系自然是最好的。

暂时就这么多。thanks for reading.

on August 25th, 2008 | No Comments »

何可欣的年龄

其实,可爱的何可欣小朋友的生日应该是1992年2月29日。

她在接受CNN采访时说:“今年2月份,我刚刚度过我的第4个,而不是14个生日。”

on August 21st, 2008 | No Comments »

韩国,射箭

韩国人的射箭完全是妖怪,而这位叫林东贤(IM Dong-Hyun)的设计选手绝对是妖怪中的妖怪。根据韩国射箭协会的资料,他是一位法律意义上的盲人,只能看到不超过6米远的距离。而他,已经获得了两枚奥运金牌、三枚世锦赛金牌以及两枚世界杯金牌,并保持着这个项目的世界记录。

on August 14th, 2008 | No Comments »

庞大的wsdl

今天在研究web service。手头有个wsdl文件里面有好多儿子import,然后还有好多孙子import,还有曾孙import……

用Axis2的wsdl2java把它转换出来,产生的Stub源文件有431,012行,一共21,741,041字节。

其结果就是:

  1. Eclipse在挣扎了五分钟之后死掉;
  2. Vim占用了超过90秒CPU使用时间来给它进行语法高亮。

这么大的源文件还是第一次看到。如果你对姚明级别的东西感兴趣,还可以看看这个

on August 13th, 2008 | No Comments »

奇怪的gcc行为

有人报告了gcc一个奇怪的行为,这样的一段代码居然能够通过编译。

#include <cstdio>

#define NUM  getnum()
int getnum()
{
    int x = 0;
    scanf("%d", &x);
    printf("%d\n", x);
    return x;
}

int main()
{
    int array[NUM];
    printf("array size =%d\n",sizeof(array));
    return 0;
}

要命的是,编译之后居然还能运行。sizeof可以是运行期间确定的吗?

为了确定问题所在,我用-E选项对源代码进行预处理展开,对展开的代码再重新进行编译,居然还是通过了。这意味着问题根本不是由于那个宏引起的。于是,这样的代码也就通过了编译。

#include <cstdio>

int getnum()
{
    int x = 0;
    scanf("%d", &x);
    printf("%d\n", x);
    return x;
}

int main()
{
    int array[getnum()];
    printf("array size =%d\n",sizeof(array));
    return 0;
}

本来sizeof应该是编译期间常数,居然现在连运行期间常数都不是了。不知道gcc做了什么样的优化导致了这个问题。如果再把template参数掺和进来,岂不是会更有意思?


有地方说在C99以后,数组有两种定义:一种是大小是编译期间常数的,另外一种则不是。区别在于,前者分配在stack上,后者分配在heap上。并且在两者上执行sizeof也有不同的效果。

on August 13th, 2008 | No Comments »

Robber or not?

这样一则新闻,大概说的是某人在大街上抢劫,结果发现对方只有1.5元钱,于是放弃。此人被抓获后,法官认为此人属于犯罪中止,又没有造成损害,应当免予处罚。

大一的时候选修刑法课,老师说了这样一个案例:有天夜里,某人潜入学校劫走一女生。到了僻静处,正欲上时,才发现此人居然有小JJ,于是放弃。

这种情况其实属于“不能犯”的概念。不能犯有很多分类,这里这种叫做“对象不能犯”。还有一种,比如买毒药杀人,结果毒药过期实效,那叫做“手段不能犯”或者“工具不能犯”。“不能犯”都有一个特点,它和犯罪中止态里嫌疑人出于主观原因放弃犯罪不同,它是因为客观原因导致嫌疑人不得不放弃犯罪。因此,“不能犯”是犯罪未遂的一种。

这样看起来,我认为本案中的犯罪嫌疑人更适合判为抢劫未遂。

on August 8th, 2008 | 2 Comments »

小猫从军记

家里的小猫叫皮蛋,名字没取好,果然是又皮又捣蛋。

今天上午--事后猜测是上午,不知道具体时间--皮蛋偷偷跑出去溜达了一圈,回来之后下巴出现了一个约一平方厘米的伤口,上面的皮都掉了,露出肉来。也不知道它是和人打了一架还是摔了一个狠的。回来的时候全身都是灰,给它洗澡得时候才发现那个伤口。

现在的问题是:它不停的反抗,不让我用酒精给它擦拭。靠近它就会大声的尖叫。看来只好等它身上干了再带去宠物医院看看。认真研究了下伤口,好像是嘴唇那里破了,下巴上的皮就朝脖子缩了一点,看样子很难长还原了,不知道猫猫能不能缝针。

on August 6th, 2008 | 2 Comments »

Javascript实现的Vi

今天发现了个有意思的东西,用Javascript实现的Vi。Vi的大部分常用功能都工作得很好,包括Vim的visual模式。有一小部分由于Javascript的局限性很难完成,比如说u。值得注意的是,这个项目还是GPL的。[原始链接|本站镜像|打包下载]

on August 4th, 2008 | No Comments »

牛博网对密码的特殊嗜好

牛博网果然很牛。今日在牛博网注册帐号的时候居然被提示“密码必须包含以下非字母数字字符: 0”。不少网站出于安全考虑要求密码强度达到一定标准,但是像牛博网这样公然要求某个特定字符出现的,这还是第一次看到。

on August 3rd, 2008 | No Comments »

校内的隐私策略做得确实不好

校内网前段时间开放了api,一瞬间各种应用全冒了出来,这两天试了试水,太清,隐私一览无余。

第一,回答问卷在勾取匿名提交的情况下还是会出现一条某某某完成了某问卷的新鲜事,让我无法理解。

第二,了若指掌应用中,根本就没办法避免让别人看到你选择的答案。在我看来,这就算不是一个默认选项,好歹也得是一个选项啊。

第三,我已经不敢再试了,甚至一度有过删号走人的打算。现在老老实实的和女朋友玩好友买卖,其他人买我一律拉黑。

on August 1st, 2008 | 2 Comments »