- 论坛徽章:
- 0
|
本帖最后由 iCoding 于 2011-09-20 12:42 编辑
汉字的编码分为三类:中国的GB系列,台湾的big5码,unicode。
1.GB
先说GB系,最早的编码是GB2312,是中国的国家汉字编码标准,GB2312可以兼容ASCII码,ASCII码字符在GB2312里仍然是一个字节,而汉字字符是两个字节,那么他们怎么区分呢?因为标准的ASCII码包含128个字符,字符值最高是127(0111 1111)因此字节最高位为0,而GB利用了这一个特点,将表示汉字的两个字节最高为置1,那么当程序读取GB2312编码的文件时,字节最高位为0的字节就是ASCII码字符,字节最高位为1并且下一个字节最高位也是1则这两个字节定义了一个汉字字符(如果不是这样的,那么就说明文件被破坏了,无法正常读取)这种方法虽然很好的解决了和ASCII码兼容的问题,但也有一个缺陷,因为两个字节的高位已经用做标识符,能表示的汉字字符个数也减少了(仅能表示2^14=16384个字符)所以在GB2312里只收录了7000多个常用汉字字符,之后的GBK等后续发布的编码进一步扩充了收录的字符个数(我看到的资料上这么说,不过这里有一个问题?扩充字符个数的难度在于要扩大字节数,那么GBK还是两个字节的大小吗?)
2.BIG5
我的理解里台湾的big5码和咱们的gb2312是一套类似的方案
3.UNICODE
说了半天,我们的主角UNICODE终于要出场了,说他是主角原因有两点:1)UNICODE的设计目标是覆盖这个星球上所有的语言,使用他理论上可以在任何一台支持UNICODE的机器上正常显示 2)UNICODE已经成为了标准了吧,windows和linux内部都是以unicode存储字符,其他编码输入时都要转成unicode,不过unicode里的术语也不是一般的多,UCS2,UCS4,UTF16,UTF16BE,UTF16LE,UTF32,UTF8,UTF7,说实话我看这么多东西的时候已经晕了,一个一个过吧
UCS2,UCS4 UCS是Universal Character Set的缩写
说的是表示一个汉字字符的字节个数,UCS2是两个字节,UCS4是4个字节,UNICODE不像GB系用一个字节表示ASCII码,因此即使是英文字母再UNICODE里也存储为2个字节,好处是就不许要浪费位来区分汉字和英文字母了,因此UCS可以表示2^16=65536字符,这比GB2312能表示的字符个数强太多了,不过貌似搞UNICODE的那帮人觉得6万多个字符还是不够,所以又搞了一个4字节的版本,叫UCS4,那么现在可以表示的字符是 2^32=4294967296(等一下,我脑子笨,让我数一下先,是42亿,囧,这下怎么都该够了吧)关于这点,我有个很好的例子可以说明一下,C标准库有一个头文件wchar.h,里面定义了一个类型是wchar_t,如果我们sizeof一下的话,发现在windows平台上是2个字节,而 linux上是4个字节,因此说明 windows平台上玩的是UCS2,而linux则直接上了UCS4,大家不妨玩一下,应该可以应证我说的
好了,那么UCS2和UCS4之间怎么转换?看起来UCS4是兼容UCS2的,因为同一个字符UCS4的2个低字节内容就是UCS2两个字节里的值,而2个高字节里全是0,因此UCS4去掉两个高字节就是UCS2编码了
实验:
在linux上建立一个文件,用gedit打开,输入内容,选择菜单文件/另存为,选择编码类型(可以增加删除UCS2和UCS4编码)分别存成UCS2和UCS4,打开shell窗口输入od -h ucs2.txt 和 od -h ucs4.txt 察看字节编码
注意UCS2和UCS4的文件都有一个数据结束符(UCS2是000A UCS4是0000 000A)
UTF16和UTF32又是啥?UTF其实是unicode transfer format就是对UCS2,UCS4进行编码然后用于传输的格式
UTF16 简单的对UCS2进行了一下简单处理,例如在数据前添加FF FE两个字节表示这是UTF16并且它是小尾的(FE FF是大尾,事实上仅仅是UTF16 还要多种处理方式,小尾是UTF-16LE,大尾是UTF-16BE, FF FE称为BOM),同样UTF32对应的是UCS4,但是显然这样的传输格式其实并不环保,写过网络通信的人都知道其实我们要降低数据传输量,因此UTF8就出来了,UTF8是一种变长编码,范围是1~6个字节,具体怎么样,暂时不描述,等将来我自己搞的比较明白还有点时间再说,UTF7还没看,不知道是啥意思
GB和unicode之间的转换?
GB和unicode 应该是没什么关系的两种版本,同一个汉字再两个编码里的值绝对不一样,但是他们是可以转换的
没找到太多的资料(或者有,我没看进去,那么效果有没有也差不多)但是看起来应该是没有问题,怎么转换一定还是立足于某个规律,而且这个规律是稳定的,就像数学归纳法一样
第i个GB字符 可以转为 UNICODE同样的字符
第i+1个字符 也可以
第i+j个可以
那么就是可以的
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
概述
计算机原理里关于汉字处理是这么描述的:计算机通过输入设备读入汉字(输入码)经过转换程序,转换为计算机内码,再经过字模库(字型码),输出到输出设备,个人理解所谓的内码就是linux选择的unicode,其他类型在存入数据时都要转成UNOCODE
为什么要设置locale?
个人认为locale.h是C语言标准库里比较晦涩的库之一(另一个是signal.h)这个库里定义了一个setlocale函数,该函数可以指定地区和编码,那么为什么要指定编码呢?我们知道在linux上我们可以再shell里通过命令运行程序,shell的编码设定其实就是C程序的环境变量,调用setlocale其实是对程序运行环境的编码做一个假设,例如将编码设定为GB2312,使用C语言标准库的fgetws等宽字符版本的函数去读写文件时,会将文件的编码设定为GB2312,尝试从GB2312转换成为UNICODE(windows上是UCS2,Linux上是UCS4)输出时再次尝试从UNICODE转换成为GB2312(注意:这种设动并不会真正影响到环境的编码设定)一旦读取的程序不是GB2312的,又或者shell不是GB2312而是unicode,那么wprintf就会输出乱码
char *setlocale(int category, const char *locale);
疑问:category在相关的书籍上都可以查阅到,但是我们怎么知道到底都有那些locale可以用呢?在linux上可以使用locale,locale -m命令查看系统正在使用的编码,支持的编码都有什么,一般来说汉字编码可以设定为zh_CN.gb2312,zh_CN.utf8它的规则是<语言>_<地区>.<字符集编码>,因此可以这样调用:
char *p = setlocale(LC_ALL,"zh_CN.utf8");
assert(p!=NULL);
为什么不设定loacle,printf("中文")也能正常输出
我们说了中文OS下的文件有多种编码格式,源文件也不例外,linux下文件(包括源文件)都被存为utf8,因此源文件中的汉字其实是若干个unicode字节,在编译时,这几个字节原封不动的进入编译后的指令,执行时相当于直接放到变量里去,输出时,因为shell默认也是utf8,数据和环境编码一致,因此可以正常输出,不仅如此,如果,从文件里读取到的文本编码也是utf8那么这些字符串printf的时候也可以正餐输出,windows也是类似情况- #include <stdio.h>
- #include <locale.h>
- #include <wchar.h>
- void print_wc_val(wchar_t c)
- {
- char *p = (char*)&c;
- wprintf(L"%x %x %x %x \n",p[0],p[1],p[2],p[3]);
- }
- int main()
- {
- int i;
- char *code;
- FILE *fp,*fp2;
- code = setlocale(LC_ALL,"zh_CN.utf8");//zh_CN.gb2312 //zh_CN.utf8
- wprintf(L"%s\n",code);
- wchar_t s[100];
- fp = fopen("1.txt","r");
- fgetws(&s,100,fp);
- wprintf(L"%ls\n",&s);
- for(i=0;i<wcslen(s);i++)
- {
- print_wc_val(s[i]);
- }
-
- fp2 = fopen("r.txt","w");
- fwprintf(fp2,L"%ls\n",s);
- fclose(fp2);
- return 0;
- }
复制代码 |
|