如何将UTF-8的std::string转换为UTF-16的std::wstring?

34

如果我有一个UTF-8的std::string,我如何将其转换为UTF-16的std::wstring?实际上,我想比较两个波斯语单词。


1
请参考以下链接:https://dev59.com/TXVC5IYBdhLWcg3w51ry 等等。 - Mark Ransom
可能是以下问题的重复:如何在C++中比较像波斯语这样的UTF8字符串? 或者 这个 - Kerrek SB
6个回答

54

以下是使用C++11的方法:

std::string str = "your string in utf8";
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>> converter;
std::wstring wstr = converter.from_bytes(str);

以下是您需要的标题:

#include <iostream>
#include <string>
#include <locale>
#include <codecvt>

这里提供一个更完整的例子: http://en.cppreference.com/w/cpp/locale/wstring_convert/from_bytes


1
非常好的答案,谢谢!但是请遵循cppreference.com上的示例。在除Windows以外的操作系统上,wchar_t不是16位类型。您需要使用char16_t代替。 - Cris Luengo
1
@CrisLuengo 谢谢!我更新了答案,使用了 char16_t - Yuchen
4
不适用于在 Lubuntu 16.04 上使用 g++ 6.2 或 clang++ 3.8,无法正常工作。 - user2918461
7
很遗憾,这个在C++17中已被弃用。https://mariusbancila.ro/blog/2018/07/05/c17-removed-and-deprecated-features/ - Andrey Belykh

31

以下是一段代码。仅进行了轻微的测试,可能还有一些可以改进的地方。调用此函数可将UTF-8字符串转换为UTF-16 wstring。如果它认为输入字符串不是UTF-8,则会抛出异常,否则返回相应的UTF-16 wstring。

std::wstring utf8_to_utf16(const std::string& utf8)
{
    std::vector<unsigned long> unicode;
    size_t i = 0;
    while (i < utf8.size())
    {
        unsigned long uni;
        size_t todo;
        bool error = false;
        unsigned char ch = utf8[i++];
        if (ch <= 0x7F)
        {
            uni = ch;
            todo = 0;
        }
        else if (ch <= 0xBF)
        {
            throw std::logic_error("not a UTF-8 string");
        }
        else if (ch <= 0xDF)
        {
            uni = ch&0x1F;
            todo = 1;
        }
        else if (ch <= 0xEF)
        {
            uni = ch&0x0F;
            todo = 2;
        }
        else if (ch <= 0xF7)
        {
            uni = ch&0x07;
            todo = 3;
        }
        else
        {
            throw std::logic_error("not a UTF-8 string");
        }
        for (size_t j = 0; j < todo; ++j)
        {
            if (i == utf8.size())
                throw std::logic_error("not a UTF-8 string");
            unsigned char ch = utf8[i++];
            if (ch < 0x80 || ch > 0xBF)
                throw std::logic_error("not a UTF-8 string");
            uni <<= 6;
            uni += ch & 0x3F;
        }
        if (uni >= 0xD800 && uni <= 0xDFFF)
            throw std::logic_error("not a UTF-8 string");
        if (uni > 0x10FFFF)
            throw std::logic_error("not a UTF-8 string");
        unicode.push_back(uni);
    }
    std::wstring utf16;
    for (size_t i = 0; i < unicode.size(); ++i)
    {
        unsigned long uni = unicode[i];
        if (uni <= 0xFFFF)
        {
            utf16 += (wchar_t)uni;
        }
        else
        {
            uni -= 0x10000;
            utf16 += (wchar_t)((uni >> 10) + 0xD800);
            utf16 += (wchar_t)((uni & 0x3FF) + 0xDC00);
        }
    }
    return utf16;
}

2
谢谢您!谢谢您!它起作用了……我简直不敢相信:)感谢您为此花费的时间,约翰。 - aliakbarian
非常高兴它有所帮助。其实这只是一个问出正确问题的问题。这个论坛上有很多知识,但新手经常无法访问那些知识,因为他们不知道该问什么。 - john
1
@aliakbarian:我刚刚发现我的代码有一个小错误,你可能应该再复制一次。我将这个if(j == utf8.size())更改为if(i == utf8.size()) - john
1
请注意:此方法仅适用于Windows系统。Unix系统使用32位wchar_t。尽管如此,在Windows上仍然可以执行std::wstring wstr(str.begin(), str.end()); - Simon Nitzsche
干得好,谢谢。我已经使用它将 FPC 字符串(通过 @str[1])转换为 C++ 宽字符串。 - roberto
显示剩余2条评论

2
这里有一些相关的问答,值得一读:这里这里
基本上,您需要将字符串转换为通用格式——我个人偏爱将其转换为UTF-8,但您的情况可能不同。
已经有很多软件可以进行转换——转换非常简单,可以在几个小时内完成——但是为什么不选择像UTF-8 CPP这样的已经完成的东西呢

如果你只使用Windows系统:http://msdn.microsoft.com/en-us/library/dd319072(v=VS.85).aspx。否则,请使用可移植库。 - Mooing Duck

2
为了在这两种类型之间进行转换,您应该使用:std::codecvt_utf8_utf16< wchar_t>
请注意,我用于定义UTF16(L)和UTF8(u8)的字符串前缀。
#include <string>

#include <codecvt>

int main()
{

    std::string original8 = u8"הלו";

    std::wstring original16 = L"הלו";

    //C++11 format converter
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;

    //convert to UTF8 and std::string
    std::string utf8NativeString = convert.to_bytes(original16);

    std::wstring utf16NativeString = convert.from_bytes(original8);

    assert(utf8NativeString == original8);
    assert(utf16NativeString == original16);

    return 0;
}

0
微软已经开发了一个美丽的库,作为他们的 Casablanca 项目的一部分,也被称为 CPPRESTSDK,用于这样的转换。这被标记在命名空间 utility::conversions 下。
一个简单的用法看起来像是这样的,使用命名空间:

utility::conversions

utf8_to_utf16("sample_string");

-1

这个页面似乎也很有用:http://www.codeproject.com/KB/string/UtfConverter.aspx

在该页面的评论部分,还有一些有趣的建议可以完成这个任务,例如:

// Get en ASCII std::string from anywhere
std::string sLogLevelA = "Hello ASCII-world!";

std::wstringstream ws;
ws << sLogLevelA.c_str();
std::wstring sLogLevel = ws.str();

或者

// To std::string:
str.assign(ws.begin(), ws.end());
// To std::wstring
ws.assign(str.begin(), str.end());

虽然我不确定这些方法的有效性...


1
assign() 绝对不是将 UTF-8<->UTF-16 转换的正确方法。请勿在家中尝试。 - crayze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接