C++ UTF8和UTF16互转代码

简介

1、这段代码只考虑在小端序情况下的转换(一般的机器都是的)。
2、这段代码需要C++11的支持(只是用到了u16string),如果不支持,可以添加下面代码

typedef uint16_t char16_t;
typedef std::basic_string<char16_t>

utfconvert.h

#ifndef __UTFCONVERT_H__
#define __UTFCONVERT_H__
#include <string>

// 从UTF16编码字符串构建,需要带BOM标记
std::string utf16_to_utf8(const std::u16string& u16str);

// 从UTF16 LE编码的字符串创建
std::string utf16le_to_utf8(const std::u16string& u16str);

// 从UTF16BE编码字符串创建
std::string utf16be_to_utf8(const std::u16string& u16str);

// 获取转换为UTF-16 LE编码的字符串
std::u16string utf8_to_utf16le(const std::string& u8str, bool addbom = false, bool* ok = NULL);

// 获取转换为UTF-16 BE的字符串
std::u16string utf8_to_utf16be(const std::string& u8str, bool addbom = false, bool* ok = NULL);

#endif //! __UTFCONVERT_H__

utfconvert.cpp

#include "utfconvert.h"

#include <stdint.h>
#ifdef __GNUC__
#include <endian.h>
#endif // __GNUC__

static inline uint16_t byteswap_ushort(uint16_t number)
{
#if defined(_MSC_VER) && _MSC_VER > 1310
    return _byteswap_ushort(number);
#elif defined(__GNUC__)
    return __builtin_bswap16(number);
#else
    return (number >> 8) | (number << 8);
#endif
}

////////////////////////////////////////
//     以下转换都是在小端序下进行     //
////////////////////////////////////////

// 从UTF16编码字符串构建,需要带BOM标记
std::string utf16_to_utf8(const std::u16string& u16str)
{
    if (u16str.empty()){ return std::string(); }
    //Byte Order Mark
    char16_t bom = u16str[0];
    switch (bom){
    case 0xFEFF:    //Little Endian
        return utf16le_to_utf8(u16str);
        break;
    case 0xFFFE:    //Big Endian
        return utf16be_to_utf8(u16str);
        break;
    default:
        return std::string();
    }
}

// 从UTF16 LE编码的字符串创建
std::string utf16le_to_utf8(const std::u16string& u16str)
{
    if (u16str.empty()){ return std::string(); }
    const char16_t* p = u16str.data();
    std::u16string::size_type len = u16str.length();
    if (p[0] == 0xFEFF){
        p += 1; //带有bom标记,后移
        len -= 1;
    }

    // 开始转换
    std::string u8str;
    u8str.reserve(len * 3);

    char16_t u16char;
    for (std::u16string::size_type i = 0; i < len; ++i){
        // 这里假设是在小端序下(大端序不适用)
        u16char = p[i];

        // 1字节表示部分
        if (u16char < 0x0080){
            // u16char <= 0x007f
            // U- 0000 0000 ~ 0000 07ff : 0xxx xxxx
            u8str.push_back((char)(u16char & 0x00FF));  // 取低8bit
            continue;
        }
        // 2 字节能表示部分
        if (u16char >= 0x0080 && u16char <= 0x07FF){
            // * U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
            u8str.push_back((char)(((u16char >> 6) & 0x1F) | 0xC0));
            u8str.push_back((char)((u16char & 0x3F) | 0x80));
            continue;
        }
        // 代理项对部分(4字节表示)
        if (u16char >= 0xD800 && u16char <= 0xDBFF) {
            // * U-00010000 - U-001FFFFF: 1111 0xxx 10xxxxxx 10xxxxxx 10xxxxxx
            uint32_t highSur = u16char;
            uint32_t lowSur = p[++i];
            // 从代理项对到UNICODE代码点转换
            // 1、从高代理项减去0xD800,获取有效10bit
            // 2、从低代理项减去0xDC00,获取有效10bit
            // 3、加上0x10000,获取UNICODE代码点值
            uint32_t codePoint = highSur - 0xD800;
            codePoint <<= 10;
            codePoint |= lowSur - 0xDC00;
            codePoint += 0x10000;
            // 转为4字节UTF8编码表示
            u8str.push_back((char)((codePoint >> 18) | 0xF0));
            u8str.push_back((char)(((codePoint >> 12) & 0x3F) | 0x80));
            u8str.push_back((char)(((codePoint >> 06) & 0x3F) | 0x80));
            u8str.push_back((char)((codePoint & 0x3F) | 0x80));
            continue;
        }
        // 3 字节表示部分
        {
            // * U-0000E000 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
            u8str.push_back((char)(((u16char >> 12) & 0x0F) | 0xE0));
            u8str.push_back((char)(((u16char >> 6) & 0x3F) | 0x80));
            u8str.push_back((char)((u16char & 0x3F) | 0x80));
            continue;
        }
    }

    return u8str;
}

// 从UTF16BE编码字符串创建
std::string utf16be_to_utf8(const std::u16string& u16str)
{
    if (u16str.empty()){ return std::string(); }
    const char16_t* p = u16str.data();
    std::u16string::size_type len = u16str.length();
    if (p[0] == 0xFEFF){
        p += 1; //带有bom标记,后移
        len -= 1;
    }

    // 开始转换
    std::string u8str;
    u8str.reserve(len * 2);
    char16_t u16char;   //u16le 低字节存低位,高字节存高位
    for (std::u16string::size_type i = 0; i < len; ++i) {
        // 这里假设是在小端序下(大端序不适用)
        u16char = p[i];
        // 将大端序转为小端序
        u16char = byteswap_ushort(u16char);

        // 1字节表示部分
        if (u16char < 0x0080) {
            // u16char <= 0x007f
            // U- 0000 0000 ~ 0000 07ff : 0xxx xxxx
            u8str.push_back((char)(u16char & 0x00FF));
            continue;
        }
        // 2 字节能表示部分
        if (u16char >= 0x0080 && u16char <= 0x07FF) {
            // * U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
            u8str.push_back((char)(((u16char >> 6) & 0x1F) | 0xC0));
            u8str.push_back((char)((u16char & 0x3F) | 0x80));
            continue;
        }
        // 代理项对部分(4字节表示)
        if (u16char >= 0xD800 && u16char <= 0xDBFF) {
            // * U-00010000 - U-001FFFFF: 1111 0xxx 10xxxxxx 10xxxxxx 10xxxxxx
            uint32_t highSur = u16char;
            uint32_t lowSur = byteswap_ushort(p[++i]);
            // 从代理项对到UNICODE代码点转换
            // 1、从高代理项减去0xD800,获取有效10bit
            // 2、从低代理项减去0xDC00,获取有效10bit
            // 3、加上0x10000,获取UNICODE代码点值
            uint32_t codePoint = highSur - 0xD800;
            codePoint <<= 10;
            codePoint |= lowSur - 0xDC00;
            codePoint += 0x10000;
            // 转为4字节UTF8编码表示
            u8str.push_back((char)((codePoint >> 18) | 0xF0));
            u8str.push_back((char)(((codePoint >> 12) & 0x3F) | 0x80));
            u8str.push_back((char)(((codePoint >> 06) & 0x3F) | 0x80));
            u8str.push_back((char)((codePoint & 0x3F) | 0x80));
            continue;
        }
        // 3 字节表示部分
        {
            // * U-0000E000 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
            u8str.push_back((char)(((u16char >> 12) & 0x0F) | 0xE0));
            u8str.push_back((char)(((u16char >> 6) & 0x3F) | 0x80));
            u8str.push_back((char)((u16char & 0x3F) | 0x80));
            continue;
        }
    }
    return u8str;
}

// 获取转换为UTF-16 LE编码的字符串
std::u16string utf8_to_utf16le(const std::string& u8str, bool addbom, bool* ok)
{
    std::u16string u16str;
    u16str.reserve(u8str.size());
    if (addbom) {
        u16str.push_back(0xFEFF);   //bom (字节表示为 FF FE)
    }
    std::string::size_type len = u8str.length();

    const unsigned char* p = (unsigned char*)(u8str.data());
    // 判断是否具有BOM(判断长度小于3字节的情况)
    if (len > 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF){
        p += 3;
        len -= 3;
    }

    bool is_ok = true;
    // 开始转换
    for (std::string::size_type i = 0; i < len; ++i) {
        uint32_t ch = p[i]; // 取出UTF8序列首字节
        if ((ch & 0x80) == 0) {
            // 最高位为0,只有1字节表示UNICODE代码点
            u16str.push_back((char16_t)ch);
            continue;
        }
        switch (ch & 0xF0)
        {
        case 0xF0: // 4 字节字符, 0x10000 到 0x10FFFF
        {
            uint32_t c2 = p[++i];
            uint32_t c3 = p[++i];
            uint32_t c4 = p[++i];
            // 计算UNICODE代码点值(第一个字节取低3bit,其余取6bit)
            uint32_t codePoint = ((ch & 0x07U) << 18) | ((c2 & 0x3FU) << 12) | ((c3 & 0x3FU) << 6) | (c4 & 0x3FU);
            if (codePoint >= 0x10000)
            {
                // 在UTF-16中 U+10000 到 U+10FFFF 用两个16bit单元表示, 代理项对.
                // 1、将代码点减去0x10000(得到长度为20bit的值)
                // 2、high 代理项 是将那20bit中的高10bit加上0xD800(110110 00 00000000)
                // 3、low  代理项 是将那20bit中的低10bit加上0xDC00(110111 00 00000000)
                codePoint -= 0x10000;
                u16str.push_back((char16_t)((codePoint >> 10) | 0xD800U));
                u16str.push_back((char16_t)((codePoint & 0x03FFU) | 0xDC00U));
            }
            else
            {
                // 在UTF-16中 U+0000 到 U+D7FF 以及 U+E000 到 U+FFFF 与Unicode代码点值相同.
                // U+D800 到 U+DFFF 是无效字符, 为了简单起见,这里假设它不存在(如果有则不编码)
                u16str.push_back((char16_t)codePoint);
            }
        }
        break;
        case 0xE0: // 3 字节字符, 0x800 到 0xFFFF
        {
            uint32_t c2 = p[++i];
            uint32_t c3 = p[++i];
            // 计算UNICODE代码点值(第一个字节取低4bit,其余取6bit)
            uint32_t codePoint = ((ch & 0x0FU) << 12) | ((c2 & 0x3FU) << 6) | (c3 & 0x3FU);
            u16str.push_back((char16_t)codePoint);
        }
        break;
        case 0xD0: // 2 字节字符, 0x80 到 0x7FF
        case 0xC0:
        {
            uint32_t c2 = p[++i];
            // 计算UNICODE代码点值(第一个字节取低5bit,其余取6bit)
            uint32_t codePoint = ((ch & 0x1FU) << 12) | ((c2 & 0x3FU) << 6);
            u16str.push_back((char16_t)codePoint);
        }
        break;
        default:    // 单字节部分(前面已经处理,所以不应该进来)
            is_ok = false;
            break;
        }
    }
    if (ok != NULL) { *ok = is_ok; }

    return u16str;
}

// 获取转换为UTF-16 BE的字符串
std::u16string utf8_to_utf16be(conststd::string& u8str, bool addbom, bool* ok)
{
    // 先获取utf16le编码字符串
    std::u16string u16str = utf8_to_utf16le(u8str, addbom, ok);
    // 将小端序转换为大端序
    for (size_t i = 0; i < u16str.size(); ++i) {
        u16str[i] = byteswap_ushort(u16str[i]);
    }
    return u16str;
}

http://www.cnblogs.com/oloroso/p/6801076.html

时间: 2024-10-03 02:01:58

C++ UTF8和UTF16互转代码的相关文章

跨平台Unicode与UTF8互转代码

参考来源:http://blog.csdn.net/flying8127/article/details/1598521 在原来原基础上,将代码整理,并加强安全性. 并按照WindowsAPI设计, 添加输出缓冲长度探测功能 当OutUTFString为NULL时, 可以进行输出的UTF8字符串长度探测 1: uint32 UniCharToUTF8(wchar_t UniChar, char *OutUTFString) 2: { 3:   4: uint32 UTF8CharLength =

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

最近在考虑写一个可以跨平台的通用字符串类,首先需要搞定的就是编码转换问题. 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编码之间

聊聊计算机中的编码(Unicode,GBK,ASCII,utf8,utf16,ISO8859-1等)

作为一个程序员,一个中国的程序员,想来“乱码”问题基本上都遇到过,也为之头疼过.出现乱码问题的根本原因是编码与解码使用了不同而且不兼容的“标准”,在国内一般出现在中文的编解码过程中. 我们平时常见的编码有Unicode,GBK,ASCII,utf8,utf16,ISO8859-1等,弄清这些编码之间的关系,就不难理解“乱码”出现的原因以及解决办法. 所谓字符集编码其实就是将字符(包括英文字符.特殊符号,控制字符,数字,汉子等)与计算机中的一个数字(二进制存储)一一对应起来,用这个数字来表示该字符

简单几句话总结Unicode,UTF-8和UTF-16

概念 先说一说基本的概念,这包括什么是Unicode,什么是UTF-8,什么是UTF-16. Unicode,UTF-8,UTF-16完整的说明请参考Wiki(Unicode,UTF-8,UTF-16).用比较简单的话来说就是,Unicode定义了所有可以用来表示字符的数值集合(称之为Code Point).UTF-8和UTF-16等UTF标准定义了这些数值和字符的映射关系. UTF-8 优势 UTF-8最大的优势是,没有字节序的概念.所以特别适合用于字符串的网络数据传输,不用考虑大小端问题.

unicode 、utf-8 、utf-16、ascii 、gbk 、gb2312 的区别

很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的,于是他们把这称为"字节".再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去.他们看到这样是好的,于是它们就这机器称为"计算机". 开始计算机只在美国用.八位的字节一共可以组合出256(2的8次方)种不同的状态. 他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端.打印机遇上约定

ASCII、Unicode、UTF-8、UTF-16、GBK、GB2312、ANSI等编码方式简析

ASCII.Unicode.UTF-8.UTF-16.GBK.GB2312.ANSI等编码方式简析 序言 从各种字节编码方法中,能看到那个计算机发展的洪荒时期的影子. ASCII ASCII码有标准ASCII码和拓展ASCII码之分,这里分开讲解. 标准ASCII码 标准ASCII码占用一个字节,但是只用了后7位,第一位是0.一个字节本来可以表示256种不同情况,而由此ASCII码只有128种符号.这128种符号包括英文26字母的大小写.数字0-9.32个不可打印的控制字母.符号(就是我们在键盘

编码格式简介(ANSI、GBK、GB2312、UTF-8、UTF-16、GB18030和 UNICODE)

很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物,他们把这称为”字节”.再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去,他们就把这机器称为”计算机”. 开始计算机只在美国用.八位的字节一共可以组合出256(2的8次方)种不同的状态.他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端.打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作.遇上 00×10, 终端就换行,遇上0×

所谓编码--泛谈ASCII、Unicode、UTF8、UTF16、UCS-2等编码格式

最近在看nodejs的源码,看到stream的实现里面满地都是encoding,不由想起以前看过的一篇文章--在前面的随笔里面有提到过--阮一峰老师的<字符编码笔记:ASCII,Unicode和UTF-8>. 好的文章有一个好处,你每次看都会有新的收获,它就像一款拼图,你每次看都能收获几块碎片,补齐之前的认识:而好文章与拼图不一样的是,好文章是一块无垠的世界,当你不愿局限于当前的眼界的时候,你可以主动走出去,外面要更宽广.更精彩的多. 闲话说到这,开始聊聊所谓的编码. 大家都知道,计算机只认识

字符编码终极笔记:ASCII、Unicode、UTF-8、UTF-16、UCS、BOM、Endian

很详细很不错,转过来学习一下: 转载自:http://www.cnblogs.com/lidabo/archive/2013/11/27/3446518.html 1.字符编码.内码,顺带介绍汉字编码 字符必须编码后才能被计算机处理.计算机使用的缺省编码方式就是计算机的内码.早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5. GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号.汉字区的内码范围