现在的位置: 首页 > 综合 > 正文

utf8与unicode转换

2013年02月27日 ⁄ 综合 ⁄ 共 4691字 ⁄ 字号 评论关闭

转自http://blog.csdn.net/nrc_douningbo/article/details/5880602  

前端时间要自己实现utf8和unicode格式转换,打算封装一个类。也想过偷懒,直接网上找一个拿来用,但是后来发现问题多多。首先:接口不一样,其次:网上大部分方法还是不靠谱的,或者说:方法现在已经已经不适用了。

     关于二者转换的原理网上很多,这里就不赘述了。主要说一下我遇到的问题:

     我希望写成的接口是这个样子的:

    

  1. class CStrConvertor  
  2. {  
  3. public:                                    
  4.     static int Unicode2Utf8(LPSTR cBuf, int& iCBuf,LPCWSTR uBuf, int iUBuf);  
  5.     static int Utf82Unicode(LPWSTR pDst,  int nDstLen , LPSTR pSrc,int nSrcLen);  
  6. };  

 

    亦即:外边分配好存储转换结果的空间,而后在方法中对其进行赋值。

 

   问题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的空间!

---------------------------------------------------------------------------------------------------------------------------------------------------

   下边是我封装的类代码,有错误大家指正。

  1.  /** 
  2.   *     该函数用于将Unicode数组转为utf8格式! 
  3.   *     @param out   转换结果存放区域指针 
  4.   *     @param outLength   转换结果存放区域大小 
  5.   *     @param in   源串存放区域指针 
  6.   *     @param inLength   源串存放区域大小 
  7.   *     @return QINT 转换结果在目的串中实际使用的长度,转换失败则返回-1 
  8.   */  
  9. int CStrConvertor::Unicode2Utf8( char* out, int& outLength,const wchar_t * in, int inLength )  
  10. {  
  11.     //------------------------------------------------  
  12.     //参数有效性判断  
  13.     if(out == NULL || in == NULL || inLength<0)  
  14.     {  
  15.         return -1;  
  16.     }  
  17.     int totalNum = 0;  
  18.     for(int i = 0; i < inLength; i++)//计算转换结果实际所需长度  
  19.     {  
  20.         wchar_t unicode = in[i];  
  21.         if (unicode >= 0x0000 && unicode <= 0x007f)  
  22.         {  
  23.             totalNum += 1;  
  24.         }  
  25.         else if (unicode >= 0x0080 && unicode <= 0x07ff)  
  26.         {  
  27.             totalNum += 2;  
  28.         }  
  29.         else if (unicode >= 0x0800 && unicode <= 0xffff)  
  30.         {  
  31.             totalNum += 3;  
  32.         }  
  33.     }  
  34.     if( outLength < totalNum )//参数有效性判断!  
  35.     {  
  36.         return -1;  
  37.     }  
  38.     //------------------------------------------------  
  39.   
  40.     int outsize = 0;//用来计数输出结果的实际大小!  
  41.     char *tmp = out;  
  42.     int i = 0;  
  43.     for (i = 0; i < inLength; i++)  
  44.     {  
  45.         if(outsize>outLength) //空间不足对应处理!  
  46.         {  
  47.             return -1;  
  48.         }  
  49.         wchar_t unicode = in[i];  
  50.           
  51.         if (unicode >= 0x0000 && unicode <= 0x007f)  
  52.         {  
  53.             *tmp = (char)unicode;  
  54.             tmp += 1;  
  55.             outsize += 1;  
  56.         }  
  57.         else if (unicode >= 0x0080 && unicode <= 0x07ff)  
  58.         {  
  59.             *tmp = 0xc0 | (unicode >> 6);  
  60.             tmp += 1;  
  61.             *tmp = 0x80 | (unicode & (0xff >> 2));  
  62.             tmp += 1;  
  63.             outsize += 2;  
  64.         }  
  65.         else if (unicode >= 0x0800 && unicode <= 0xffff)  
  66.         {  
  67.             *tmp = 0xe0 | (unicode >> 12);  
  68.             tmp += 1;  
  69.             *tmp = 0x80 | (unicode >> 6 & 0x00ff);  
  70.             tmp += 1;  
  71.             *tmp = 0x80 | (unicode & (0xff >> 2));  
  72.             tmp += 1;  
  73.             outsize += 3;  
  74.         }  
  75.     }  
  76.     return outsize;  
  77. }  
  78. -------------------------------------------------------  
  79. /** 
  80.   * 该函数用于将utf8数组转为Unicode格式! 
  81.   * 目前该函数返回值为:转换后unicode数据占用的wchar_t的个数(切记不是总char的个数) ! 
  82.   *     @param out   转换结果存放区域指针 
  83.   *     @param outsize   转换结果存放区域大小 
  84.   *     @param in   源串存放区域指针 
  85.   *     @param insize   源串存放区域大小 
  86.   *     @return QINT 转换结果在目的串中的长度,转换失败则返回-1 
  87.   */   
  88. QINT  CStrConvertor::Utf82Unicode(LPWSTR out,  QINT outsize , LPSTR in,QINT insize)  
  89. {  
  90.     //-------------------------------------------------------------------------------------------  
  91.     //参数有效性判断  
  92.     if(out == NULL || in == NULL || insize<0)  
  93.     {  
  94.         return -1;  

抱歉!评论已关闭.