问题 将“normal”std :: string转换为utf-8


让我们看看我是否可以在没有太多事实错误的情况下解释这一点......

我正在写一个字符串类,我希望它能够使用 utf-8 (存储在std :: string中)作为内部存储。 我希望它能够兼顾“正常” std::string 和 std::wstring 作为输入和输出。

使用std :: wstring不是问题,我可以使用 std::codecvt_utf8<wchar_t> 要转换为和转换为std :: wstring。

然而,经过广泛的谷歌搜索和搜索SO我还没有找到一种方法来转换“正常/默认”C ++ std :: string(我假设在Windows中使用本地系统本地化?)和utf-8标准: :串。

我想一个选项是首先将std :: string转换为std :: wstring std::codecvt<wchar_t, char> 然后将其转换为如上所述的utf-8,但这看起来非常低效,因为如果我理解正确的话,至少char的前128个值应该直接转换为utf-8而不进行转换。

我发现了类似的问题: C ++:如何将ASCII或ANSI转换为UTF8并存储在std :: string中 虽然我对这个答案有点怀疑,因为它很难编码为拉丁语1,我希望这可以安全地使用所有类型的本地化。

没有答案涉及提升感谢,我不希望让我的代码库使用它的头痛。


1150
2018-02-05 10:59


起源

首先,您需要以某种方式获得“(我假设在Windows中使用本地系统本地化?)”的问号。 std::string 没有正常/默认编码。你可以选择假设 std::string 你有根据语言环境进行编码,但是如果你刚刚从文件中读取它,那么这可能是不真实的,因为它将被编码,但文件是编码的。 - Steve Jessop
通常,在阅读原始文本文件时,无法知道它具有什么样的编码。缺少此信息似乎更有可能在具有相同编码的系统上创建文件,因此我假设读取文件的输入是本地编码。 - DaedalusAlpha
好的,所以你确实可以删除问号:-)毫无疑问,你正在假设特定于语言环境的编码。 - Steve Jessop


答案:


如果您的“普通字符串”是使用系统的代码页编码的,并且您想将其转换为UTF-8,那么这应该有效:

std::string codepage_str;
int size = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, codepage_str.c_str(),
                               codepage_str.length(), nullptr, 0);
std::wstring utf16_str(size, '\0');
MultiByteToWideChar(CP_ACP, MB_COMPOSITE, codepage_str.c_str(),
                    codepage_str.length(), &utf16_str[0], size);

int utf8_size = WideCharToMultiByte(CP_UTF8, 0, utf16_str.c_str(),
                                    utf16_str.length(), nullptr, 0,
                                    nullptr, nullptr);
std::string utf8_str(utf8_size, '\0');
WideCharToMultiByte(CP_UTF8, 0, utf16_str.c_str(),
                    utf16_str.length(), &utf8_str[0], utf8_size,
                    nullptr, nullptr);

14
2018-02-05 11:11



这与我的天真解决方案没有太大的不同,问题不是吗?首先转换为wstring然后转换为utf-8,这意味着通过数据至少有4个循环(检查大小,转换,检查大小,转换),而如果输入数据是仅使用ascii字符的典型英文文本,那么一个循环就足够了没有转换。 - DaedalusAlpha
@DaedalusAlpha,除非你想看看处理Windows代码页的Unicode库的位置,那么这是你使用Win32 API做的最好的。您必须处理7位范围之外的那些字符,因此仅使用一个循环是不够的。 - Simple
如果您第一次运行循环检查该字符是否在7位范围内并将其添加到utf-8字符串,并且一旦检查失败,您将清除该字符串然后回退到该怎么办?在这种情况下,对于ascii文本来说肯定至少要快1000%,对于非ascii来说最多只有20%。我认为很多文本文件都在ascii范围内。 - DaedalusAlpha
@DaedalusAlpha取决于你是否住在美国。 - Simple
不一定,我认为很多文本文件都是从用英语编码并用英语输出的程序输出的。此外,许多文本文件根本不包含任何文本,只包含在ascii范围内的数字。我住在瑞典,可以肯定地说,我的计算机上至少有99%的文本文件是英文的,或者只包含数字,例如csv文件。 - DaedalusAlpha