C ++-在字符串,u16string和u32string之间转换

我一直在寻找一种在Unicode字符串类型之间进行转换的方法,并且遇到了这种方法。 我不仅不完全了解该方法(没有评论),而且该文章还暗示将来会有更好的方法。

如果这是最好的方法,请您指出使它起作用的原因,否则,我想听听关于更好方法的建议。

DrYap asked 2020-08-01T07:52:04Z
3个解决方案
87 votes

__STDC_ISO_10646____STDC_ISO_10646__不一定会转换为UTF-16或UTF-32,它们会转换为wchar_t以及任何区域设置wchar_t编码。 所有Windows语言环境都使用两个字节的wchar_t和UTF-16作为编码,但是其他主要平台使用具有UTF-32的4字节wchar_t(对于某些语言环境甚至是非Unicode编码)。 仅支持单字节编码的平台甚至可以具有一个字节wchar_t,并且其编码因地区而异。 因此,在我看来,wchar_t对于可移植性和Unicode而言是一个错误的选择。 *

C ++ 11中引入了一些更好的选项。 std :: codecvt的新专业化,新的codecvt类和新的模板使使用它们进行转换非常方便。

首先,使用codecvt的新模板类是std :: wstring_convert。 一旦创建了std :: wstring_convert类的实例,就可以轻松地在字符串之间进行转换:

std::wstring_convert<...> convert; // ... filled in with a codecvt to do UTF-8 <-> UTF-16
std::string utf8_string = u8"This string has UTF-8 content";
std::u16string utf16_string = convert.from_bytes(utf8_string);
std::string another_utf8_string = convert.to_bytes(utf16_string);

为了进行不同的转换,您只需要不同的模板参数,其中之一是codecvt构面。 以下是一些易于与wstring_convert一起使用的新方面:

std::codecvt_utf8_utf16<char16_t> // converts between UTF-8 <-> UTF-16
std::codecvt_utf8<char32_t> // converts between UTF-8 <-> UTF-32
std::codecvt_utf8<char16_t> // converts between UTF-8 <-> UCS-2 (warning, not UTF-16! Don't bother using this one)

使用这些示例:

std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string a = convert.to_bytes(u"This string has UTF-16 content");
std::u16string b = convert.from_bytes(u8"blah blah blah");

新的std :: codecvt专业化名称很难使用,因为它们具有受保护的析构函数。 为了解决这个问题,您可以定义一个具有析构函数的子类,或者可以使用std :: use_facet模板函数来获取现有的编解码器实例。 同样,这些专业化的问题是您不能在Visual Studio 2010中使用它们,因为模板专业化不适用于typedef类型,并且编译器将char16_t和char32_t定义为typedef。 这是定义自己的codecvt子类的示例:

template <class internT, class externT, class stateT>
struct codecvt : std::codecvt<internT,externT,stateT>
{ ~codecvt(){} };

std::wstring_convert<codecvt<char16_t,char,std::mbstate_t>,char16_t> convert16;
std::wstring_convert<codecvt<char32_t,char,std::mbstate_t>,char32_t> convert32;

char16_t专业化可在UTF-16和UTF-8之间转换。 char32_t专长是UTF-32和UTF-8。

请注意,C ++ 11提供的这些新转换不包括任何在UTF-32和UTF-16之间直接转换的方法。 相反,您只需要组合两个std :: wstring_convert实例。


*****我想我会在wchar_t及其目的上添加一条注释,以强调为什么通常不应该将它用于Unicode或可移植的国际化代码。 以下是我的答案的简短版本[https://stackoverflow.com/a/11107667/365496]

什么是wchar_t?

wchar_t的定义使得任何语言环境的char编码都可以转换为wchar_t,其中每个wchar_t都恰好表示一个代码点:

wchar_t类型是一种独特的类型,其值可以代表在支持的语言环境(22.3.1)中指定的最大扩展字符集的所有成员的独特代码。 -[基本。根本] 3.9.1 / 5

这不需要wchar_t足够大到可以同时表示来自所有语言环境的任何字符。 即,用于wchar_t的编码在语言环境之间可能有所不同。 这意味着您不一定必须使用一种语言环境将字符串转换为wchar_t,然后使用另一种语言环境转换回char。

由于这似乎是wchar_t在实践中的主要用途,因此您可能会想知道这样做的好处。

wchar_t的最初意图和目的是通过定义文本来简化文本处理,从而要求从字符串的代码单元到文本字符的一对一映射,从而允许使用与ascii字符串相同的简单算法 与其他语言一起使用。

不幸的是,对wchar_t的要求假设字符和代码点之间是一对一的映射才能实现。 Unicode打破了这一假设,因此您也无法安全地将wchar_t用于简单的文本算法。

这意味着便携式软件不能将wchar_t用作语言环境之间文本的通用表示形式,也不能使用简单的文本算法。

今天的wchar_t有什么用?

不管怎样,对于可移植的代码而言。 如果定义了__STDC_ISO_10646__,则wchar_t的值将直接表示在所有语言环境中具有相同值的Unicode代码点。 这样可以安全地进行前面提到的区域间转换。 但是,您不能仅仅依靠它来决定是否可以使用wchar_t,因为尽管大多数Unix平台都定义了它,但Windows并没有,即使Windows在所有语言环境中都使用相同的wchar_t语言环境。

我认为Windows未定义__STDC_ISO_10646__的原因是因为Windows使用UTF-16作为其wchar_t编码,并且因为UTF-16使用代理对来表示大于U + FFFF的代码点,这意味着UTF-16不满足 要求为__STDC_ISO_10646__

对于平台特定的代码,wchar_t可能更有用。 Windows实质上是必需的(例如,某些文件如果不使用wchar_t文件名就无法打开),尽管据我所知Windows是唯一的实现(因此也许我们可以将wchar_t视为'Windows_char_t')。

在事后看来,wchar_t对于简化文本处理或存储与区域设置无关的文本显然没有用。 可移植代码不应尝试将其用于这些目的。

bames53 answered 2020-08-01T07:53:53Z
12 votes

我已经写了一些帮助函数来转换为UTF8字符串(C ++ 11):

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

using namespace std;

template <typename T>
string toUTF8(const basic_string<T, char_traits<T>, allocator<T>>& source)
{
    string result;

    wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
    result = convertor.to_bytes(source);

    return result;
}

template <typename T>
void fromUTF8(const string& source, basic_string<T, char_traits<T>, allocator<T>>& result)
{
    wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
    result = convertor.from_bytes(source);
}

用法示例:

// Unicode <-> UTF8
{
    wstring uStr = L"Unicode string";
    string str = toUTF8(uStr);

    wstring after;
    fromUTF8(str, after);
    assert(uStr == after);
}

// UTF16 <-> UTF8
{
    u16string uStr;
    uStr.push_back('A');
    string str = toUTF8(uStr);

    u16string after;
    fromUTF8(str, after);
    assert(uStr == after);
}
dimon4eg answered 2020-08-01T07:54:18Z
-2 votes

据我所知,C ++没有提供标准方法来与UTF-32进行相互转换。 但是,对于UTF-16,有mbstowcs(多字节到宽字符串)方法,以及相反的wcstombs方法。

如果还需要UTF-32,则需要iconv,它在POSIX 2001中存在,但在标准C中不存在,因此在Windows上,您将需要libiconv之类的替代品。

这是有关如何使用mbstowcs的示例:

#include <string>
#include <iostream>
#include <stdlib.h>

using namespace std;

wstring widestring(const string &text);

int main()
{
  string text;
  cout << "Enter something: ";
  cin >> text;

  wcout << L"You entered " << widestring(text) << ".\n";
  return 0;
}

wstring widestring(const string &text)
{
  wstring result;
  result.resize(text.length());
  mbstowcs(&result[0], &text[0], text.length());
  return result;
}

相反的是这样的:

string mbstring(const wstring &text)
{
  string result;
  result.resize(text.length());
  wcstombs(&result[0], &text[0], text.length());
  return result;
}

Nitpick:是的,我知道,wchar_t的大小是实现定义的,因此它可以是4字节(UTF-32)。 但是,我不知道执行该操作的编译器。

Raphael R. answered 2020-08-01T07:54:56Z
translate from https://stackoverflow.com:/questions/7232710/convert-between-string-u16string-u32string