在网上找了个ucs2转utf8的C语言实现, 看他写的代码超级麻烦。虽然同样是实现了转换。
我这里提供一个比较经典的实现转换的函数, 用到的技术主要是位操作符号。该函数来自于
公司内部人员的一个模块。 写的非常棒, 就拿出来共享一下了。
在开始写之前还是附带上一些utf8与ucs2的资料, 方便理解该转换的宏模块
以下是从一些网站截取的一些基础资料
=======================================================================
1.什么是UCS和ISO10646?
国际标准ISO10646定义了通用字符集(Universal Character Set, UCS). UCS是所有其它字符集标准的一个超集,它保证也其它字符集双向兼容,即编码间相互转换不会丢失任何信息。UCS字符集U+0000到U+007F与US-ASCII是一致的
2.什么是UNICODE
历史上, 有两个独立的, 创立单一字符集的尝试. 一个是国际标准化组织(ISO)的 ISO 10646 项目, 另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 Unicode 项目. 幸运的是, 1991年前后, 两个项目的参与者都认识到, 世界不需要两个不同的单一字符集. 它们合并双方的工作成果, 并为创立一个单一编码表而协同工作. 两个项目仍都存在并独立地公布各自的标准, 但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 标准的码表兼容, 并紧密地共同调整任何未来的扩展.
3.什么是UTF-8(一种传送和存储格式)
UCS和UNICODE为每个字符分配了一个对应的整数,但并没有明确说明其实现机制.故存在多种编码方式,其中以两个字节和四个字节来存储一个字符的方法分别叫UCS-2, UCS-4,要将一个ASCII文件转换成一个UCS-2文件只要在每个字节前加一个字节0X00,转换成UCS-4只要在每个字节前加三个0X00。
而internet上大量的信息是以ASCII码存在的,如果都用两个字节来存储将浪费大量的资源,同时Unix和Linux下使用USC-2和USC-4会导致严重问题,于是出现了UTF-8(定义于ISO10646-1).
UTF-8(UTF-8 stands for Unicode Transformation Format-8. It is an octet (8-bit) lossless encoding of Unicode characters.)
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制) | UTF-8 字节流(二进制) |
0000 - 007F | 0xxxxxxx |
0080 - 07FF | 110xxxxx 10xxxxxx |
0800 - FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
读者可以用记事本测试一下我们的编码是否正确。需要注意,UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16,可能产生混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。
========================================================================
下面是一个转换的宏模块:
//ucs2 char to utf8 char #define UCS2_CHAR_CONVERT(c,dest,len) {\ if(c < 0x80)\ len = 1;\ else if(c < 0x800)\ len = 2;\ else\ len = 3;\ switch (len) {\ case 3: dest[2] = 0x80 | (c & 0x3f); c = (c >> 6) | 0x800;\ case 2: dest[1] = 0x80 | (c & 0x3f); c = (c >> 6) | 0xc0;\ case 1: dest[0] = (BYTE)c;\ };\ }
代码的分析:
参数的含义:
@c: ucs2的一个字符, 2个字节
@dest: 存放转换后的一个数组, byte类型的, 数组长度是3
@len: 一个Int类型的技术flag, 用于记录ucs2的字符"c"转换成uft8需要多少个字节
代码段的第一个模块是, 判断字符"c"转换成uft8的存储格式需要几个字节。
代码段的第二个模块, 也就是switch语句: 作用是转换字符码。下面分析若len等于3的情况。
case3的步骤: 1、将"c"中的前6个字符取出来, 然后在加上1000 0000, 最终保存在数组3中的数据时10xxxxxx。
2、把"c"向右移动6位, 同时加上一个标志符号。 "c"结果为: 000010xx xxxxxxxx
case2的步骤: 1、将"c"中的后6个字符取出来, 加上1000 0000, 保存在数组2中的数据是10xxxxxx。
2、将"c"向右移动6位, 同时加上一个标志位。 "c"的结果为: 00000000 1110xxxx。
case1的步骤: 1、将"c"的剩余4位直接保存在数组1中。
这样就完成了3字节数据格式的转换了
至于2字节的转换, 1字节的转换, 我想聪明的你们一定应该知道是怎么回事了,以及如何使用它了。
希望这个函数能减轻你的代码量。
这是本人第一次对外写技术文档, 希望大家给点支持,同时给点意见, 以后我会继续将我工作中好的
代码拿出来给大家分享。
同时给大家点意见, 就是多看开源社区大师级的代码, 这样对你的代码质量会有很大的帮助。