如何将wchar_t*转换为std :: string?

cod*_*rog 30 c++ string wchar-t stdstring

我改变了我的类使用std :: string(基于我在这里得到的答案,但是我有一个函数返回wchar_t*.如何将它转换为std :: string?

我试过这个:

std::string test = args.OptionArg();
Run Code Online (Sandbox Code Playgroud)

但它说错误C2440:'初始化':无法从'wchar_t*'转换为'std :: basic_string <_Elem,_Traits,_Ax>'

Ult*_*ior 44

std::wstring ws( args.OptionArg() );
std::string test( ws.begin(), ws.end() );
Run Code Online (Sandbox Code Playgroud)

  • 我不知道为什么这个答案得到了这么多的赞成,它的作用相当于每个字符的`char c = static_cast <char>(wideChar)`,所以如果宽字符不在*中,它显然会丢失信息*ASCII范围**. (8认同)
  • 提供问题的实际答案! (4认同)
  • 我喜欢这个解决方案,因为它很简单。然而,一点解释也无妨。它留下了字符如何实际转换的问题。是否存在信息丢失或宽字符转换为 unicode? (2认同)
  • @jb 取决于 `std::string` 的编码。例如 使用 UTF-8 时不会丢失信息。 (2认同)

Pra*_*ian 8

您可以使用以下函数将宽字符串转换为ASCII字符串:

#include <locale>
#include <sstream>
#include <string>

std::string ToNarrow( const wchar_t *s, char dfault = '?', 
                      const std::locale& loc = std::locale() )
{
  std::ostringstream stm;

  while( *s != L'\0' ) {
    stm << std::use_facet< std::ctype<wchar_t> >( loc ).narrow( *s++, dfault );
  }
  return stm.str();
}
Run Code Online (Sandbox Code Playgroud)

请注意,这只会替换dfault参数中不存在等效ASCII字符的任何宽字符; 它不会从UTF-16转换为UTF-8.如果要转换为UTF-8,请使用ICU等库.


zko*_*oza 8

令人相当失望的是,这个老问题的答案都没有解决将宽字符串转换为 UTF-8 字符串的问题,这在非英语环境中很重要。

\n

这是一个可以运行的示例代码,可以用作构造自定义转换器的提示。它基于cppreference.com 中的示例代码中的示例代码。

\n
#include <iostream>\n#include <clocale>\n#include <string>\n#include <cstdlib>\n#include <array>\n\nstd::string convert(const std::wstring& wstr)\n{\n    const int BUFF_SIZE = 7;\n    if (MB_CUR_MAX >= BUFF_SIZE) throw std::invalid_argument("BUFF_SIZE too small");\n    std::string result;\n    bool shifts = std::wctomb(nullptr, 0);  // reset the conversion state\n    for (const wchar_t wc : wstr)\n    {\n        std::array<char, BUFF_SIZE> buffer;\n        const int ret = std::wctomb(buffer.data(), wc);\n        if (ret < 0) throw std::invalid_argument("inconvertible wide characters in the current locale");\n        buffer[ret] = \'\\0\';  // make \'buffer\' contain a C-style string\n        result = result + std::string(buffer.data());\n    }\n    return result;\n}\n\nint main()\n{\n    auto loc = std::setlocale(LC_ALL, "en_US.utf8");  // UTF-8\n    if (loc == nullptr) throw std::logic_error("failed to set locale");\n    std::wstring wstr = L"a\xc4\x85\xc3\x9f\xe6\xb0\xb4-\xe6\x89\xab\xe6\x8f\x8f-\xe2\x82\xac\\u00df\\u6c34\\U0001d10b";\n    std::cout << convert(wstr) << "\\n";\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

正如预期的那样打印:

\n

程序打印输出

\n

解释

\n
    \n
  • 7 似乎是缓冲区大小的最小安全值BUFF_SIZE。其中包括 4 作为编码单个字符的 UTF-8 字节的最大数量;2 表示可能的“移位序列”,1 表示尾随\'\\0\'.
  • \n
  • MB_CUR_MAX是一个运行时变量,所以static_assert在这里不可用
  • \n
  • char每个宽字符都使用以下方式 转换为其表示形式std::wctomb
  • \n
  • 仅当当前语言环境允许字符的多字节表示时,此转换才有意义
  • \n
  • 为此,应用程序需要设置正确的区域设置。en_US.utf8似乎足够通用(在大多数机器上可用)。在Linux中,可以通过命令在控制台中查询可用的语言环境locale -a
  • \n
\n

对最高票数答案的批评

\n

点赞数最高的答案,

\n
std::wstring ws( args.OptionArg() );\nstd::string test( ws.begin(), ws.end() );\n
Run Code Online (Sandbox Code Playgroud)\n

仅当宽字符表示 ASCII 字符时才能正常工作 - 但这不是宽字符的设计目的。在此解决方案中,转换后的字符串每个源宽字符包含一个字符ws.size() == test.size()。因此,它会丢失原始 wstring 中的信息,并生成无法解释为正确的 UTF-8 序列的字符串。例如,在我的机器上,“\xc4\x84\xc5\x9a\xc4\x86II”的简单转换产生的字符串打印为“ZII”,即使其大小为 5(应该是 8)。

\n


Ste*_*end 6

您可以使用wstring并保留Unicode中的所有内容

  • 如果它甚至没有回答这个问题,它是如何被接受的答案? (20认同)
  • 由于您在Windows上编程,您可能应该使用Unicode.Windows API和NTFS本身支持UTF-16,因此构建ASCII应用程序会产生额外的开销,其中每个函数都在为您执行字符串转换. (3认同)
  • 如果使用.c_str(),我仍然会得到const char *?我还有其他需要const char *的功能 (2认同)

pau*_*uap 5

这是一个老问题,但如果是这样的话,你并不真正寻求转换,而是使用Mircosoft的TCHAR能够构建ASCII和Unicode,你可以回想起std :: string真的

typedef std::basic_string<char> string
Run Code Online (Sandbox Code Playgroud)

所以我们可以定义我们自己的typedef

#include <string>
namespace magic {
typedef std::basic_string<TCHAR> string;
}
Run Code Online (Sandbox Code Playgroud)

然后,你可以使用magic::stringTCHAR,LPCTSTR