zoukankan      html  css  js  c++  java
  • unicode 和 utf8

    关于 unicode utf8

    文章来自于 http://blog.csdn.net/tge7618291/article/details/7599902

    ascii

    主要来表示英文.但是要全世界那么多语言符号文字,ascii就不够使用了,为了统一,unicode出现了.

    unicode

    包含全世界所有的语言文字的编码方案.对于每个字符都有一个规定的二进制代码.
    需要注意的是, "Unicode只是一个符号集, 它只规定了符号的二进制代码, 却没有规定这
    个二进制代码应该如何存储".

    unicode的问题

    1. 如何区分unicode和ascii编码?
    2. 如果unicode统一规定, 每个符号用三个或四个字节表示, 那么每个英文字母前都必然有二到三个字节是0, 这对于存来说是极大的浪费,文本文件的大小会因此大出二三倍, 这是无法接受的.

    它们造成的结果是:

    1. 出现了unicode的多种存储方式, 也就是说有许多种不同的二进制格式,
      可以用来表示unicode.
    2. unicode在很长一段时间内无法推广, 直到互联网的出现

    utf-8

    互联网的普及, 强烈要求出现一种统一的编码方式. UTF-8就是在互联网上使用最广的一
    种unicode的实现方式. 其他实现方式还包括UTF-16和UTF-32, 不过在互联网上基本不用.
    重复一遍, 这里的关系是, UTF-8是Unicode的实现方式之一.

    UTF-8最大的一个特点, 就是它是一种变长的编码方式. 它可以使用1~6个字节表示一个符
    号, 根据不同的符号而变化字节长度.

    utf-8的编码规则

    1. 对于单字节的符号, 字节的第一位设为0, 后面7位为这个符号的unicode码. 因此对于
      英语字母, UTF-8编码和ASCII码是相同的.

    2. 对于n字节的符号(n>1), 第一个字节的前n位都设为1, 第n+1位设为0, 后面字节的前
      两位一律设为10. 剩下的没有提及的二进制位, 全部为这个符号的unicode码.

    下表总结了编码规则, 字母x表示可用编码的位.

    0 | Unicode符号范围 | UTF-8编码方式
    n | (十六进制) | (二进制)
    ---+-----------------------+------------------------------------------------------
    1 | 0000 0000 - 0000 007F | 0xxxxxxx
    2 | 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx
    3 | 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
    4 | 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    5 | 0020 0000 - 03FF FFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

    下面, 以汉字"严"为例, 演示如何实现UTF-8编码.

    已知"严"的unicode是4E25(1001110 00100101), 根据上表, 可以发现4E25处在第三行的
    范围内(0000 0800 - 0000 FFFF), 因此"严"的UTF-8编码需要三个字节, 即格式是
    "1110xxxx 10xxxxxx 10xxxxxx". 然后, 从"严"的最后一个二进制位开始, 依次从后向前
    填入格式中的x, 多出的位补0. 这样就得到了, "严"的UTF-8编码是 "11100100 10111000
    10100101", 转换成十六进制就是E4B8A5.

    Little endian和Big endian

    上一节已经提到, Unicode码可以采用UCS-2格式直接存储. 以汉字"严"为例, Unicode码
    是4E25, 需要用两个字节存储, 一个字节是4E, 另一个字节是25. 存储的时候, 4E在前,
    25在后, 就是Big endian方式; 25在前, 4E在后, 就是Little endian方式.
    // Big Endian(4E25) Little Endian(254E)

    因此, 第一个字节在前, 就是"大头方式"(Big endian), 第二个字节在前就是"小头方式
    "(Little endian).

    计算机怎么知道某一个文件到底采用哪一种方式编码?(零宽度非换行空格(FEFF))

    Unicode规范中定义, 每一个文件的最前面分别加入一个表示编码顺序的字符, 这个字符
    的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE), 用FEFF表示. 这正好是
    两个字节, 而且FF比FE大1.
    // Big Endian(FEFF) Little Endian(FFFE)

    NOTE:
    如果一个文本文件的头两个字节是FE FF, 就表示该文件采用大头方式; 如果头两个字节
    是FF FE, 就表示该文件采用小头方式.

    utf8 和 unicode 之间的转换代码,

    // #c---
    /*****************************************************************************
     * 将一个字符的Unicode(UCS-2和UCS-4)编码转换成UTF-8编码.
     *
     * 参数:
     *    unic     字符的Unicode编码值
     *    pOutput  指向输出的用于存储UTF8编码值的缓冲区的指针
     *    outsize  pOutput缓冲的大小
     *
     * 返回值:
     *    返回转换后的字符的UTF8编码所占的字节数, 如果出错则返回 0 .
     *
     * 注意:
     *     1. UTF8没有字节序问题, 但是Unicode有字节序要求;
     *        字节序分为大端(Big Endian)和小端(Little Endian)两种;
     *        在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位)
     *     2. 请保证 pOutput 缓冲区有最少有 6 字节的空间大小!
     ****************************************************************************/
    int enc_unicode_to_utf8_one(unsigned long unic, unsigned char *pOutput,
                                int outSize){
        assert(pOutput != NULL);
        assert(outSize >= 6);
    
        if ( unic <= 0x0000007F )
        {
            // * U-00000000 - U-0000007F:  0xxxxxxx
            *pOutput     = (unic & 0x7F);
            return 1;
        }
        else if ( unic >= 0x00000080 && unic <= 0x000007FF )
        {
            // * U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
            *(pOutput+1) = (unic & 0x3F) | 0x80;
            *pOutput     = ((unic >> 6) & 0x1F) | 0xC0;
            return 2;
        }
        else if ( unic >= 0x00000800 && unic <= 0x0000FFFF )
        {
            // * U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
            *(pOutput+2) = (unic & 0x3F) | 0x80;
            *(pOutput+1) = ((unic >>  6) & 0x3F) | 0x80;
            *pOutput     = ((unic >> 12) & 0x0F) | 0xE0;
            return 3;
        }
        else if ( unic >= 0x00010000 && unic <= 0x001FFFFF )
        {
            // * U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
            *(pOutput+3) = (unic & 0x3F) | 0x80;
            *(pOutput+2) = ((unic >>  6) & 0x3F) | 0x80;
            *(pOutput+1) = ((unic >> 12) & 0x3F) | 0x80;
            *pOutput     = ((unic >> 18) & 0x07) | 0xF0;
            return 4;
        }
        else if ( unic >= 0x00200000 && unic <= 0x03FFFFFF )
        {
            // * U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            *(pOutput+4) = (unic & 0x3F) | 0x80;
            *(pOutput+3) = ((unic >>  6) & 0x3F) | 0x80;
            *(pOutput+2) = ((unic >> 12) & 0x3F) | 0x80;
            *(pOutput+1) = ((unic >> 18) & 0x3F) | 0x80;
            *pOutput     = ((unic >> 24) & 0x03) | 0xF8;
            return 5;
        }
        else if ( unic >= 0x04000000 && unic <= 0x7FFFFFFF )
        {
            // * U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            *(pOutput+5) = (unic & 0x3F) | 0x80;
            *(pOutput+4) = ((unic >>  6) & 0x3F) | 0x80;
            *(pOutput+3) = ((unic >> 12) & 0x3F) | 0x80;
            *(pOutput+2) = ((unic >> 18) & 0x3F) | 0x80;
            *(pOutput+1) = ((unic >> 24) & 0x3F) | 0x80;
            *pOutput     = ((unic >> 30) & 0x01) | 0xFC;
            return 6;
        }
    
        return 0;
    }  
    // #c---end
    
    
    int enc_get_utf8_size(const unsigned char pInput){
        unsigned char c = pInput;
        // 0xxxxxxx 返回0
        // 10xxxxxx 不存在
        // 110xxxxx 返回2
        // 1110xxxx 返回3
        // 11110xxx 返回4
        // 111110xx 返回5
        // 1111110x 返回6
        if(c< 0x80) return 0;
        if(c>=0x80 && c<0xC0) return -1;
        if(c>=0xC0 && c<0xE0) return 2;
        if(c>=0xE0 && c<0xF0) return 3;
        if(c>=0xF0 && c<0xF8) return 4;
        if(c>=0xF8 && c<0xFC) return 5;
        if(c>=0xFC) return 6;
        return -1;
    }
    
    
    /*****************************************************************************
     * 将一个字符的UTF8编码转换成Unicode(UCS-2和UCS-4)编码.
     *
     * 参数:
     *    pInput      指向输入缓冲区, 以UTF-8编码
     *    Unic        指向输出缓冲区, 其保存的数据即是Unicode编码值,
     *                类型为unsigned long .
     *
     * 返回值:
     *    成功则返回该字符的UTF8编码所占用的字节数; 失败则返回0.
     *
     * 注意:
     *     1. UTF8没有字节序问题, 但是Unicode有字节序要求;
     *        字节序分为大端(Big Endian)和小端(Little Endian)两种;
     *        在Intel处理器中采用小端法表示, 在此采用小端法表示. (低地址存低位)
     ****************************************************************************/
    int enc_utf8_to_unicode_one(const unsigned char* pInput, unsigned long *Unic)
    {
        assert(pInput != NULL && Unic != NULL);
    
        // b1 表示UTF-8编码的pInput中的高字节, b2 表示次高字节, ...
        char b1, b2, b3, b4, b5, b6;
    
        *Unic = 0x0; // 把 *Unic 初始化为全零
        int utfbytes = enc_get_utf8_size(*pInput);
        unsigned char *pOutput = (unsigned char *) Unic;
    
        switch ( utfbytes )
        {
            case 0:
                *pOutput     = *pInput;
                utfbytes    += 1;
                break;
            case 2:
                b1 = *pInput;
                b2 = *(pInput + 1);
                if ( (b2 & 0xE0) != 0x80 )
                    return 0;
                *pOutput     = (b1 << 6) + (b2 & 0x3F);
                *(pOutput+1) = (b1 >> 2) & 0x07;
                break;
            case 3:
                b1 = *pInput;
                b2 = *(pInput + 1);
                b3 = *(pInput + 2);
                if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) )
                    return 0;
                *pOutput     = (b2 << 6) + (b3 & 0x3F);
                *(pOutput+1) = (b1 << 4) + ((b2 >> 2) & 0x0F);
                break;
            case 4:
                b1 = *pInput;
                b2 = *(pInput + 1);
                b3 = *(pInput + 2);
                b4 = *(pInput + 3);
                if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) )
                    return 0;
                *pOutput     = (b3 << 6) + (b4 & 0x3F);
                *(pOutput+1) = (b2 << 4) + ((b3 >> 2) & 0x0F);
                *(pOutput+2) = ((b1 << 2) & 0x1C)  + ((b2 >> 4) & 0x03);
                break;
            case 5:
                b1 = *pInput;
                b2 = *(pInput + 1);
                b3 = *(pInput + 2);
                b4 = *(pInput + 3);
                b5 = *(pInput + 4);
                if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80) )
                    return 0;
                *pOutput     = (b4 << 6) + (b5 & 0x3F);
                *(pOutput+1) = (b3 << 4) + ((b4 >> 2) & 0x0F);
                *(pOutput+2) = (b2 << 2) + ((b3 >> 4) & 0x03);
                *(pOutput+3) = (b1 << 6);
                break;
            case 6:
                b1 = *pInput;
                b2 = *(pInput + 1);
                b3 = *(pInput + 2);
                b4 = *(pInput + 3);
                b5 = *(pInput + 4);
                b6 = *(pInput + 5);
                if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80)
                    || ((b6 & 0xC0) != 0x80) )
                    return 0;
                *pOutput     = (b5 << 6) + (b6 & 0x3F);
                *(pOutput+1) = (b5 << 4) + ((b6 >> 2) & 0x0F);
                *(pOutput+2) = (b3 << 2) + ((b4 >> 4) & 0x03);
                *(pOutput+3) = ((b1 << 6) & 0x40) + (b2 & 0x3F);
                break;
            default:
                return 0;
                break;
        }
    
        return utfbytes;
    }
    // #c---end
    
    
    
    // for test
    int main(){
        const char* utf_text = "我";   // u6211
        unsigned long unicode_text = 0;
        enc_utf8_to_unicode_one((unsigned char*)utf_text,&unicode_text);
    
        printf("unicode is : %lx
    ",unicode_text);
    
        // 再将 unicode 转换成 utf8
        unsigned char utf_result[8] = {0};
        enc_unicode_to_utf8_one(unicode_text,utf_result,8);
    
        printf("utf8 is : %s
    ",utf_result);
    
        return 0;
    }
    
  • 相关阅读:
    Java String 乱码
    HBase非原理性浅析
    git cherry-pick
    数据结构之队列
    数据结构之栈
    算法之简单排序
    数据结构之数组
    数据结构简介
    Java类型信息
    基数排序
  • 原文地址:https://www.cnblogs.com/daihanlong/p/5820053.html
Copyright © 2011-2022 走看看