Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2027303
  • 博文数量: 413
  • 博客积分: 10926
  • 博客等级: 上将
  • 技术积分: 3862
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-09 18:14
文章分类

全部博文(413)

文章存档

2015年(5)

2014年(1)

2013年(5)

2012年(6)

2011年(138)

2010年(85)

2009年(42)

2008年(46)

2007年(26)

2006年(59)

分类: LINUX

2010-03-08 13:41:43

Part 1
一直在编码方面要求不是很高,所以对Unicode和UTF-8也不甚了解。
  最近偶然翻到一篇UTF-8的文章,感觉解释的非常繁杂,因此才想到重新写一篇简单易懂一点的。


  首先说明一下现在常用的一些编码方案:
  1、在中国,大陆最常用的就是GBK18030编码,除此之外还有GBK,GB2312,这几个编码的关系是这样的。
最早制定的汉字编码是GB2312,包括6763个汉字和682个其它符号
95年重新修订了编码,命名GBK1.0,共收录了21886个符号。
之后又推出了GBK18030编码,共收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字,现在WINDOWS平台必需要支持GBK18030编码。

按照GBK18030、GBK、GB2312的顺序,3种编码是向下兼容,同一个汉字在三个编码方案中是相同的编码。

  2、台湾,香港等地使用的是BIG5编码
  3、日本:SJIS编码

  如果把各种文字编码形容为各地的方言,那么Unicode就是世界各国合作开发的一种语言。
  在这种语言环境下,不会再有语言的编码冲突,在同屏下,可以显示任何语言的内容,这就是Unicode的最大好处。

  那么Unicode是如何编码的呢?其实非常简单。
  就是将世界上所有的文字用2个字节统一进行编码。可能你会问,2个字节最多能够表示65536个编码,够用吗?
  韩国和日本的大部分汉字都是从中国传播过去的,字型是完全一样的。
  比如:“文”字,GBK和SJIS中都是同一个汉字,只是编码不同而已。
  那样,像这样统一编码,2个字节就已经足够容纳世界上所有的语言的大部分文字了。

  Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。
  现在用的是UCS-2,即2个字节编码,而UCS-4是为了防止将来2个字节不够用才开发的。UCS-2也称为基本多文种平面。
  UCS-2转换到UCS-4只是简单的在前面加2个字节0。
  UCS-4则主要用于保存辅助平面,例如Unicode 4.0中的第二辅助平面
   20000-20FFF - 21000-21FFF - 22000-22FFF - 23000-23FFF - 24000-24FFF - 25000-25FFF -   26000-26FFF - 27000-27FFF - 28000-28FFF - 29000-29FFF - 2A000-2AFFF - 2F000-2FFFF
  总共增加了16个辅助平面,由原先的65536个编码扩展至将近100万编码。

  那么既然统一了编码,如何兼容原先各国的文字编码呢?
  这个时候就需要codepage了。
  什么是codepage?codepage就是各国的文字编码和Unicode之间的映射表。
  比如简体中文和Unicode的映射表就是CP936,点这里查看官方的映射表。

  以下是几个常用的codepage,相应的修改上面的地址的数字即可。
codepage=936 简体中文GBK
codepage=950 繁体中文BIG5
codepage=437 美国/加拿大英语
codepage=932 日文
codepage=949 韩文
codepage=866 俄文
codepage=65001 unicode UFT-8

最后一个65001,据个人理解,应该只是一个虚拟的映射表,实际只是一个算法而已。

从936中随意取一行,例如:
0x9993 0x6ABD #CJK UNIFIED IDEOGRAPH
前面的编码是GBK的编码,后面的是Unicode。
通过查这张表,就能简单的实现GBK和Unicode之间的转换。



  现在明白了Unicode,那么UTF-8又是什么呢?又为什么会出现UTF-8呢?

  ASCII转换成UCS-2,只是在编码前插入一个0x0。用这些编码,会包括一些控制符,比如 '' 或 '/',这在UNIX和一些C函数中,将会产生严重错误。因此可以肯定,UCS-2不适合作为Unicode的外部编码。

  因此,才诞生了UTF-8。那么UTF-8是如何编码的?又是如何解决UCS-2的问题呢?

例:
E4 BD A0        11100100 10111101 10100000
这是“你”字的UTF-8编码
4F 60          01001111 01100000
这是“你”的Unicode编码

按照UTF-8的编码规则,分解如下:xxxx0100 xx111101 xx100000
把除了x之外的数字拼接在一起,就变成“你”的Unicode编码了。
注意UTF-8的最前面3个1,表示整个UTF-8串是由3个字节构成的。
经过UTF-8编码之后,再也不会出现敏感字符了,因为最高位始终为1。

以下是Unicode和UTF-8之间的转换关系表:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

Unicode编码转换到UTF-8,简单的把Unicode字节流套到x中就变成UTF-8了。



Part 2

Codepage简介

1. Codepage的定义和历史
字符内码(charcter code)指的是用来代表字符的内码.读者在输入和存储文档时都要使用内码,内码分为

单字节内码 -- Single-Byte character sets (SBCS),可以支持256个字符编码.  
双字节内码 -- Double-Byte character sets)(DBCS),可以支持65000个字符编码.主要用来对大字符集的东方文字进行编码.  
codepage 指的是一个经过挑选的以特定顺序排列的字符内码列表,对于早期的单字节内码的语种,codepage中的内码顺序使得系统可以按照此列表来根据键盘的输入 值给出一个对应的内码.对于双字节内码,则给出的是MultiByte到Unicode的对应表,这样就可以把以Unicode形式存放的字符转化为相应 的字符内码,或者反之,在Linux核心中对应的函数就是utf8_mbtowc和utf8_wctomb.  
在1980年前,仍然没有任何国际标准如ISO-8859或Unicode来定义如何扩展US-ASCII编码以便非英语国家的用户使用.很多IT 厂商发明了他们自己的编码,并且使用了难以记忆的数目来标识:

例如936代表简体中文. 950代表繁体中文.

1.1 CJK Codepage
同 Extended Unix Coding ( EUC )编码大不一样的是,下面所有的远东 codepage 都利用了C1控制码 { =80..=9F } 做为首字节, 使用ASCII值 { =40..=7E { 做为第二字节,这样才能包含多达数万个双字节字符,这表明在这种编码之中小于3F的ASCII值不一定代表ASCII字符.

CP932

Shift -JIS包含日本语 charset JIS X 0201 (每个字符一个字节) 和 JIS X 0208 (每个字符两个字节),所以 JIS X 0201平假名包含一个字节半宽的字符,其剩馀的60个字节被用做7076个汉字以及648个其他全宽字符的首字节.同EUC-JP编码区别的是, Shift-JIS没有包含JIS X 202中定义的5802个汉字.

CP936

GBK 扩展了 EUC-CN 编码( GB 2312-80编码,包含 6763 个汉字)到Unicode (GB13000.1-93)中定义的20902个汉字,中国大陆使用的是简体中文zh_CN.

CP949

UnifiedHangul (UHC) 是韩文 EUC-KR 编码(KS C 5601-1992 编码,包括2350 韩文音节和 4888 个汉字a)的超集,包含 8822个附加的韩文音节( 在C1中 )

CP950

是代替EUC-TW (CNS 11643-1992)的 Big5 编码(13072 繁体 zh_TW 中文字) 繁体中文,这些定义都在Ken Lunde的 CJK.INF中或者 Unicode 编码表中找到.

注意: Microsoft采用以上四种Codepage,因此要访问Microsoft的文件系统时必需采用上面的Codepage .

1.2 IBM的远东语言Codepage
IBM的Codepage分为SBCS和DBCS两种:

IBM SBCS Codepage

37 (英文) *  
290 (日文) *  
833 (韩文) *  
836 (简体中文) *  
891 (韩文)  
897 (日文)  
903 (简体中文)  
904 (繁体中文)  
IBM DBCS Codepage

300 (日文) *  
301 (日文)  
834 (韩文) *  
835 (繁体中文) *  
837 (简体中文) *  
926 (韩文)  
927 (繁体中文)  
928 (简体中文)  
将SBCS的Codepage和DBCS的Codepage混合起来就成为: IBM MBCS Codepage

930 (日文) (Codepage 300 加 290) *  
932 (日文) (Codepage 301 加 897)  
933 (韩文) (Codepage 834 加 833) *  
934 (韩文) (Codepage 926 加 891)  
938 (繁体中文) (Codepage 927 加 904)  
936 (简体中文) (Codepage 928 加 903)  
5031 (简体中文) (Codepage 837 加 836) *  
5033 (繁体中文) (Codepage 835 加 37) *  
*代表采用EBCDIC编码格式

由此可见,Mircosoft的CJK Codepage来源于IBM的Codepage.


2. Linux下Codepage的作用
在Linux 下引入对Codepage的支持主要是为了访问FAT/VFAT/FAT32/NTFS/NCPFS等文件系统下的多语种文件名的问题,目前在NTFS和 FAT32/VFAT下的文件系统上都使用了Unicode,这就需要系统在读取这些文件名时动态将其转换为相应的语言编码.因此引入了NLS支持.其相 应的程序文件在/usr/src/linux/fs/nls下:

Config.in  
Makefile  
nls_base.c  
nls_cp437.c  
nls_cp737.c  
nls_cp775.c  
nls_cp850.c  
nls_cp852.c  
nls_cp855.c  
nls_cp857.c  
nls_cp860.c  
nls_cp861.c  
nls_cp862.c  
nls_cp863.c  
nls_cp864.c  
nls_cp865.c  
nls_cp866.c  
nls_cp869.c  
nls_cp874.c  
nls_cp936.c  
nls_cp950.c  
nls_iso8859-1.c  
nls_iso8859-15.c  
nls_iso8859-2.c  
nls_iso8859-3.c  
nls_iso8859-4.c  
nls_iso8859-5.c  
nls_iso8859-6.c  
nls_iso8859-7.c  
nls_iso8859-8.c  
nls_iso8859-9.c  
nls_koi8-r.c  
实现了下列函数:

extern int utf8_mbtowc(__u16 *, const __u8 *, int);  
extern int utf8_mbstowcs(__u16 *, const __u8 *, int);  
extern int utf8_wctomb(__u8 *, __u16, int);  
extern int utf8_wcstombs(__u8 *, const __u16 *, int);  
这样在加载相应的文件系统时就可以用下面的参数来设置Codepage:

对于Codepage 437 来说

mount -t vfat /dev/hda1 /mnt/1 -o codepage=437,iocharset=cp437

这样在Linux下就可以正常访问不同语种的长文件名了.


3. Linux下支持的Codepage
nls codepage 437 -- 美国/加拿大英语  
nls codepage 737 -- 希腊语  
nls codepage 775 -- 波罗的海语  
nls codepage 850 -- 包括西欧语种(德语,西班牙语,意大利语)中的一些字符  
nls codepage 852 -- Latin 2 包括中东欧语种(阿尔巴尼亚语,克罗地亚语,捷克语,英语,芬兰语,匈牙利语,爱尔兰语,德语,波兰语,罗马利亚语,塞尔维亚语,斯洛伐克语,斯洛文尼亚语,Sorbian语)  
nls codepage 855 -- 斯拉夫语  
nls codepage 857 -- 土耳其语  
nls codepage 860 -- 葡萄牙语  
nls codepage 861 -- 冰岛语  
nls codepage 862 -- 希伯来语  
nls codepage 863 -- 加拿大语  
nls codepage 864 -- 阿拉伯语  
nls codepage 865 -- 日尔曼语系  
nls codepage 866 -- 斯拉夫语/俄语  
nls codepage 869 -- 希腊语(2)  
nls codepage 874 -- 泰语  
nls codepage 936 -- 简体中文GBK  
nls codepage 950 -- 繁体中文Big5  
nls iso8859-1 -- 西欧语系(阿尔巴尼亚语,西班牙加泰罗尼亚语,丹麦语,荷兰语,英语,Faeroese语,芬兰语,法语,德语,加里西亚语,爱尔兰语,冰岛语,意大利语,挪威语,葡萄牙语,瑞士语.)这同时适用于美国英语.  
nls iso8859-2 -- Latin 2 字符集,斯拉夫/中欧语系(捷克语,德语,匈牙利语,波兰语,罗马尼亚语,克罗地亚语,斯洛伐克语,斯洛文尼亚语)  
nls iso8859-3 -- Latin 3 字符集, (世界语,加里西亚语,马耳他语,土耳其语)  
nls iso8859-4 -- Latin 4 字符集, (爱莎尼亚语,拉脱维亚语,立陶宛语),是Latin 6 字符集的前序标准  
nls iso8859-5 -- 斯拉夫语系(保加利亚语,Byelorussian语,马其顿语,俄语,塞尔维亚语,乌克兰语) 一般推荐使用 KOI8-R codepage  
nls iso8859-6 -- 阿拉伯语.  
nls iso8859-7 -- 现代希腊语  
nls iso8859-8 -- 希伯来语  
nls iso8859-9 -- Latin 5 字符集, (去掉了 Latin 1中不经常使用的一些冰岛语字符而代以土耳其语字符)  
nls iso8859-10 -- Latin 6 字符集, (因纽特(格陵兰)语,萨摩斯岛语等Latin 4 中没有包括的北欧语种)  
nls iso8859-15 -- Latin 9 字符集, 是Latin 1字符集的更新版本,去掉一些不常用的字符,增加了对爱莎尼亚语的支持,修正了法语和芬兰语部份,增加了欧元字符)  
nls koi8-r -- 俄语的缺省支持

4. 简体中文GBK/繁体中文Big5的Codepage
如何制作简体中文GBK/繁体中文Big5的Codepage?

从 Unicode 组织取得GBK/Big5的Unicode的定义.  
由于GBK是基于ISO 10646-1:1993标准的,而相应的日文是JIS X 0221-1995,韩文是KS C 5700-1995,他们被提交到Unicode标准的时间表为:
Unicode Version 1.0
Unicode Version 1.1 <-> ISO 10646-1:1993, JIS X 0221-1995, GB 13000.1-93
Unicode Version 2.0 <-> KS C 5700-1995

从Windows 95开始均采用GBK编码. 您需要的是 CP936.TXT和 BIG5.TXT

然后使用下面的程序就可以将其转化为Linux核心需要的Unicode<->GBK码表
./genmap BIG5.txt | perl uni2big5.pl
./genmap CP936.txt | perl uni2gbk.pl  
再修改fat/vfat/ntfs的相关函数就可以完成对核心的修改工作. 具体使用时可以使用下面的命令:  
简体中文: mount -t vfat /dev/hda1 /mnt/1 -o codepage=936,iocharset=cp936  
繁体中文: mount -t vfat /dev/hda1 /mnt/1 -o codepage=950,iocharset=cp936  
有趣的是,由于GBK包含了全部的GB2312/Big5/JIS的内码,所以使用936的Codepage也可以显示Big5的文件名.


5. 附录
5.1 作者和相关文档
制作codepage950支持的是台湾的 cosmos先生, 主页为

制作GBK的cp936支持的是TurboLinux的中文研发小组的 方汉和 陈向阳

5.2 genmap
#!/bin/shcat $1 | awk '{if(index($1,"#")==0)print $0}' | awk 'BEGIN{FS="0x"}{print $2 $3}' | awk '{if(length($1)==length($2))print $1,$2}'
5.3 uni2big5.pl
5.4 uni2gbk.pl
5.5 转换CODEPAGE的工具



让 ubuntu 也支持 gb-2312的办法
修改文件:
/var/lib/locales/supported.d/local
内容是:
en_US.UTF-8 UTF-8
zh_CN.UTF-8 UTF-8
zh_CN.GBK GBK
zh_CN GB2312
保存后 ,执行命令:
sudo locale-gen
让系统能够很好的 支持中文 。
阅读(1824) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~