C++将ASII转义的unicode字符串转换为utf8字符串

C++ convert ASII escaped unicode string into utf8 string

本文关键字:字符串 转换 utf8 unicode ASII 转义 C++      更新时间:2023-10-16

我需要读取一个带有unicode转义的标准ascii风格字符串,并将其转换为包含utf8编码的等效字符串的std::字符串。因此,例如"\u03a0"(一个包含6个字符的std::字符串)应该转换为包含两个字符的std::字符串,分别为0xce和0xa0。

如果有一个使用icu或boost的简单答案,我会非常高兴,但我一直找不到。

(这类似于将Unicode字符串转换为转义的ASCII字符串,但注意,我最终需要达到UTF8编码。如果我们可以使用Unicode作为中间步骤,那就好了。)

试试这样的东西:

std::string to_utf8(uint32_t cp)
{
    /*
    if using C++11 or later, you can do this:
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
    return conv.to_bytes( (char32_t)cp );
    Otherwise...
    */
    std::string result;
    int count;
    if (cp <= 0x007F)
        count = 1
    else if (cp <= 0x07FF)
        count = 2;
    else if (cp <= 0xFFFF)
        count = 3;
    else if (cp <= 0x10FFFF)
        count = 4;
    else
        return result; // or throw an exception
    result.resize(count);
    if (count > 1)
    {
        for (int i = count-1; i > 0; --i)
        {
            result[i] = (char) (0x80 | (cp & 0x3F));
            cp >>= 6;
        }
        for (int i = 0; i < count; ++i)
            cp |= (1 << (7-i));
    }
    result[0] = (char) cp;
    return result;
}

std::string str = ...; // "\u03a0"
std::string::size_type startIdx = 0;
do
{
    startIdx = str.find("\u", startIdx);
    if (startIdx == std::string::npos) break;
    std::string::size_type endIdx = str.find_first_not_of("0123456789abcdefABCDEF", startIdx+2);
    if (endIdx == std::string::npos) break;
    std::string tmpStr = str.substr(startIdx+2, endIdx-(startIdx+2));
    std::istringstream iss(tmpStr);
    uint32_t cp;
    if (iss >> std::hex >> cp)
    {
        std::string utf8 = to_utf8(cp);
        str.replace(startIdx, 2+tmpStr.length(), utf8);
        startIdx += utf8.length();
    }
    else
        startIdx += 2;
}
while (true);

(\u03a0是希腊大写字母PI的Unicode代码点,其UTF-8编码为0xCE 0xA0)

您需要:

  1. 从字符串"\u03a0"中获取数字0x03a0:去掉反斜杠和u,将03a0解析为十六进制,并将其转换为wchar_t。重复,直到你得到一个(宽)字符串
  2. 将0x3a0转换为UTF-8。C++11有一个代码cvt_utf8,它可能会有所帮助

我的解决方案:

convert_unicode_escape_sequences(str)
input: "u043fu0440u0438u0432u0435u0442"
output: "привет"

用于wchar/chars转换的Boost:

#include <boost/locale/encoding_utf.hpp>
using boost::locale::conv::utf_to_utf;
inline uint8_t get_uint8(uint8_t h, uint8_t l)
{
    uint8_t ret;
    if (h - '0' < 10)
        ret = h - '0';
    else if (h - 'A' < 6)
        ret = h - 'A' + 0x0A;
    else if (h - 'a' < 6)
        ret = h - 'a' + 0x0A;
    ret = ret << 4;
    if (l - '0' < 10)
        ret |= l - '0';
    else if (l - 'A' < 6)
        ret |= l - 'A' + 0x0A;
    else if (l - 'a' < 6)
        ret |= l - 'a' + 0x0A;
    return  ret;
}
std::string wstring_to_utf8(const std::wstring& str)
{
    return utf_to_utf<char>(str.c_str(), str.c_str() + str.size());
}
std::string convert_unicode_escape_sequences(const std::string& source)
{
    std::wstring ws; ws.reserve(source.size());
    std::wstringstream wis(ws);
    auto s = source.begin();
    while (s != source.end())
    {
        if (*s == '')
        {
            if (std::distance(s, source.end()) > 5)
            {
                if (*(s + 1) == 'u')
                {
                    unsigned int v = get_uint8(*(s + 2), *(s + 3)) << 8;
                    v |= get_uint8(*(s + 4), *(s + 5));
                    s += 6;
                    wis << boost::numeric_cast<wchar_t>(v);
                    continue;
                }
            }
        }
        wis << wchar_t(*s);
        s++;
    }
    return wstring_to_utf8(wis.str());
}