如何在C++中将Unicode字符编码转换为UTF-8?

10

我有一个由Unicode编码点组成的数组

unsigned short array[3]={0x20ac,0x20ab,0x20ac};

我只是希望将此内容转换为 utf-8 格式,以便使用 C++ 按字节写入文件。

例如:0x20ac 应该被转换为 e2 82 ac。

或者是否有其他方法可以直接将 Unicode 字符写入文件。


您可以使用Boost库的Boost.Locale: http://www.boost.org/doc/libs/1_55_0/libs/locale/doc/html/index.html - Nick Louloudakis
1
使用像 ICU 这样的 Unicode 库。即使 Windows 本身也有足够的能力来完成这个任务。 - Mooing Duck
我假设这是问题中的代码点数组。你能确认你将忽略那些不能适应 short 的代码点,并且它实际上不是UTF-16或UCS-2编码吗? - Mooing Duck
你能具体说明一下吗? - xin
为了实现这个目标,Boost.Locale使用最先进的Unicode和本地化库:ICU - Unicode国际组件。 - Chris
7个回答

11

终于!有了C++11!

#include <string>
#include <locale>
#include <codecvt>
#include <cassert>

int main()
{
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
    std::string u8str = converter.to_bytes(0x20ac);
    assert(u8str == "\xe2\x82\xac");
}

1
这很不错,但是在使用支持char32_tstd::codecvt的Visual Studio 2015和2017编译器方面存在问题。但是你可以使用 uint32_t 来替代: std::wstring_convert< std::codecvt_utf8<uint32_t>, uint32_t > converter; - Matthew
9
给现在阅读此内容的所有人:这在C++17中已被弃用。 - handicraftsman
3
给所有阅读上述评论的人:在C++ 17中,STL没有替换方案。 - Kidsunbo

5
术语“Unicode”指的是一种文本编码和处理标准。这包括像“UTF-8”,“UTF-16”,“UTF-32”,“UCS-2”等编码方式。
我猜您正在Windows环境下进行编程,其中“Unicode”通常指“UTF-16”。
在C++中使用Unicode,我建议使用ICU库
如果您正在Windows上编程,不想使用外部库,并且没有关于平台依赖性的限制,您可以使用WideCharToMultiByte
ICU的示例:
#include <iostream>
#include <unicode\ustream.h>

using icu::UnicodeString;

int main(int, char**) {
    //
    // Convert from UTF-16 to UTF-8
    //
    std::wstring utf16 = L"foobar";
    UnicodeString str(utf16.c_str());
    std::string utf8;
    str.toUTF8String(utf8);

    std::cout << utf8 << std::endl;
}

要做到你想要的,需要:
// Assuming you have ICU\include in your include path
// and ICU\lib(64) in your library path.
#include <iostream>
#include <fstream>
#include <unicode\ustream.h>
#pragma comment(lib, "icuio.lib")
#pragma comment(lib, "icuuc.lib")

void writeUtf16ToUtf8File(char const* fileName, wchar_t const* arr, size_t arrSize) {
    UnicodeString str(arr, arrSize);
    std::string utf8;
    str.toUTF8String(utf8);

    std::ofstream out(fileName, std::ofstream::binary);
    out << utf8;
    out.close();
}

如何下载和设置。 - Venkatesan
2
请前往这里,向下滚动至ICU4C二进制下载,并下载您所需的版本。解压缩ZIP文件,并将提取的目录放置在您可以从项目中访问的位置。将'path-where-you-put-it/icu/include'添加到您的项目包含路径中,将'path-where-you-put-it/icu/lib'(或lib64)添加到您的项目库路径中。 - Max Truxa

2

以下代码可能会对您有所帮助:

#include <atlconv.h>
#include <atlstr.h>

#define ASSERT ATLASSERT

int main()
{
    const CStringW unicode1 = L"\x0391 and \x03A9"; // 'Alpha' and 'Omega'

    const CStringA utf8 = CW2A(unicode1, CP_UTF8);

    ASSERT(utf8.GetLength() > unicode1.GetLength());

    const CStringW unicode2 = CA2W(utf8, CP_UTF8);

    ASSERT(unicode1 == unicode2);
}

1
这段代码使用 WideCharToMultiByte 函数(假定你正在使用 Windows):
unsigned short wide_str[3] = {0x20ac, 0x20ab, 0x20ac};
int utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_str, 3, NULL, 0, NULL, NULL) + 1;
char* utf8_str = calloc(utf8_size);
WideCharToMultiByte(CP_UTF8, 0, wide_str, 3, utf8_str, utf8_size, NULL, NULL);

你需要调用它两次:第一次获取输出字节数,第二次才能实际转换。如果你知道输出缓冲区大小,可以跳过第一次调用。或者,你可以简单地分配比原始大小大2倍的缓冲区+1个字节(对于你的情况,意味着12+1个字节) - 这应该总是足够的。

不错,但我正在使用Linux机器。 - Venkatesan

0

我有一个类似但略有不同的问题。我的字符串中包含Unicode代码点,以字符串表示形式存在。例如:"F\u00f3\u00f3 B\u00e1r"。我需要将字符串代码点转换为它们的Unicode字符。

这是我的C#解决方案:

using System.Globalization;
using System.Text.RegularExpressions;

static void Main(string[] args)
{
    Regex CodePoint = new Regex(@"\\u(?<UTF32>....)");
    Match Letter;
    string s = "F\u00f3\u00f3 B\u00e1r";
    string utf32;
    Letter = CodePoint.Match(s);
    while (Letter.Success)
    {
        utf32 = Letter.Groups[1].Value;
        if (Int32.TryParse(utf32, NumberStyles.HexNumber, CultureInfo.GetCultureInfoByIetfLanguageTag("en-US"), out int HexNum))
            s = s.Replace("\\u" + utf32, Char.ConvertFromUtf32(HexNum));
        Letter = Letter.NextMatch();
    }
    Console.WriteLine(s);
}

输出:Fóó Bár


0

使用标准的C++

#include <iostream>
#include <locale>
#include <vector>

int main()
{
    typedef std::codecvt<wchar_t, char, mbstate_t> Convert;
    std::wstring w = L"\u20ac\u20ab\u20ac";
    std::locale locale("en_GB.utf8");
    const Convert& convert = std::use_facet<Convert>(locale);

    std::mbstate_t state;
    const wchar_t* from_ptr;
    char* to_ptr;
    std::vector<char> result(3 * w.size() + 1, 0);
    Convert::result convert_result = convert.out(state,
          w.c_str(), w.c_str() + w.size(), from_ptr,
          result.data(), result.data() + result.size(), to_ptr);

    if (convert_result == Convert::ok)
        std::cout << result.data() << std::endl;
    else std::cout << "Failure: " << convert_result << std::endl;
}

1
拥有一个STL解决方案总是很好,但这里的警告是,在C++17中将弃用codecvt,而令人惊讶的是,在C++17及以后的版本中没有可替代的解决方案。 - galactica

0

Iconv 是一个被广泛应用于多个平台的流行库。


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