将wstring转换为UTF-8编码的string

27

我需要在wstring和string之间进行转换。我想到使用codecvt facet应该可以解决问题,但似乎对于utf-8语言环境不起作用。

我的想法是,当我将utf-8编码的文件读入字符时,一个utf-8字符会被读入两个普通字符(这就是utf-8的工作方式)。我想为我在代码中使用的库创建这个utf-8字符串。

有人知道如何做吗?

我已经尝试过这个:

  locale mylocale("cs_CZ.utf-8");
  mbstate_t mystate;

  wstring mywstring = L"čřžýáí";

  const codecvt<wchar_t,char,mbstate_t>& myfacet =
    use_facet<codecvt<wchar_t,char,mbstate_t> >(mylocale);

  codecvt<wchar_t,char,mbstate_t>::result myresult;  

  size_t length = mywstring.length();
  char* pstr= new char [length+1];

  const wchar_t* pwc;
  char* pc;

  // translate characters:
  myresult = myfacet.out (mystate,
      mywstring.c_str(), mywstring.c_str()+length+1, pwc,
      pstr, pstr+length+1, pc);

  if ( myresult == codecvt<wchar_t,char,mbstate_t>::ok )
   cout << "Translation successful: " << pstr << endl;
  else cout << "failed" << endl;
  return 0;

该函数在cs_CZ.utf-8语言环境下返回“失败”,而在cs_CZ.iso8859-2语言环境下正常工作。


1
请查看此链接:http://www.boost.org/doc/libs/1_42_0/libs/serialization/doc/codecvt.html 可能会有所帮助。 - smerlin
3
一个UTF-8字符被读入后会变成两个普通字符(这就是UTF-8的工作方式)。不,实际上不是这样的。UTF-16(大多数情况下)采用这种方式,而一个UTF-8代码点由1到4个字节表示,一个“字符”可以由多个代码点组成。 - ephemient
是的 - 我知道它,我只是写得不好 :) - Trakhan
8个回答

95

下面的代码可能会有所帮助 :)

#include <codecvt>
#include <string>

// convert UTF-8 string to wstring
std::wstring utf8_to_wstring (const std::string& str)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.from_bytes(str);
}

// convert wstring to UTF-8 string
std::string wstring_to_utf8 (const std::wstring& str)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.to_bytes(str);
}

7
但在 Linux 上使用 libstdc++ 则不行。 - Tom
1
虽然上述工作可以完成,但我强烈建议研究Unicode库,例如ICU和Boost.Locale。 - skyde
你可能还需要 #include <locale>,否则它应该可以使用libc++构建。 - Hofi
5
codecvt 自 C++17 起已被弃用,目前没有替代方案。 - Alex Reinking
2
@AlexReinking cpp参考文档并没有说codecvt已经被弃用。虽然一些成员已经被弃用,但也有新的成员被添加进来了(例如C++20添加了std::codecvt<char32_t, char8_t, std::mbstate_t>)。 https://en.cppreference.com/w/cpp/locale/codecvt - Sahil Singh
显示剩余3条评论

9

您的平台是什么?请注意,Windows不支持UTF-8语言环境,这可能解释了为什么您失败了。

要以与平台相关的方式完成此操作,您可以在Windows上使用MultiByteToWideChar/WideCharToMultiByte,在Linux上使用iconv。您可能可以使用一些boost魔术以平台无关的方式完成此操作,但我自己没有尝试过,因此无法添加有关此选项的信息。


3
在Windows上,您需要使用std :: codecvt_utf8_utf16 !否则,您的转换将在需要两个16位代码单元的Unicode代码点上失败。例如(U + 1F609)
#include <codecvt>
#include <string>

// convert UTF-8 string to wstring
std::wstring utf8_to_wstring (const std::string& str)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> myconv;
    return myconv.from_bytes(str);
}

// convert wstring to UTF-8 string
std::string wstring_to_utf8 (const std::wstring& str)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> myconv;
    return myconv.to_bytes(str);
}

2
你可以使用boost的utf_to_utf转换器将字符格式转换为std::string中的存储格式。"最初的回答"
std::string myresult = boost::locale::conv::utf_to_utf<char>(my_wstring);

1

当前得票最多的答案不是平台无关的。它在非BMP字符(即表情符号)上会出错。JWiesemann已经在他们的答案中指出了这一点,但他们的代码只能在Windows上运行。

所以这里是一个正确的平台无关版本:

#include <codecvt>
#include <codecvt>
#include <string>
#include <type_traits>

std::string wstring_to_utf8(std::wstring const& str)
{
  std::wstring_convert<std::conditional_t<
        sizeof(wchar_t) == 4,
        std::codecvt_utf8<wchar_t>,
        std::codecvt_utf8_utf16<wchar_t>>> converter;
  return converter.to_bytes(str);
}

std::wstring utf8_to_wstring(std::string const& str)
{
  std::wstring_convert<std::conditional_t<
        sizeof(wchar_t) == 4,
        std::codecvt_utf8<wchar_t>,
        std::codecvt_utf8_utf16<wchar_t>>> converter;
  return converter.from_bytes(str);
}

在MSVC上,这可能会生成一些弃用警告。您可以通过将函数包装起来来禁用这些警告。
#pragma warning(push)
#pragma warning(disable : 4996)
<the two functions>
#pragma warning(pop)

参见这个答案以了解为什么可以禁用该警告的另一个问题。

-1

Locale的作用是提供了程序有关外部编码的信息,但假设内部编码没有改变。如果您想输出UTF-8,则需要从而不是< char*> 进行输出。

您可以将其输出为原始数据(而不是字符串),如果系统语言环境为UTF-8,则应正确解释它。

此外,在使用(w)cout /(w)cerr /(w)cin时,您需要在流上注入区域设置。


UTF-8使用8位代码单元。char(以及signed charunsigned char)必须至少为8位。我认为你可能在想UTF-16,UTF-32,UCS2或UCS4。 - Justin Time - Reinstate Monica

-2

Lexertl库有一个迭代器,可以让您这样做:

std::string str;
str.assign(
  lexertl::basic_utf8_out_iterator<std::wstring::const_iterator>(wstr.begin()),
  lexertl::basic_utf8_out_iterator<std::wstring::const_iterator>(wstr.end()));

-11

C++对Unicode的支持不够完善。建议使用外部库,例如ICU(UnicodeString)或Qt(QString),它们都支持Unicode,包括UTF-8。


8
不完全正确,C++支持区域设置,其中包括编码(不幸的是在Windows上对UTF-8的支持有问题)。 - Šimon Tóth
同意。C++并不保证Unicode或locale("cs_CZ.utf-8")的存在。但是如果你的系统有这个语言环境,它应该能够正常工作。 - MSalters
3
自从 C++11 以来,这段话已经不再正确。 char16_t 特别用于 UTF-16,而 char32_t 则特别用于 UTF-32;C++14 在此基础上扩展了功能,要求 char 类型足够大,能够存储256个不同的值,特别适用于 UTF-8。此外,C++11 还添加了类 codecvt_utf8codecvt_utf16codecvt_utf8_utf16,以及 codecvt 的两个新特化 (std::codecvt<char16_t, char, std::mbstate_t>std::codecvt<char32_t, char, std::mbstate_t>)。因此,现在 C++ 正式支持UTF-8、UTF-16、UTF-32、UCS2 和 UCS4。 - Justin Time - Reinstate Monica
在这些codecvt中,codecvt_utf8用于UTF-8和UCS2/UCS4之间的转换,codecvt_utf16用于UTF-16和UCS2/UCS4之间的转换,codecvt_utf8_utf16用于UTF-8和UTF-16之间的转换,而codecvtchar16_t特化也用于UTF-8和UTF-16,codecvtchar32_t特化则用于UTF-8和UTF-32之间的转换。我还不是100%确定它们的工作原理,因为我今天刚开始学习Unicode转换。 - Justin Time - Reinstate Monica

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