UTF-8、UTF-16、UTF-32编码的相互转换

本文探讨了在跨平台开发中,如何处理不同编码之间的转换,尤其是UTF-8、UTF-16和UTF-32。作者通过借鉴UnicodeConverter,创建了一个泛型版本的转换算法,简化了编码转换的过程。代码示例展示了如何将UTF-8文字转换为宽字符并存储到wstring中,强调了利用类型大小来确定编码格式的思路,提供了泛型化的转换方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题。

vs默认保存代码文件,使用的是本地code(中文即GBK,日文即Shift-JIS),也可以使用带BOM的UTF-8。
gcc则是UTF-8,有无BOM均可(源代码的字符集可以由参数-finput-charset指定)。
那么源代码可以采用带BOM的UTF-8来保存。而windows下的unicode是UTF-16编码;linux则使用UTF-8或UTF-32。因此不论在哪种系统里,程序在处理字符串时都需要考虑UTF编码之间的相互转换。

下面直接贴出算法代码。算法上我借鉴了秦建辉(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;
        }
    }
}

上面这些算法都是针对单个字符的,并且是UTF-32和UTF-16/8之间的互转。
通过上面的算法,可以得到UTF-16和UTF-8之间的单字符转换算法:

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;
    }
}

同样,通过上面的单字符转换算法,可以得到整个字符串的转换算法:

namespace transform
{
    /*
        UTF-X: string to string
    */
 
    template <typename T>
    size_t utf(const uint32* src, T* des)   // UTF-32 to UTF-X(8/1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值