跨平台utf8转unicode研究实现(3)
2013-08-05 18:53
281 查看
前端时间要自己实现utf8和unicode格式转换,打算封装一个类。也想过偷懒,直接网上找一个拿来用,但是后来发现问题多多。首先:接口不一样,其次:网上大部分方法还是不靠谱的,或者说:方法现在已经已经不适用了。
关于二者转换的原理网上很多,这里就不赘述了。主要说一下我遇到的问题:
我希望写成的接口是这个样子的:
亦即:外边分配好存储转换结果的空间,而后在方法中对其进行赋值。
问题1:如何做参数有效性检查。检查集中在结果存储空间是否足够大小上。由于utf8是变长的,在转换结束之前永远不知道其具体占用多大空间,所以不好先期判断。可能也有人说了:Unicode2Utf8时我直接开3倍+1大小的空间,Utf82Unicode直接开2k+2的空间不就足够了?在转换函数中直接判断结果存储空间达没达到这个大小不就OK了?的确,这样没错。但是作为一个完善的类,这样是不严谨的!比如:Unicode2Utf8时可能提供的结果串只需要2k空间就足够了,外边也开了2倍空间,但是我们函数中上来就判断其不到3倍+1然后给报错了,这显然不行!所以:用最大空间来判断不好!
解决:1:网上也有人这么做,就是结果存储空间是在转换函数内部分配的,外部只是声明了一个指针而已。这样随着转换的进行,同时追加分配新空间! 这样的确解决了上边的问题,但是分配空间本身是相当消耗资源的,这函数的效率就低了!
2:我用的方法:在转换前也做一次遍历,看每个字符转换后占用几个byte,进行累加。依据最后的值判断参数的有效性!这样虽然给人感觉多做了一遍,但是较为严谨!
问题2:比如在Utf82Unicode时,我们需要看每个byte的前几位是否满足utf8的格式定义,这要用到:从一个字节中取出前/后若干位来。网上很多方法都是简单的直接做了个移位操作,比如:0xff<<5 。但是这样显然会遇到一个很基础的错误!
把问题抽象出来:0xff << 5 和 0xe0是一样的吗?嗯,有时候一样,有时候不一样!当直接使用<< 或>>进行位移操作时:你要明白它在cpu中是如何进行的。比如一个32位宽的cpu,当执行0xff << 5时:高位移除的5位并没有丢失掉!毕竟32位宽,所以移位完之后变为:0x1fe0 ,这也是为什么0xff << 5 和 0xe不一定相等的原因。二者相等不相等取决于cpu的位宽!
网上很多算法都没有注意到这点,我猜测那些算法都是8位宽时代实现的,后来放网上时也没去修改。但是这种不同只有在左移时才发生,右移时不会发生!这提示我们:在使用时尽可能少用移位操作符,本身考虑的东西太多而且代码阅读性也不好。
---------------------------------------------------------------------------------------------------------------------------------------------------
基础补充:
1:一个unicode在内存中占用2个byte。而utf是一种存储格式,其是变长的,最长可达6byte。但是基本上3个byte已经包括目前所能用到的全部字符了。 所以:Unicode2Utf8时最坏情况为:每个Unicode转完后都占用3个byte,所以最大要开3倍空间,再加上有一个/0结束符,所以最大需要开3k+1的空间! 而Utf82Unicode时:最坏情况下每个Utf8都转为一个unicode,亦即原本的每个byte先变为2个byte,再加上最后两个byte的/0
所以最大需要开2k+2的空间!
---------------------------------------------------------------------------------------------------------------------------------------------------
下边是我封装的类代码,有错误大家指正。
关于二者转换的原理网上很多,这里就不赘述了。主要说一下我遇到的问题:
我希望写成的接口是这个样子的:
class CStrConvertor { public: static int Unicode2Utf8(LPSTR cBuf, int& iCBuf,LPCWSTR uBuf, int iUBuf); static int Utf82Unicode(LPWSTR pDst, int nDstLen , LPSTR pSrc,int nSrcLen); };
亦即:外边分配好存储转换结果的空间,而后在方法中对其进行赋值。
问题1:如何做参数有效性检查。检查集中在结果存储空间是否足够大小上。由于utf8是变长的,在转换结束之前永远不知道其具体占用多大空间,所以不好先期判断。可能也有人说了:Unicode2Utf8时我直接开3倍+1大小的空间,Utf82Unicode直接开2k+2的空间不就足够了?在转换函数中直接判断结果存储空间达没达到这个大小不就OK了?的确,这样没错。但是作为一个完善的类,这样是不严谨的!比如:Unicode2Utf8时可能提供的结果串只需要2k空间就足够了,外边也开了2倍空间,但是我们函数中上来就判断其不到3倍+1然后给报错了,这显然不行!所以:用最大空间来判断不好!
解决:1:网上也有人这么做,就是结果存储空间是在转换函数内部分配的,外部只是声明了一个指针而已。这样随着转换的进行,同时追加分配新空间! 这样的确解决了上边的问题,但是分配空间本身是相当消耗资源的,这函数的效率就低了!
2:我用的方法:在转换前也做一次遍历,看每个字符转换后占用几个byte,进行累加。依据最后的值判断参数的有效性!这样虽然给人感觉多做了一遍,但是较为严谨!
问题2:比如在Utf82Unicode时,我们需要看每个byte的前几位是否满足utf8的格式定义,这要用到:从一个字节中取出前/后若干位来。网上很多方法都是简单的直接做了个移位操作,比如:0xff<<5 。但是这样显然会遇到一个很基础的错误!
把问题抽象出来:0xff << 5 和 0xe0是一样的吗?嗯,有时候一样,有时候不一样!当直接使用<< 或>>进行位移操作时:你要明白它在cpu中是如何进行的。比如一个32位宽的cpu,当执行0xff << 5时:高位移除的5位并没有丢失掉!毕竟32位宽,所以移位完之后变为:0x1fe0 ,这也是为什么0xff << 5 和 0xe不一定相等的原因。二者相等不相等取决于cpu的位宽!
网上很多算法都没有注意到这点,我猜测那些算法都是8位宽时代实现的,后来放网上时也没去修改。但是这种不同只有在左移时才发生,右移时不会发生!这提示我们:在使用时尽可能少用移位操作符,本身考虑的东西太多而且代码阅读性也不好。
---------------------------------------------------------------------------------------------------------------------------------------------------
基础补充:
1:一个unicode在内存中占用2个byte。而utf是一种存储格式,其是变长的,最长可达6byte。但是基本上3个byte已经包括目前所能用到的全部字符了。 所以:Unicode2Utf8时最坏情况为:每个Unicode转完后都占用3个byte,所以最大要开3倍空间,再加上有一个/0结束符,所以最大需要开3k+1的空间! 而Utf82Unicode时:最坏情况下每个Utf8都转为一个unicode,亦即原本的每个byte先变为2个byte,再加上最后两个byte的/0
所以最大需要开2k+2的空间!
---------------------------------------------------------------------------------------------------------------------------------------------------
下边是我封装的类代码,有错误大家指正。
/** * 该函数用于将Unicode数组转为utf8格式! * @param out 转换结果存放区域指针 * @param outLength 转换结果存放区域大小 * @param in 源串存放区域指针 * @param inLength 源串存放区域大小 * @return QINT 转换结果在目的串中实际使用的长度,转换失败则返回-1 */ int CStrConvertor::Unicode2Utf8( char* out, int& outLength,const wchar_t * in, int inLength ) { //------------------------------------------------ //参数有效性判断 if(out == NULL || in == NULL || inLength<0) { return -1; } int totalNum = 0; for(int i = 0; i < inLength; i++)//计算转换结果实际所需长度 { wchar_t unicode = in[i]; if (unicode >= 0x0000 && unicode <= 0x007f) { totalNum += 1; } else if (unicode >= 0x0080 && unicode <= 0x07ff) { totalNum += 2; } else if (unicode >= 0x0800 && unicode <= 0xffff) { totalNum += 3; } } if( outLength < totalNum )//参数有效性判断! { return -1; } //------------------------------------------------ int outsize = 0;//用来计数输出结果的实际大小! char *tmp = out; int i = 0; for (i = 0; i < inLength; i++) { if(outsize>outLength) //空间不足对应处理! { return -1; } wchar_t unicode = in[i]; if (unicode >= 0x0000 && unicode <= 0x007f) { *tmp = (char)unicode; tmp += 1; outsize += 1; } else if (unicode >= 0x0080 && unicode <= 0x07ff) { *tmp = 0xc0 | (unicode >> 6); tmp += 1; *tmp = 0x80 | (unicode & (0xff >> 2)); tmp += 1; outsize += 2; } else if (unicode >= 0x0800 && unicode <= 0xffff) { *tmp = 0xe0 | (unicode >> 12); tmp += 1; *tmp = 0x80 | (unicode >> 6 & 0x00ff); tmp += 1; *tmp = 0x80 | (unicode & (0xff >> 2)); tmp += 1; outsize += 3; } } return outsize; } ------------------------------------------------------- /** * 该函数用于将utf8数组转为Unicode格式! * 目前该函数返回值为:转换后unicode数据占用的wchar_t的个数(切记不是总char的个数) ! * @param out 转换结果存放区域指针 * @param outsize 转换结果存放区域大小 * @param in 源串存放区域指针 * @param insize 源串存放区域大小 * @return QINT 转换结果在目的串中的长度,转换失败则返回-1 */ QINT CStrConvertor::Utf82Unicode(LPWSTR out, QINT outsize , LPSTR in,QINT insize) { //------------------------------------------------------------------------------------------- //参数有效性判断 if(out == NULL || in == NULL || insize<0) { return -1; } int totalNum = 0; char *p = in; for(int i=0;i<insize;i++) { if (*p >= 0x00 && *p <= 0x7f)//说明最高位为'0',这意味着utf8编码只有1个字节! { p++; totalNum += 1; } else if ((*p & (0xe0))== 0xc0)//只保留最高三位,看最高三位是不是110,如果是则意味着utf8编码有2个字节! { p++; p++; totalNum += 1; } else if ((*p & (0xf0))== 0xe0)//只保留最高四位,看最高三位是不是1110,如果是则意味着utf8编码有3个字节! { p++; p++; p++; totalNum += 1; } } if( outsize < totalNum )//参数有效性判断! { return -1; } //------------------------------------------------ int resultsize = 0; p = in; char* tmp = (char *)out; while(*p) { if (*p >= 0x00 && *p <= 0x7f)//说明最高位为'0',这意味着utf8编码只有1个字节! { *tmp = *p; tmp++; //*tmp = '/0'; tmp++; resultsize += 1; } else if ((*p & 0xe0)== 0xc0)//只保留最高三位,看最高三位是不是110,如果是则意味着utf8编码有2个字节! { wchar_t t = 0; char t1 = 0; char t2 = 0; t1 = *p & (0x1f);//高位的后5位!(去除了头部的110这个标志位) p++; t2 = *p & (0x3f);//低位的后6位!(去除了头部的10这个标志位) *tmp = t2 | ((t1 & (0x03)) << 6); tmp++; *tmp = t1 >> 2;//留下其保留的三位 tmp++; resultsize += 1; } else if ((*p & (0xf0))== 0xe0)//只保留最高四位,看最高三位是不是1110,如果是则意味着utf8编码有3个字节! { wchar_t t = 0; wchar_t t1 = 0; wchar_t t2 = 0; wchar_t t3 = 0; t1 = *p & (0x1f); p++; t2 = *p & (0x3f); p++; t3 = *p & (0x3f); *tmp = ((t2 & (0x03)) << 6) | t3; tmp++; *tmp = (t1 << 4) | (t2 >> 2); tmp++; resultsize += 1; } p++; } /*不考虑结束符,如果考虑则打开此段! *tmp = '/0'; tmp++; *tmp = '/0'; resultsize += 2; */ return resultsize; }
相关文章推荐
- 跨平台utf8转unicode研究实现(2)
- 跨平台utf8转unicode研究实现(2)
- 跨平台utf8转unicode研究实现(4)
- 基于J2EE平台Web系统的软件测试研究和实现
- pb字符串实现GB2312与Unicode、UTF8之间的相互转换
- 毕业设计项目展示作品四:基于iOS平台《百思不得姐》(似快手APP)Xcode开发技术研究与实现
- 纯C实现unicode-utf8互转
- 纯C实现unicode-utf8互转
- 跨平台:utf8与Unicode、string与wstring相互转换【经验】
- 跨平台utf8转码unicode研究实现(1)
- Web Service平台无关性研究与实现
- 纯C实现unicode-utf8互转
- Unicode编码,以及实现方式UTF8的实现方式
- Web Service平台无关性研究与实现
- 纯C实现unicode-utf8互转
- NDK移植havlenapetr/FFMpeg |Android平台实现 【Mark一下,目前仅仅测试了一下可行性(可行,但有问题)|接下来就好好研究研究(站下巨人的肩膀么- -)】
- USBKEY用户认证平台的研究和实现
- USBKEY用户认证平台的研究和实现
- Java实现的utf8,gbk,unicode编码相互转换的代码
- Android平台好友点击微信分享的内容后跳转来源App的实现方案研究