免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 11074 | 回复: 22
打印 上一主题 下一主题

C和汉字编码(两章,欢迎高人指正) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-09-20 11:40 |只看该作者 |倒序浏览
本帖最后由 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也是类似情况
  1. #include <stdio.h>
  2. #include <locale.h>
  3. #include <wchar.h>

  4. void print_wc_val(wchar_t c)
  5. {
  6.     char *p = (char*)&c;
  7.     wprintf(L"%x %x %x %x \n",p[0],p[1],p[2],p[3]);
  8. }

  9. int main()
  10. {
  11.     int i;
  12.     char *code;
  13.     FILE *fp,*fp2;

  14.     code = setlocale(LC_ALL,"zh_CN.utf8");//zh_CN.gb2312 //zh_CN.utf8
  15.     wprintf(L"%s\n",code);

  16.     wchar_t s[100];
  17.     fp = fopen("1.txt","r");
  18.     fgetws(&s,100,fp);
  19.     wprintf(L"%ls\n",&s);

  20.     for(i=0;i<wcslen(s);i++)
  21.     {
  22.         print_wc_val(s[i]);
  23.     }
  24.    
  25.     fp2 = fopen("r.txt","w");
  26.     fwprintf(fp2,L"%ls\n",s);
  27.     fclose(fp2);
  28.     return 0;
  29. }
复制代码

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
2 [报告]
发表于 2011-09-20 11:51 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
3 [报告]
发表于 2011-09-20 12:01 |只看该作者
这个说法成立吗?
GB2312里有ASCII码么?
pmerofc 发表于 2011-09-20 11:51



起码GB2312里英文字符和数字的值和ASCII码值一样,所以我就这么说了

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
4 [报告]
发表于 2011-09-20 12:16 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
5 [报告]
发表于 2011-09-20 12:19 |只看该作者
回复  iCoding


    不是吧,GB里的字符是全角的
pmerofc 发表于 2011-09-20 12:16



    全角和非全角不是同一个字符吧

论坛徽章:
0
6 [报告]
发表于 2011-09-20 12:26 |只看该作者
GB码表里面有半角字母和标点。

论坛徽章:
5
技术图书徽章
日期:2013-11-07 13:21:58技术图书徽章
日期:2013-12-07 10:34:46技术图书徽章
日期:2014-04-23 08:50:31双鱼座
日期:2014-09-16 09:12:34亥猪
日期:2015-01-23 13:37:49
7 [报告]
发表于 2011-09-20 12:34 |只看该作者
windows和linux内部都是以unicode存储字符,其他编码输入时都要转成unicode,不过unicode里的术语也不是一般的

linux内部?啥叫linux内部?kernel?应用的底层库? Windows据说是,但linux不是这样,最终要转成unicode不假,但是linux默认使用的是utf-8,不是直接的unicdoe,只是从字库中查找字型的时候在转成unicode。

但是他们是可以转换的
没找到太多的资料(或者有,我没看进去,那么效果有没有也差不多)但是看起来应该是没有问题,怎么转换一定还是立足于某个规律,而且这个规律是稳定的,就像数学归纳法一样
第i个GB字符 可以转为 UNICODE同样的字符
第i+1个字符 也可以
第i+j个可以

这个真的没有规律,象iconv,icu之类的程序都是查表。一张很大的GB和unicode的对应表。

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
8 [报告]
发表于 2011-09-20 12:38 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
2
程序设计版块每日发帖之星
日期:2015-06-17 22:20:00每日论坛发贴之星
日期:2015-06-17 22:20:00
9 [报告]
发表于 2011-09-20 12:39 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
10 [报告]
发表于 2011-09-20 12:57 |只看该作者
linux内部?啥叫linux内部?kernel?应用的底层库? Windows据说是,但linux不是这样,最终要转成unicode ...
nketc 发表于 2011-09-20 12:34



这个说法不准,不过我的测试程序读取gb2312编码的文件字符串到wchar_t数组时,去比过里面内容,就已经是unicode了,内容和UCS4编码的文件相似
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP