• UTF-8, Unicode, GB2312格式串转换之C语言版


    原住址:http://www.cnitblog.com/wujian-IT/archive/2007/12/13/37671.html    
          /*      author:   wu.jian   (吴剑)      English name: Sword
          /*      date:      2007-12-13
          /*      purpose:   知识共享

          这几天工作上碰到了UTF-8转GB2312的问题,而且是在嵌入式的环境下,没有API可用,查了很多网上的资料,大多调用VC或者Linux下自带的接口。在这里我将这两天的工作做个总结。
          总的来说分为两大步(这里就不介绍基础知识了):

          一、UTF8 -> Unicode
          由于UTF8和Unicode存在着联系,所以不需要任何库就可以直接进行转换。首先要看懂UTF8的编码格式:
          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  
          前面几个1就代表后面几个字节是属于一起的。如果要解析一长串UTF8格式的字符串,这点就很有用了。下面这个函数就是判断前面几个1的(这里有define APP_PRINT printf,这样当release的时候将这个宏定义为空就行了,不需要一个一个去改,又方便重新调试):
          int GetUtf8ByteNumForWord(u8 firstCh)  //判断前面几个1
          {
              u8 temp = 0x80;
              int num = 0;
     
              while (temp & firstCh)
              {
                    num++;
                     temp = (temp >> 1);
               }

           APP_PRINT("the num is: %d", num);
            return num;
          }
          利用这个函数可以得到字符串中那几个字节是一起的。因为UTF8最大只有6个字节,所以就根据返回值来处理这里我只处理了3个字节和1个字节的UTF8的编码,因为一般来说中文在UTF8中是3个字节

    //将len个字节的UTF8格式的转换成GB2312格式存放在temp预先申请好的缓冲区中
    void Utf8ToGb2312(const char* utf8, int len, char *temp)
    {
           APP_PRINT("utf8->unicode: ");
           APP_PRINT("utf8: [");
           for (int k = 0; k < len; k++)
           {
                  APP_PRINT("%02x ", utf8[k]);
           }
           APP_PRINT("] ");
     
           int byteCount = 0;
           int i = 0;
           int j = 0;

           u16 unicodeKey = 0;
           u16 gbKey = 0;


          //循环解析
           while (i < len)
           {   
            switch(GetUtf8ByteNumForWord((u8)utf8[i]))
            {
              case 0:
                    temp[j] = utf8[i];
                    byteCount = 1;
              break;

              case 2:
              temp[j] = utf8[i];
              temp[j + 1] = utf8[i + 1];
              byteCount = 2;
              break;

             case 3:
                     //这里就开始进行UTF8->Unicode
                     temp[j + 1] = ((utf8[i] & 0x0F) << 4) | ((utf8[i + 1] >> 2) & 0x0F);
                     temp[j] = ((utf8[i + 1] & 0x03) << 6) + (utf8[i + 2] & 0x3F);

                    //取得Unicode的值
                     memcpy(&unicodeKey, (temp + j), 2);
                     APP_PRINT("unicode key is: 0x%04X ", unicodeKey);

                      //根据这个值查表取得对应的GB2312的值
                    gbKey = SearchCodeTable(unicodeKey);
                    APP_PRINT("gb2312 key is: 0x%04X ", gbKey);
        
                    if (gbKey != 0)
                    {
                           //here change the byte
                            //不为0表示搜索到,将高低两个字节调换调成我要的形式
                           gbKey = (gbKey >> 8) | (gbKey << 8);
                           APP_PRINT("after changing, gb2312 key is: 0x%04X ", gbKey);
                           memcpy((temp + j), &gbKey, 2);
                      }

                    byteCount = 3;
              break;

              case 4:
              byteCount = 4;
              break;
             case 5:
              byteCount = 5;
              break;
             case 6:
              byteCount = 6;
              break;
        
             default:
              APP_PRINT("the len is more than 6 ");
              break;    
            }

            i += byteCount;
            if (byteCount == 1)
            {
                   j++;
            }
            else
            {
                   j += 2;
            }
      
           }
           APP_PRINT("utf8: [");
           for (k = 0; k < j; k++)
           {
                  APP_PRINT("%02x ", temp[k]);
           }
           APP_PRINT("] ");
    }

          二、下面主要谈谈利用查表法来进行Unicode->GB2312的转换,首先下载码表,一般码表都是将GB2312的放在前面,Unicode放在后面,这样对于我们来说不方便使用,所以我转换了下,将Unicode放在前面,而且按照从小到大排好序。(这里只需要考虑都为两个字节的情况,因为前面的UTF8->Unicode并没有将单字节的ASCII转换成Unicode)
                (1)做表:(可以到这里下载:http://blog.91bs.com/?action=show&id=20,这里谢谢渣渣的猪窝)
                这个是原来的样子:
                0x8140 0x4E02 #CJK UNIFIED IDEOGRAPH
                0x8141 0x4E04 #CJK UNIFIED IDEOGRAPH
                0x8142 0x4E05 #CJK UNIFIED IDEOGRAPH
                先弄成(这个可以写个小程序来做,我就是在VC上做的,如果需要可以联系我):
                { 0x4E02 ,0x8140 }, //CJK UNIFIED IDEOGRAPH
                { 0x4E04 ,0x8141 }, //CJK UNIFIED IDEOGRAPH
                { 0x4E05 ,0x8142 }, //CJK UNIFIED IDEOGRAPH
                这样就可以把这些放在.h文件中了,下面是我的定义:
                typedef struct unicode_gb
                {
                       unsigned short unicode;
                       unsigned short gb;
                } UNICODE_GB;

                UNICODE_GB code_table[] = 
                {
                      { 0x4E02, 0x8140 },   //CJK UNIFIED IDEOGRAPH
                      { 0x4E04, 0x8141 },  //CJK UNIFIED IDEOGRAPH
                      { 0x4E05, 0x8142 },  //CJK UNIFIED IDEOGRAPH
                      。。。。。。省略

                下面这一步也很简单,在VC中用冒泡排序法,对这个数组按照unicode值进行排序,如果需要可以联系我,把最终结果打印出来,在cmd下运行name > 1.txt就输出到文件,这样就有了一个按照unicode排好序的unicode->gb2312码表。

       int main(int argc, char *argv[])
    {

        int num = 0;
        UNICODE_GB temp;
        int i = 0;
        int j = 0;

        num = sizeof(code_table) / sizeof(UNICODE_GB);

        printf("struct size: %d | total size: %d | num is: %d ", 
        sizeof(UNICODE_GB), sizeof(code_table), num);

        for (i = 0; i < num; i++)
        {
            for (j = 1; j < num - i; j++)
            {
                if (code_table[j - 1].unicode > code_table[j].unicode)
                {
                    temp.unicode = code_table[j - 1].unicode;
                    temp.gb = code_table[j - 1].gb;
                    code_table[j - 1].unicode = code_table[j].unicode;
                    code_table[j - 1].gb = code_table[j].gb;
                    code_table[j].unicode = temp.unicode;
                    code_table[j].gb = temp.gb;
                }
            }
        }

        printf("here is the code table sorted by unicode ");

        for (i = 0; i < num; i++)
        {
            printf("{ 0x%04X, 0x%04X }, ", code_table[i].unicode, code_table[i].gb);
         }

           printf(" print over! ");

       //以下注释掉的其实就是我用来对原来的码表添加,{,}等用的
       /*
        char buff[100];
        char buff_1[100]; 
     
        FILE* fp = NULL;
        FILE *fp_1 = NULL;

        memset(buff, 0, 100);
        memset(buff_1, 0, 100);
     
        fp = fopen("table.txt", "rw");
        fp_1 = fopen("table_1.txt", "a+");

        if ((fp == NULL) || (fp_1 == NULL))
        {
            printf("open file error! ");
            return 1;
        }

        while (fgets(buff, 100, fp) != NULL)
        {
            buff[8] = ',';

            fputs(buff, fp_1);
        }
     */

        return 0;
    }

          最后就是搜索算法了,前面已经排好序了,现在我们把排好序的码表放在我们真正需要的.h文件中。大家应该猜我用什么算法搜索了吧,二分法。

    #define CODE_TABLE_SIZE 21791
    //这个表是死的,所以就直接用宏表示长度,不用每次都用size,不过这样可能对移植性不好。
    u16 SearchCodeTable(u16 unicodeKey)
    {
        int first = 0;
        int end = CODE_TABLE_SIZE - 1;
        int mid = 0;

        while (first <= end)
        {
            mid = (first + end) / 2;

            if (code_table[mid].unicode == unicodeKey)
            {
                return code_table[mid].gb;
            }
            else if (code_table[mid].unicode > unicodeKey)
            {
                end = mid - 1;
            }
            else 
            {
                first = mid + 1;
            }
        }
        return 0;
    }
          到此,已经能够将UTF8串转换成GB2312了。是一长串哦,而不是单个汉字的编码转换。

  • 相关阅读:
    HR问“你目前有几个offer”,聪明人会怎么说?
    秋招还有 1 个月到达战场,请做好准备 !
    我人生中的第一场Java面试
    MZ头里面的东西。真他妈多
    特殊的一卦
    今天出门去办事,又倒霉了
    内核回调
    sys_call_table HOOK
    起一卦,看看情况
    我的简陋界面库的模块组成
  • 原文地址:https://www.cnblogs.com/alan666/p/8311940.html
Copyright © 2020-2023  润新知