为神马不要在代码里写中文(二)

在很多个明天之后,我想起来要更新blog了。上次话说到一半,这次回来把结果补上,说的是字符串”a好人”在string literal中的故事。这个字符串有1个洋文字母和2个中文,测试的环境是win7中文版,非unicode的代码页设置的是中文简体,应该是gbk的编码。为了让结果更加整齐一些,我先把出现的结果列一下。win7 x64是little endian的,我直接打印的字符串各个字节,所以,对应的字符编码就自己调顺序吧:

// (1) "a好人"用UTF-8编码为:

61 | e5 a5 bd | e4 ba ba | 00

//(2) "a好人"用GBK编码为:

61 | ba c3 | c8 cb | 00

//(3) "a好人"用UTF-16 LE编码为:

61 00 | 7d 59 | ba 4e | 00 00 

我用’|’分割了一下结果,实际打印结果里是木有那个’|’的。不过还没完,因为编译器不一定能识别出正确的编码,所以会出现一些诡异的结果。编码(1)所列的utf-8字符如果强制被当做gbk来理解,对应的字符串是“a濂戒汉”,也就是1个英文3个汉字。以此字符串为基准我也列一些结果,待会儿填到表格中。

//(4) "a濂戒汉"用GBK编码和(1)结果相同:

61 | e5 a5 bd | e4 ba ba | 00

//(5) "a濂戒汉"用UTF-16 LE编码结果为:

61 00 | c2 6f | 12 62 | 49 6c  | 00 00

再次说明,“a濂戒汉”是utf-8的“a好人”被错误的以GBK编码方式解释而得到的错误字串,本身并没有写在源码中。在我测试的时候,utf-8的源代码文件中字符串就是以(1)那种编码储存的,gbk的源代码文件中字符串就是以(2)那种编码储存的。

下面贴一下结果,输入的源代码文件以gbk,utf-8,和utf-8 with BOM(byte order mark)三种格式储存,分别在不同的编译器下运行上一篇中提到的代码

源文件编码 \ 编译器 gcc4 vc6 vc2003.net vc2005 vc2008 vc2010
UTF-8(char) (1) (1) (1) (1) (1) (1)
GBK(char) (2)* (2) (2) (2) (2) (2)
UTF-8 BOM(char) N/A N/A (1) (2) (2) (2)
UTF-8(wchar_t) (3) (5) (5) (5) (5) (5)
GBK(wchar_t) (3)* (3) (3) (3) (3) (3)
UTF-8 BOM(wchar_t) N/A N/A (3) (3) (3) (3)

*gcc4使用GBK编码的源文件时,加了编译参数-finput-charset=gbk。

这个表格给出的结果很有趣。首先说说char string literal,几乎所有的编译器都得到了和源文件编码一致的结果……但这里面隐含着一个巧合,那就是对utf-8的处理。对于gcc来说,utf-8是默认的编码格式,但对于微软的编译器来说,当前codepage,在我这里也就是gbk,才是默认的编码格式。所以,UTF-8(char)的结果在所有编译器中都一致不过是巧合而已,只要看看下一行UTF-8 BOM(char)的结果就明白了。

vc的编译器据说是自动识别文件编码的,但对于90%都是英文字符的源代码,utf-8和gbk几乎无法自动区分开来,所以加了BOM在文件头才可以让vc识别出真正的编码。不过加了BOM的源代码gcc是不认识的,而vc自己也直到vc2003才开始支持。加上BOM之后,vc才可以真正理解utf-8格式的源代码,这时vc2003和gcc依旧保持一致,仍把char string literal直接编译成utf-8,但是!!但是!!坑爹的事情从vc2005开始出现,此版本之后vc会强制对char string literal进行转码,它会强制转换成当前的codepage,也就是gbk!

可以这么理解,对于char string literal,自vc2005开始,无论源文件是什么格式,它统统会转码到当前的codepage。而gcc,vc6和vc2003则是和源码文件中的编码保持一致——虽然实际上vc2003和vc6并没有真的理解不带BOM的utf-8文件,它们其实是当gbk的编码来理解的,往后看wchar_t的转码结果就能明白。

上面啰嗦了半天,结论就是,假如你在程序中写了中文,例如这样:

const char* p = "五道杠"; 

vc2005之后编译的结果将和当前codepage挂钩……而且将和gcc或vc2003的编译结果不一致……想搞跨平台?还是洗洗睡吧。

再来看wchar_t string literal,如果理解了char身上发生的事情,对照来看表格中wchar_t的结果倒是相当一致,大家都对源文件中的字串进行了转码(注意我没有提供任何UTF16格式的源文件),最终结果都是UTF16-LE编码。遗憾的是,在没有BOM的情况下,vc的编译器不能正确识别utf-8,所以它把“a好人”当成了“a濂戒汉”,由这4个字转换成UTF16-LE之后就变出了结果(5)

说到这里,可以看出wchar_t string literal也是个坑,更不要提ndk中的gcc是以4字节为1个wchar_t的长度,或许是按UTF32来进行编码的?以后有机会再试验吧。

最后总结一下,如果有潜在的跨平台需求,那是无论如何不能在代码中写ASCII以外的字符的。除非显式的使用0x1234, “\u1234″这种方式。另外,使用平台无关的字符串处理函数库代替wchar_t系的标准库函数也很有必要,例如ICU神马的,星际2里面就用的是它。对于vc这一系的编译器,其string literal的编译结果不仅和编译器版本相关,而且和编译器运行环境的codepage相关,着实是在给广大程序员添堵……

好长~以上= =b

为神马不要在代码里写中文(一)

c++中的字符串是个很麻烦的东西,有了宽字符之后变得更加麻烦,据说c++0x要引入raw string,就是类似这种

const char* str = R"噼里

回个车

啪啦";

的奔放写法,所以未来估计会更加的乱。最近看了一些关于字符串编码的东西,才明白双引号之间的这些玩意儿还是有很多说道的,接下来详细记录一下,要是说错了,那就日后发现再改= =b

双引号搞出来的这种字符串称为string literal,相当于一个const char*或者const wchar_t*。vc的编译器(估计大部分都有)有个优化,叫string pool,是说编译的时候所有相同的字符串会被合并,只保留一份在内存中,因此相同的string literal会返回相同的指针。

但是只要有字符就会牵扯到字符编码,因为程序只存取byte,写在引号之间的字符对应哪些byte呢?另外,代码文件本身就是一个文本文件,也存在编码的问题,这二者之间的关系又是如何呢?请不要走开,我们广告之后再进行讨论……

跨平台移植刻不容缓……程序中惊现神秘字串……究竟是程序员脑残还是编译器脑残……答案即将揭晓,请看走进科学之《不要在代码中写中文》……

嗯嗯,广告完毕,答案是~~~~~~这得看用的什么编译器了╮(╯_╰)╭

我写了段程序,用于打印string literal的各个byte:

#include <iostream>
using namespace std;

void outbyte(const void* buf, int len)
{
	unsigned char* p = (unsigned char*)buf;
	cout<<"Len:"<<len<<endl;
	while(len--)
	{
		cout<<hex<<(int)(*p++)<<" ";
	}
	cout<<endl;
}

int main(int argc, char** argv)
{
	const char* p = "a好人";
	outbyte(p, sizeof("a好人"));

	const wchar_t* pw = L"a好人";
	outbyte(pw, sizeof(L"a好人"));

	return 0;
}

代码分别保存成不同的编码格式,有utf-8, 以BOM(byte order mark)开头的utf-8,还有gbk。然后用gcc4,vc6,vc2003,vc2005以及vc2008编译并运行,得到了很有趣(蛋疼?)的结果。

嗯嗯,结果和分析就明天再贴了= =b

Xoom体验

从永高那边借到Xoom一台,在五一假期体验了一把,趁着还没忘记记录几笔。硬件不提了,只谈软件。我先说优点吧,优点就是……挺新奇的……ORZ

这个Honeycomb的UI flow有很大不同,界面上增加了各种特效,android默认的几个应用都有为平板设计的新UI。但我感觉都是花哨的东西,没有什么实际的亮点。

说到亮点,好吧,桌面上的小部件确实丰富了许多。不过我还是觉得需要手动把应用拖放到桌面上这个设定很鸡肋,删除倒是终于可以在my apps里面进行了,不用费劲的跑到setting中搞来搞去。

还有个亮点就是对于sdcard目录的操作,在接驳电脑之后平板和电脑可以同时访问,不必像原来那样还得先mount然后再unmount神马的。

其他的亮点么……嗯……

我还是吐槽吧,实在忍不住了。首先说说流畅度的问题,Xoom的桌面流畅度确实不错,但仅限于“横向正放”的情况。竖起来,或者反过来横放都会导致桌面滑动变得有点卡,不是卡到受不了,但是绝对可以察觉。这真是个坑爹的Bug,这么高的配置了连个桌面都做不好。

第二是桌面下边任务栏不可隐藏,“Home,Back”两个键就放在这里。多了一个用来显示最近开过的应用缩略图的键,Menu键只有在需要的时候才会显示,个人猜测是app注册了option menu项目才会显示。感觉这一条就能坑死一茬app,原来不是说“Menu,Search”是必备的按键么?

Back键放在屏幕最下方实在很难按到,很多应用不提供Back按钮只好用这个键,平板太大所以手指移动距离变得非常长。这一点iPad不错,逼迫应用自己搞一个什么back按钮到屏幕上。而android 3.0居然不思悔改还沿用了手机的设定,唉……

Market上没有专门对应平板的分类,下载应用的时候只能赌。要是自适应屏幕的还好,不能自适应的就显示在桌面中间, 也没有类似iPad放大的功能,很多手机应用就悲剧了。不过还好也有不少应用写出来就是自适应屏幕的,不必像app store里面那样搞个HD版出来骗钱。

浏览器很不好用,因为本身就16:9很扁了,任务栏又白白占了一行,这个浏览器的Tab又占去一行,地址栏再占去一行。好在地址栏在拖动的时候可以隐藏掉,但Tab是隐藏不了的,很是浪费空间。并且Tab的关闭按钮和”+”离得太近,有时候想新增Tab结果把旧的关闭了……至于浏览器突然自动关闭的事情就更不用说了……

Xoom最坑爹的地方就是电源接口居然不是USB,你妹啊又用你自己的接口,moto啊我本想说这一切都是软件的问题但这个接口绝对是moto你自己搞的啊!!

我本来是对Xoom和Android 3.0抱有很大期待的,若不是它太贵我就第一时间入手了。现在看来还是不入为好,至少得等下一个版本出来之后再考虑。感觉Android每一个整数版本都很悲剧,1.0,2.0,3.0都是过渡的,不出意外的话,3.1很快就会出现,我就继续期待吧……