溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

UTF-8、UTF-16、UTF-32編碼相互轉(zhuǎn)換的方法

發(fā)布時(shí)間:2021-06-24 11:03:30 來源:億速云 閱讀:1546 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“UTF-8、UTF-16、UTF-32編碼相互轉(zhuǎn)換的方法”,在日常操作中,相信很多人在UTF-8、UTF-16、UTF-32編碼相互轉(zhuǎn)換的方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”UTF-8、UTF-16、UTF-32編碼相互轉(zhuǎn)換的方法”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

最近在考慮寫一個(gè)可以跨平臺的通用字符串類,首先需要搞定的就是編碼轉(zhuǎn)換問題。

vs默認(rèn)保存代碼文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用帶BOM的UTF-8。
gcc則是UTF-8,有無BOM均可(源代碼的字符集可以由參數(shù)-finput-charset指定)。
那么源代碼可以采用帶BOM的UTF-8來保存。而windows下的unicode是UTF-16編碼;Linux則使用UTF-8或UTF-32。因此不論在哪種系統(tǒng)里,程序在處理字符串時(shí)都需要考慮UTF編碼之間的相互轉(zhuǎn)換。

下面直接貼出算法代碼。算法上我借鑒了秦建輝(http://blog.csdn.net/jhqin)的UnicodeConverter,只是在外面增加了一些泛型處理,讓使用相對簡單。

核心算法(來自UnicodeConverter):

namespace transform  
{  
    /* 
        UTF-32 to UTF-8 
    */  
   
    inline static size_t utf(uint32 src, uint8* des)  
    {  
        if (src == 0) return 0;  
   
        static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };  
        static const uint32 CODE_UP[] =  
        {  
            0x80,           // U+00000000 - U+0000007F  
            0x800,          // U+00000080 - U+000007FF  
            0x10000,        // U+00000800 - U+0000FFFF  
            0x200000,       // U+00010000 - U+001FFFFF  
            0x4000000,      // U+00200000 - U+03FFFFFF  
            0x80000000      // U+04000000 - U+7FFFFFFF  
        };  
   
        size_t i, len = sizeof(CODE_UP) / sizeof(uint32);  
        for(i = 0; i < len; ++i)  
            if (src < CODE_UP[i]) break;  
   
        if (i == len) return 0; // the src is invalid  
   
        len = i + 1;  
        if (des)  
        {  
            for(; i > 0; --i)  
            {  
                des[i] = static_cast<uint8>((src & 0x3F) | 0x80);  
                src >>= 6;  
            }  
            des[0] = static_cast<uint8>(src | PREFIX[len - 1]);  
        }  
        return len;  
    }  
   
    /* 
        UTF-8 to UTF-32 
    */  
   
    inline static size_t utf(const uint8* src, uint32& des)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        uint8 b = *(src++);  
   
        if (b < 0x80)  
        {  
            des = b;  
            return 1;  
        }  
   
        if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid  
   
        size_t len;  
   
        if (b < 0xE0)  
        {  
            des = b & 0x1F;  
            len = 2;  
        }  
        else  
        if (b < 0xF0)  
        {  
            des = b & 0x0F;  
            len = 3;  
        }  
        else  
        if (b < 0xF8)  
        {  
            des = b & 0x07;  
            len = 4;  
        }  
        else  
        if (b < 0xFC)  
        {  
            des = b & 0x03;  
            len = 5;  
        }  
        else  
        {  
            des = b & 0x01;  
            len = 6;  
        }  
   
        size_t i = 1;  
        for (; i < len; ++i)  
        {  
            b = *(src++);  
            if (b < 0x80 || b > 0xBF) return 0; // the src is invalid  
            des = (des << 6) + (b & 0x3F);  
        }  
        return len;  
    }  
   
    /* 
        UTF-32 to UTF-16 
    */  
   
    inline static size_t utf(uint32 src, uint16* des)  
    {  
        if (src == 0) return 0;  
   
        if (src <= 0xFFFF)  
        {  
            if (des) (*des) = static_cast<uint16>(src);  
            return 1;  
        }  
        else  
        if (src <= 0xEFFFF)  
        {  
            if (des)  
            {  
                des[0] = static_cast<uint16>(0xD800 + (src >> 10) - 0x40);  // high  
                des[1] = static_cast<uint16>(0xDC00 + (src & 0x03FF));      // low  
            }  
            return 2;  
        }  
        return 0;  
    }  
   
    /* 
        UTF-16 to UTF-32 
    */  
   
    inline static size_t utf(const uint16* src, uint32& des)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        uint16 w1 = src[0];  
        if (w1 >= 0xD800 && w1 <= 0xDFFF)  
        {  
            if (w1 < 0xDC00)  
            {  
                uint16 w2 = src[1];  
                if (w2 >= 0xDC00 && w2 <= 0xDFFF)  
                {  
                    des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);  
                    return 2;  
                }  
            }  
            return 0; // the src is invalid  
        }  
        else  
        {  
            des = w1;  
            return 1;  
        }  
    }  
}

上面這些算法都是針對單個(gè)字符的,并且是UTF-32和UTF-16/8之間的互轉(zhuǎn)。
通過上面的算法,可以得到UTF-16和UTF-8之間的單字符轉(zhuǎn)換算法:

namespace transform  
{  
    /* 
        UTF-16 to UTF-8 
    */  
   
    inline static size_t utf(uint16 src, uint8* des)  
    {  
        // make utf-16 to utf-32  
        uint32 tmp;  
        if (utf(&src, tmp) != 1) return 0;  
        // make utf-32 to utf-8  
        return utf(tmp, des);  
    }  
   
    /* 
        UTF-8 to UTF-16 
    */  
   
    inline static size_t utf(const uint8* src, uint16& des)  
    {  
        // make utf-8 to utf-32  
        uint32 tmp;  
        size_t len = utf(src, tmp);  
        if (len == 0) return 0;  
        // make utf-32 to utf-16  
        if (utf(tmp, &des) != 1) return 0;  
        return len;  
    }  
}

同樣,通過上面的單字符轉(zhuǎn)換算法,可以得到整個(gè)字符串的轉(zhuǎn)換算法:

namespace transform  
{  
    /* 
        UTF-X: string to string 
    */  
   
    template <typename T>  
    size_t utf(const uint32* src, T* des)   // UTF-32 to UTF-X(8/16)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        size_t num = 0;  
        for(; *src; ++src)  
        {  
            size_t len = utf(*src, des);  
            if (len == 0) break;  
            if (des) des += len;  
            num += len;  
        }  
        if (des) (*des) = 0;  
        return num;  
    }  
   
    template <typename T>  
    size_t utf(const T* src, uint32* des)   // UTF-X(8/16) to UTF-32  
    {  
        if (!src || (*src) == 0) return 0;  
   
        size_t num = 0;  
        while(*src)  
        {  
            uint32 tmp;  
            size_t len = utf(src, tmp);  
            if (len == 0) break;  
            if (des)  
            {  
                (*des) = tmp;  
                ++des;  
            }  
            src += len;  
            num += 1;  
        }  
        if (des) (*des) = 0;  
        return num;  
    }  
   
    template <typename T, typename U>  
    size_t utf(const T* src, U* des)    // UTF-X(8/16) to UTF-Y(16/8)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        size_t num = 0;  
        while(*src)  
        {  
            // make utf-x to ucs4  
            uint32 tmp;  
            size_t len = utf(src, tmp);  
            if (len == 0) break;  
            src += len;  
            // make ucs4 to utf-y  
            len = utf(tmp, des);  
            if (len == 0) break;  
            if (des) des += len;  
            num += len;  
        }  
        if (des) (*des) = 0;  
        return num;  
    }  
}

有了這些之后,我們已經(jīng)可以完整的做UTF-8/16/32之間的相互轉(zhuǎn)換了,但是這些函數(shù)的使用仍然不是很方便。
比如我現(xiàn)在想把一個(gè)UTF-8字符串轉(zhuǎn)換成一個(gè)wchar_t*字符串,我得這樣寫:

const uint8* c = (uint8*)"こんにちわ、世界";  
size_t n = (sizeof(wchar_t) == 2) ?  
    transform::utf(c, (uint16*)0) :  
    transform::utf(c, (uint32*)0);  
wchar_t* s = new wchar_t[n];  
if (sizeof(wchar_t) == 2)  
    transform::utf(c, (uint16*)s);  
else  
    transform::utf(c, (uint32*)s);

這顯然是一件很抽搐的事情,因?yàn)閣char_t在不同的操作系統(tǒng)(windows/linux)里有不同的sizeof長度。
上面的類型強(qiáng)制轉(zhuǎn)換只是為了去適配合適的函數(shù)重載,當(dāng)然我們也可以通過函數(shù)名來區(qū)分這些函數(shù):比如分別叫utf8_to_utf32之類的。但是這改變不了寫if-else來適配長度的問題。

顯然這里可以通過泛型來讓算法更好用。
首先,需要被抽離出來的就是參數(shù)的類型大小和類型本身的依賴關(guān)系:

template <size_t X> struct utf_type;  
template <>         struct utf_type<1> { typedef uint8  type_t; };  
template <>         struct utf_type<2> { typedef uint16 type_t; };  
template <>         struct utf_type<4> { typedef uint32 type_t; };

然后,實(shí)現(xiàn)一個(gè)簡單的check算法,這樣后面就可以利用SFINAE的技巧篩選出合適的算法函數(shù):

template <size_t X, typename T>  
struct check  
{  
    static const bool value =  
        ((sizeof(T) == sizeof(typename utf_type<X>::type_t)) && !is_pointer<T>::value);  
};

下面我們需要一個(gè)detail,即泛型適配的細(xì)節(jié)。從上面的算法函數(shù)參數(shù)中,我們可以很容易的觀察出一些規(guī)律:
只要是由大向小轉(zhuǎn)換(比如32->16,或16->8)的,其對外接口可以抽象成這兩種形式:

type_t utf(T src, U* des)  
type_t utf(const T* src, U* des)

而由小向大的轉(zhuǎn)換,則是下面這兩種形式:

type_t utf(const T* src, U& des)  
type_t utf(const T* src, U* des)

再加上第二個(gè)指針參數(shù)是可以給一個(gè)默認(rèn)值(空指針)的,因此適配的泛型類就可以寫成這樣:

template <size_t X, size_t Y, bool = (X > Y), bool = (X != Y)>  
struct detail;  
   
/* 
    UTF-X(32/16) to UTF-Y(16/8) 
*/  
   
template <size_t X, size_t Y>  
struct detail<X, Y, true, true>  
{  
    typedef typename utf_type<X>::type_t src_t;  
    typedef typename utf_type<Y>::type_t des_t;  
   
    template <typename T, typename U>  
    static typename enable_if<check<X, T>::value && check<Y, U>::value,  
    size_t>::type_t utf(T src, U* des)  
    {  
        return transform::utf((src_t)(src), (des_t*)(des));  
    }  
   
    template <typename T>  
    static typename enable_if<check<X, T>::value,  
    size_t>::type_t utf(T src)  
    {  
        return transform::utf((src_t)(src), (des_t*)(0));  
    }  
   
    template <typename T, typename U>  
    static typename enable_if<check<X, T>::value && check<Y, U>::value,  
    size_t>::type_t utf(const T* src, U* des)  
    {  
        return transform::utf((const src_t*)(src), (des_t*)(des));  
    }  
   
    template <typename T>  
    static typename enable_if<check<X, T>::value,  
    size_t>::type_t utf(const T* src)  
    {  
        return transform::utf((src_t)(src), (des_t*)(0));  
    }  
};  
   
/* 
    UTF-X(16/8) to UTF-Y(32/16) 
*/  
   
template <size_t X, size_t Y>  
struct detail<X, Y, false, true>  
{  
    typedef typename utf_type<X>::type_t src_t;  
    typedef typename utf_type<Y>::type_t des_t;  
   
    template <typename T, typename U>  
    static typename enable_if<check<X, T>::value && check<Y, U>::value,  
    size_t>::type_t utf(const T* src, U& des)  
    {  
        des_t tmp; // for disable the warning strict-aliasing from gcc 4.4  
        size_t ret = transform::utf((const src_t*)(src), tmp);  
        des = tmp;  
        return ret;  
    }  
   
    template <typename T, typename U>  
    static typename enable_if<check<X, T>::value && check<Y, U>::value,  
    size_t>::type_t utf(const T* src, U* des)  
    {  
        return transform::utf((const src_t*)(src), (des_t*)(des));  
    }  
   
    template <typename T>  
    static typename enable_if<check<X, T>::value,  
    size_t>::type_t utf(const T* src)  
    {  
        return transform::utf((const src_t*)(src), (des_t*)(0));  
    }  
};

最后的外敷類收尾就可以相當(dāng)?shù)暮唵危?/p>

template <typename T, typename U>  
struct converter  
    : detail<sizeof(T), sizeof(U)>  
{};

通過上面的detail,我們也可以很輕松的寫出一個(gè)通過指定8、16這些數(shù)字,來控制選擇哪些轉(zhuǎn)換算法的外敷模板。
有了converter,同類型的需求(指UTF-8轉(zhuǎn)wchar_t)就可以變得輕松愉快很多:

const char* c = "こんにちわ、世界";  
wstring s;  
size_t n; wchar_t w;  
while (!!(n = converter<char, wchar_t>::utf(c, w))) // 這里的!!是為了屏蔽gcc的警告  
{  
    s.push_back(w);  
    c += n;  
}  
FILE* fp = fopen("test_converter.txt", "wb");  
fwrite(s.c_str(), sizeof(wchar_t), s.length(), fp);  
fclose(fp);

上面這一小段代碼是將一段UTF-8的文字逐字符轉(zhuǎn)換為wchar_t,并一個(gè)個(gè)push_back到wstring里,最后把轉(zhuǎn)換完畢的字符串輸出到test_converter.txt里。


其實(shí)上面的泛型還是顯得累贅了。為什么不直接在transform::utf上使用泛型參數(shù)呢?
一開始只想到上面那個(gè)方法,自然是由于慣性的想要手動(dòng)指定如何轉(zhuǎn)換編碼的緣故,比如最開始的想法,是想做成類似這樣的模板:utf<8, 32>(s1, s2),指定兩個(gè)數(shù)字,來決定輸入和輸出的格式。

后來發(fā)現(xiàn),直接指定字符串/字符的類型或許更加直接些。
現(xiàn)在回頭再看看,其實(shí)轉(zhuǎn)換所需要的字長(8、16、32)已經(jīng)在參數(shù)的類型中指定了:8bits的char或byte類型肯定不會(huì)是用來存放UTF-32的嘛。。
所以只需要把上面核心算法的參數(shù)泛型化就可以了。這時(shí)代碼就會(huì)寫成下面這個(gè)樣子:

namespace transform  
{  
    namespace private_  
    {  
        template <size_t X> struct utf_type;  
        template <>         struct utf_type<1> { typedef uint8  type_t; };  
        template <>         struct utf_type<2> { typedef uint16 type_t; };  
        template <>         struct utf_type<4> { typedef uint32 type_t; };  
   
        template <typename T, size_t X>  
        struct check  
        {  
            static const bool value =  
                ((sizeof(T) == sizeof(typename utf_type<X>::type_t)) && !is_pointer<T>::value);  
        }  
    }  
   
    using namespace transform::private_;  
   
    /* 
        UTF-32 to UTF-8 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 4>::value && check<U, 1>::value,  
    size_t>::type_t utf(T src, U* des)  
    {  
        if (src == 0) return 0;  
   
        static const byte PREFIX[] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };  
        static const uint32 CODE_UP[] =  
        {  
            0x80,           // U+00000000 - U+0000007F  
            0x800,          // U+00000080 - U+000007FF  
            0x10000,        // U+00000800 - U+0000FFFF  
            0x200000,       // U+00010000 - U+001FFFFF  
            0x4000000,      // U+00200000 - U+03FFFFFF  
            0x80000000      // U+04000000 - U+7FFFFFFF  
        };  
   
        size_t i, len = sizeof(CODE_UP) / sizeof(uint32);  
        for(i = 0; i < len; ++i)  
            if (src < CODE_UP[i]) break;  
   
        if (i == len) return 0; // the src is invalid  
   
        len = i + 1;  
        if (des)  
        {  
            for(; i > 0; --i)  
            {  
                des[i] = static_cast<U>((src & 0x3F) | 0x80);  
                src >>= 6;  
            }  
            des[0] = static_cast<U>(src | PREFIX[len - 1]);  
        }  
        return len;  
    }  
   
    /* 
        UTF-8 to UTF-32 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 1>::value && check<U, 4>::value,  
    size_t>::type_t utf(const T* src, U& des)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        uint8 b = *(src++);  
   
        if (b < 0x80)  
        {  
            des = b;  
            return 1;  
        }  
   
        if (b < 0xC0 || b > 0xFD) return 0; // the src is invalid  
   
        size_t len;  
   
        if (b < 0xE0)  
        {  
            des = b & 0x1F;  
            len = 2;  
        }  
        else  
        if (b < 0xF0)  
        {  
            des = b & 0x0F;  
            len = 3;  
        }  
        else  
        if (b < 0xF8)  
        {  
            des = b & 0x07;  
            len = 4;  
        }  
        else  
        if (b < 0xFC)  
        {  
            des = b & 0x03;  
            len = 5;  
        }  
        else  
        {  
            des = b & 0x01;  
            len = 6;  
        }  
   
        size_t i = 1;  
        for (; i < len; ++i)  
        {  
            b = *(src++);  
            if (b < 0x80 || b > 0xBF) return 0; // the src is invalid  
            des = (des << 6) + (b & 0x3F);  
        }  
        return len;  
    }  
   
    /* 
        UTF-32 to UTF-16 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 4>::value && check<U, 2>::value,  
    size_t>::type_t utf(T src, U* des)  
    {  
        if (src == 0) return 0;  
   
        if (src <= 0xFFFF)  
        {  
            if (des) (*des) = static_cast<U>(src);  
            return 1;  
        }  
        else  
        if (src <= 0xEFFFF)  
        {  
            if (des)  
            {  
                des[0] = static_cast<U>(0xD800 + (src >> 10) - 0x40);  // high  
                des[1] = static_cast<U>(0xDC00 + (src & 0x03FF));      // low  
            }  
            return 2;  
        }  
        return 0;  
    }  
   
    /* 
        UTF-16 to UTF-32 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 2>::value && check<U, 4>::value,  
    size_t>::type_t utf(const T* src, U& des)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        uint16 w1 = src[0];  
        if (w1 >= 0xD800 && w1 <= 0xDFFF)  
        {  
            if (w1 < 0xDC00)  
            {  
                uint16 w2 = src[1];  
                if (w2 >= 0xDC00 && w2 <= 0xDFFF)  
                {  
                    des = (w2 & 0x03FF) + (((w1 & 0x03FF) + 0x40) << 10);  
                    return 2;  
                }  
            }  
            return 0; // the src is invalid  
        }  
        else  
        {  
            des = w1;  
            return 1;  
        }  
    }  
   
    /* 
        UTF-16 to UTF-8 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 2>::value && check<U, 1>::value,  
    size_t>::type_t utf(T src, U* des)  
    {  
        // make utf-16 to utf-32  
        uint32 tmp;  
        if (utf(&src, tmp) != 1) return 0;  
        // make utf-32 to utf-8  
        return utf(tmp, des);  
    }  
   
    /* 
        UTF-8 to UTF-16 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 1>::value && check<U, 2>::value,  
    size_t>::type_t utf(const T* src, U& des)  
    {  
        // make utf-8 to utf-32  
        uint32 tmp;  
        size_t len = utf(src, tmp);  
        if (len == 0) return 0;  
        // make utf-32 to utf-16  
        if (utf(tmp, &des) != 1) return 0;  
        return len;  
    }  
   
    /* 
        UTF-X: string to string 
    */  
   
    template <typename T, typename U>  
    typename enable_if<check<T, 4>::value && (check<U, 1>::value || check<U, 2>::value),  
    size_t>::type_t utf(const T* src, U* des)   // UTF-32 to UTF-X(8/16)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        size_t num = 0;  
        for(; *src; ++src)  
        {  
            size_t len = utf(*src, des);  
            if (len == 0) break;  
            if (des) des += len;  
            num += len;  
        }  
        if (des) (*des) = 0;  
        return num;  
    }  
   
    template <typename T, typename U>  
    typename enable_if<(check<T, 1>::value || check<T, 2>::value) && check<U, 4>::value,  
    size_t>::type_t utf(const T* src, U* des)   // UTF-X(8/16) to UTF-32  
    {  
        if (!src || (*src) == 0) return 0;  
   
        size_t num = 0;  
        while(*src)  
        {  
            uint32 tmp;  
            size_t len = utf(src, tmp);  
            if (len == 0) break;  
            if (des)  
            {  
                (*des) = tmp;  
                ++des;  
            }  
            src += len;  
            num += 1;  
        }  
        if (des) (*des) = 0;  
        return num;  
    }  
   
    template <typename T, typename U>  
    typename enable_if<(check<T, 1>::value && check<U, 2>::value) ||  
                       (check<T, 2>::value && check<U, 1>::value),  
    size_t>::type_t utf(const T* src, U* des)    // UTF-X(8/16) to UTF-Y(16/8)  
    {  
        if (!src || (*src) == 0) return 0;  
   
        size_t num = 0;  
        while(*src)  
        {  
            // make utf-x to utf-32  
            uint32 tmp;  
            size_t len = utf(src, tmp);  
            if (len == 0) break;  
            src += len;  
            // make utf-32 to utf-y  
            len = utf(tmp, des);  
            if (len == 0) break;  
            if (des) des += len;  
            num += len;  
        }  
        if (des) (*des) = 0;  
        return num;  
    }  
}

這樣用起來就更加簡單了:

const char* c = "你好世界";  
size_t n = nx::transform::utf(c, (wchar_t*)0);

完整代碼請參考:
https://code.google.com/p/nixy/source/browse/trunk/nixycore/string/transform.h

到此,關(guān)于“UTF-8、UTF-16、UTF-32編碼相互轉(zhuǎn)換的方法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI