如何将wstring转换为字符串?

时间:2011-01-26 11:58:02

标签: c++ unicode stl wstring

问题是如何将wstring转换为字符串?

我有下一个例子:

#include <string>
#include <iostream>

int main()
{
    std::wstring ws = L"Hello";
    std::string s( ws.begin(), ws.end() );

  //std::cout <<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;
    std::cout <<"std::string =     "<<s<<std::endl;
}

带注释掉的输出行是:

std::string =     Hello
std::wstring =    Hello
std::string =     Hello

但不仅仅是:

std::wstring =    Hello

示例中有什么问题吗?我可以像上面那样进行转换吗?

修改

新例子(考虑到一些答案)是

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

int main()
{
    setlocale(LC_CTYPE, "");

    const std::wstring ws = L"Hello";
    const std::string s( ws.begin(), ws.end() );

    std::cout<<"std::string =     "<<s<<std::endl;
    std::wcout<<"std::wstring =    "<<ws<<std::endl;

    std::stringstream ss;
    ss << ws.c_str();
    std::cout<<"std::stringstream =     "<<ss.str()<<std::endl;
}

输出结果为:

std::string =     Hello
std::wstring =    Hello
std::stringstream =     0x860283c

因此stringstream不能用于将wstring转换为字符串。

18 个答案:

答案 0 :(得分:268)

正如Cubbi在其中一条评论中指出的那样,std::wstring_convert(C ++ 11)提供了一个简洁的简单解决方案(您需要#include <locale><codecvt>) :

std::wstring string_to_convert;

//setup converter
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;

//use converter (.to_bytes: wstr->str, .from_bytes: str->wstr)
std::string converted_str = converter.to_bytes( string_to_convert );

在我遇到这个问题之前,我正在使用wcstombs和繁琐的内​​存分配/重新分配。

http://en.cppreference.com/w/cpp/locale/wstring_convert

<强>更新(2013年11月28日)

一个衬垫可以这样说(感谢Guss的评论):

std::wstring str = std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes("some string");

包装函数可以如下声明:(感谢ArmanSchwarz的评论)

std::wstring s2ws(const std::string& str)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.from_bytes(str);
}

std::string ws2s(const std::wstring& wstr)
{
    using convert_typeX = std::codecvt_utf8<wchar_t>;
    std::wstring_convert<convert_typeX, wchar_t> converterX;

    return converterX.to_bytes(wstr);
}

注意:关于string / wstring是否应该作为引用或文字(由于C ++ 11和编译器更新)传递给函数存在一些争议。我会把决定留给实施者,但值得了解。

注意:我在上面的代码中使用std::codecvt_utf8,但如果您不使用UTF-8,则需要将其更改为您正在使用的相应编码:

http://en.cppreference.com/w/cpp/header/codecvt

答案 1 :(得分:117)

解决方案来自:http://forums.devshed.com/c-programming-42/wstring-to-string-444006.html

std::wstring wide( L"Wide" ); 
std::string str( wide.begin(), wide.end() );

// Will print no problemo!
std::cout << str << std::endl;

谨防此处有 no 字符集转换。这样做只是将每个迭代的wchar_t分配给char - 截断转换。它使用std::string c'tor

template< class InputIt >
basic_string( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );

如评论中所述:

  

值0-127几乎在每个编码中都相同,因此截断   小于127的值会产生相同的文本。投入一个   中国人,你会看到失败。

-

  

Windows代码页1252(Windows英语版)的值128-255   默认情况下,unicode的值128-255大致相同,所以如果   你使用大多数这些角色的代码页应该是   截断为正确的值。 (我完全希望á和õ能够工作,   我知道我们的工作代码依赖于é,我很快就会解决这个问题。

请注意,Win12520x80 - 0x9F范围内的代码点有效。其中包括œžŸ,...

答案 2 :(得分:29)

以下是基于其他建议的解决方案:

#include <string>
#include <iostream>
#include <clocale>
#include <locale>
#include <vector>

int main() {
  std::setlocale(LC_ALL, "");
  const std::wstring ws = L"ħëłlö";
  const std::locale locale("");
  typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
  const converter_type& converter = std::use_facet<converter_type>(locale);
  std::vector<char> to(ws.length() * converter.max_length());
  std::mbstate_t state;
  const wchar_t* from_next;
  char* to_next;
  const converter_type::result result = converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next);
  if (result == converter_type::ok or result == converter_type::noconv) {
    const std::string s(&to[0], to_next);
    std::cout <<"std::string =     "<<s<<std::endl;
  }
}

这通常适用于Linux,但会在Windows上产生问题。

答案 3 :(得分:11)

除了包含区域设置和所有那些花哨的东西,如果你知道FACT你的字符串是可转换的,只需这样做:

#include <iostream>
#include <string>

using namespace std;

int main()
{
  wstring w(L"bla");
  string result;
  for(char x : w)
    result += x;

  cout << result << '\n';
}

实例here

答案 4 :(得分:7)

我认为官方方式仍然是codecvt方面(你需要某种语言环境感知翻译),如

resultCode = use_facet<codecvt<char, wchar_t, ConversionState> >(locale).
  in(stateVar, scratchbuffer, scratchbufferEnd, from, to, toLimit, curPtr);

或类似的东西,我没有工作代码。但我不确定现在有多少人使用这种机器,有多少人只是要求指向内存,让ICU或其他一些图书馆处理血腥细节。

答案 5 :(得分:6)

代码有两个问题:

  1. const std::string s( ws.begin(), ws.end() );中的转换不需要将宽字符正确映射到它们的窄对应字符。最有可能的是,每个宽字符只会被char强制转换 此问题的解决方案已在the answer by kem中提供,并涉及区域设置narrow方面的ctype功能。

  2. 您正在同一程序中将输出写入std::coutstd::wcoutcoutwcout都与相同的流(stdout)相关联,并且使用相同的流作为面向字节的流(如cout所做的那样)和未定义面向广泛的流(如wcout所示) 最好的选择是避免将窄输出和宽输出混合到同一(底层)流。对于stdout / cout / wcout,您可以尝试在宽输出和窄输出之间切换stdout的方向(反之亦然):

    #include <iostream>
    #include <stdio.h>
    #include <wchar.h>
    
    int main() {
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
        fwide(stdout, -1); // switch to narrow
        std::cout << "narrow" << std::endl;
        fwide(stdout, 1); // switch to wide
        std::wcout << L"wide" << std::endl;
    }
    

答案 6 :(得分:6)

在撰写此答案时,谷歌搜索“convert string wstring”的第一名将会让您登陆此页面。我的回答显示了如何将字符串转换为wstring,虽然这不是实际的问题,我应该删除这个答案,但这被认为是不好的形式。你可能想要跳转到this StackOverflow answer,这是现在排名高于此页面。


这是将字符串,wstring和混合字符串常量组合到wstring的方法。使用wstringstream类。

#include <sstream>

std::string narrow = "narrow";
std::wstring wide = "wide";

std::wstringstream cls;
cls << " abc " << narrow.c_str() << L" def " << wide.c_str();
std::wstring total= cls.str();

答案 7 :(得分:6)

你也可以直接使用ctype facet的narrow方法:

#include <clocale>
#include <locale>
#include <string>
#include <vector>

inline std::string narrow(std::wstring const& text)
{
    std::locale const loc("");
    wchar_t const* from = text.c_str();
    std::size_t const len = text.size();
    std::vector<char> buffer(len + 1);
    std::use_facet<std::ctype<wchar_t> >(loc).narrow(from, from + len, '_', &buffer[0]);
    return std::string(&buffer[0], &buffer[len]);
}

答案 8 :(得分:3)

在我的情况下,我必须使用多字节字符(MBCS),我想使用std :: string和std :: wstring。并且不能使用c ++ 11。所以我使用mbstowcs和wcstombs。

我使用new,delete []做同样的功能,但它比这慢。

这可以帮助How to: Convert Between Various String Types

修改

但是,如果转换为wstring并且源字符串不是字母和多字节字符串,则它不起作用。 所以我将wcstombs更改为WideCharToMultiByte。

#include <string>

std::wstring get_wstr_from_sz(const char* psz)
{
    //I think it's enough to my case
    wchar_t buf[0x400];
    wchar_t *pbuf = buf;
    size_t len = strlen(psz) + 1;

    if (len >= sizeof(buf) / sizeof(wchar_t))
    {
        pbuf = L"error";
    }
    else
    {
        size_t converted;
        mbstowcs_s(&converted, buf, psz, _TRUNCATE);
    }

    return std::wstring(pbuf);
}

std::string get_string_from_wsz(const wchar_t* pwsz)
{
    char buf[0x400];
    char *pbuf = buf;
    size_t len = wcslen(pwsz)*2 + 1;

    if (len >= sizeof(buf))
    {
        pbuf = "error";
    }
    else
    {
        size_t converted;
        wcstombs_s(&converted, buf, pwsz, _TRUNCATE);
    }

    return std::string(pbuf);
}

编辑使用'MultiByteToWideChar'代替'wcstombs'

#include <Windows.h>
#include <boost/shared_ptr.hpp>
#include "string_util.h"

std::wstring get_wstring_from_sz(const char* psz)
{
    int res;
    wchar_t buf[0x400];
    wchar_t *pbuf = buf;
    boost::shared_ptr<wchar_t[]> shared_pbuf;

    res = MultiByteToWideChar(CP_ACP, 0, psz, -1, buf, sizeof(buf)/sizeof(wchar_t));

    if (0 == res && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        res = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);

        shared_pbuf = boost::shared_ptr<wchar_t[]>(new wchar_t[res]);

        pbuf = shared_pbuf.get();

        res = MultiByteToWideChar(CP_ACP, 0, psz, -1, pbuf, res);
    }
    else if (0 == res)
    {
        pbuf = L"error";
    }

    return std::wstring(pbuf);
}

std::string get_string_from_wcs(const wchar_t* pcs)
{
    int res;
    char buf[0x400];
    char* pbuf = buf;
    boost::shared_ptr<char[]> shared_pbuf;

    res = WideCharToMultiByte(CP_ACP, 0, pcs, -1, buf, sizeof(buf), NULL, NULL);

    if (0 == res && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        res = WideCharToMultiByte(CP_ACP, 0, pcs, -1, NULL, 0, NULL, NULL);

        shared_pbuf = boost::shared_ptr<char[]>(new char[res]);

        pbuf = shared_pbuf.get();

        res = WideCharToMultiByte(CP_ACP, 0, pcs, -1, pbuf, res, NULL, NULL);
    }
    else if (0 == res)
    {
        pbuf = "error";
    }

    return std::string(pbuf);
}

答案 9 :(得分:3)

此解决方案受到dk123解决方案的启发,但使用依赖于语言环境的codecvt方面。结果是在locale编码的字符串而不是utf8(如果它没有设置为locale):

std::string w2s(const std::wstring &var)
{
   static std::locale loc("");
   auto &facet = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
   return std::wstring_convert<std::remove_reference<decltype(facet)>::type, wchar_t>(&facet).to_bytes(var);
}

std::wstring s2w(const std::string &var)
{
   static std::locale loc("");
   auto &facet = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
   return std::wstring_convert<std::remove_reference<decltype(facet)>::type, wchar_t>(&facet).from_bytes(var);
}

我在寻找它,但我无法找到它。最后我发现我可以使用带有正确typename的std :: use_facet()函数从std :: locale获得正确的方面。希望这会有所帮助。

答案 10 :(得分:3)

除了转换类型之外,您还应该注意字符串的实际格式。

在编译多字节字符集时,Visual Studio和Win API假定使用UTF8(实际上是Windows-28591的Windows编码)。
在编译 Unicode字符集时,Visual Studio和Win API均采用UTF16。

因此,您还必须将字符串也从UTF16转换为UTF8格式,而不仅仅是转换为std :: string。
当使用多种字符格式(例如某些非拉丁语言)时,这将变得很有必要。

想法是确定std::wstring 始终代表 UTF16
std::string 始终代表 UTF8

这不是由编译器强制执行的,它具有更好的策略。 请注意,我用于定义UTF16( L )和UTF8( u8 )的字符串前缀。

要在两种类型之间转换,您应该使用: std::codecvt_utf8_utf16< wchar_t>

#include <string>

#include <codecvt>

int main()
{

    std::string original8 = u8"הלו";

    std::wstring original16 = L"הלו";

    //C++11 format converter
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;

    //convert to UTF8 and std::string
    std::string utf8NativeString = convert.to_bytes(original16);

    std::wstring utf16NativeString = convert.from_bytes(original8);

    assert(utf8NativeString == original8);
    assert(utf16NativeString == original16);

    return 0;
}

答案 11 :(得分:2)

  

默认编码为:

     
      
  • Windows UTF-16。
  •   
  • Linux UTF-8。
  •   
  • MacOS UTF-8。
  •   

此代码具有两种形式,可将std :: string转换为std :: wstring,将std :: wstring转换为std :: string。 如果否定#if定义WIN32,则会得到相同的结果。

1。 std :: string到std :: wstring

MultiByteToWideChar WinAPI

_mbstowcs_s_l

#if defined WIN32
#include <windows.h>
#endif

std::wstring StringToWideString(std::string str)
{
    if (str.empty())
    {
        return std::wstring();
    }
    size_t len = str.length() + 1;
    std::wstring ret = std::wstring(len, 0);
#if defined WIN32
    int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, &str[0], str.size(), &ret[0], len);
    ret.resize(size);
#else
    size_t size = 0;
    _locale_t lc = _create_locale(LC_ALL, "en_US.UTF-8");
    errno_t retval = _mbstowcs_s_l(&size, &ret[0], len, &str[0], _TRUNCATE, lc);
    _free_locale(lc);
    ret.resize(size - 1);
#endif
    return ret;
}

2。 std :: wstring到std :: string

WideCharToMultiByte WinAPI

_wcstombs_s_l

std::string WidestringToString(std::wstring wstr)
{
    if (wstr.empty())
    {
        return std::string();
    }
#if defined WIN32
    int size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &wstr[0], wstr.size(), NULL, 0, NULL, NULL);
    std::string ret = std::string(size, 0);
    WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, &wstr[0], wstr.size(), &ret[0], size, NULL, NULL);
#else
    size_t size = 0;
    _locale_t lc = _create_locale(LC_ALL, "en_US.UTF-8");
    errno_t err = _wcstombs_s_l(&size, NULL, 0, &wstr[0], _TRUNCATE, lc);
    std::string ret = std::string(size, 0);
    err = _wcstombs_s_l(&size, &ret[0], size, &wstr[0], _TRUNCATE, lc);
    _free_locale(lc);
    ret.resize(size - 1);
#endif
    return ret;
}

3。在Windows上,您需要使用WinAPI打印unicode。

WriteConsole

#if defined _WIN32
    void WriteLineUnicode(std::string s)
    {
        std::wstring unicode = StringToWideString(s);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
        std::cout << std::endl;
    }

    void WriteUnicode(std::string s)
    {
        std::wstring unicode = StringToWideString(s);
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
    }

    void WriteLineUnicode(std::wstring ws)
    {
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
        std::cout << std::endl;
    }

    void WriteUnicode(std::wstring ws)
    {
        WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
    }

4。在主程序上。

#if defined _WIN32
int wmain(int argc, WCHAR ** args)
#else
int main(int argc, CHAR ** args)
#endif
{
    std::string source = u8"ÜüΩωЙ你月曜日\naèéøÞǽлљΣæča";
    std::wstring wsource = L"ÜüΩωЙ你月曜日\naèéøÞǽлљΣæča";

    WriteLineUnicode(L"@" + StringToWideString(source) + L"@");
    WriteLineUnicode("@" + WidestringToString(wsource) + "@");
    return EXIT_SUCCESS;
}

5。最后,您需要在控制台中对Unicode字符提供强大而完整的支持。 我推荐ConEmu并设置为default terminal on Windows。您需要将Visual Studio挂接到ConEmu。请记住,Visual Studio的exe文件是 devenv.exe

  

在带有VC ++的Visual Studio 2017上测试; std = c ++ 17。

结果

Result1

答案 12 :(得分:1)

如果其他人感兴趣:我需要一个可以互换使用的课程,无论是string还是wstring。基于dk123's solution的以下课程convertible_string可以使用stringchar const*wstringwchar_t const*初始化,并且可以分配通过或隐式转换为stringwstring(因此可以传递给任何一个函数)。

class convertible_string
{
public:
    // default ctor
    convertible_string()
    {}

    /* conversion ctors */
    convertible_string(std::string const& value) : value_(value)
    {}
    convertible_string(char const* val_array) : value_(val_array)
    {}
    convertible_string(std::wstring const& wvalue) : value_(ws2s(wvalue))
    {}
    convertible_string(wchar_t const* wval_array) : value_(ws2s(std::wstring(wval_array)))
    {}

    /* assignment operators */
    convertible_string& operator=(std::string const& value)
    {
        value_ = value;
        return *this;
    }
    convertible_string& operator=(std::wstring const& wvalue)
    {
        value_ = ws2s(wvalue);
        return *this;
    }

    /* implicit conversion operators */
    operator std::string() const { return value_; }
    operator std::wstring() const { return s2ws(value_); }
private:
    std::string value_;
};

答案 13 :(得分:1)

我度过了很多悲伤的日子,试图提出一种针对C ++ 17的方法,该方法已弃用code_cvt方面,这是我能够通过组合来自几个不同的来源:

setlocale( LC_ALL, "en_US.UTF-8" ); //Invoked in main()

std::string wideToMultiByte( std::wstring const & wideString )
{
     std::string ret;
     std::string buff( MB_CUR_MAX, '\0' );

     for ( wchar_t const & wc : wideString )
     {
         int mbCharLen = std::wctomb( &buff[ 0 ], wc );

         if ( mbCharLen < 1 ) { break; }

         for ( int i = 0; i < mbCharLen; ++i ) 
         { 
             ret += buff[ i ]; 
         }
     }

     return ret;
 }

 std::wstring multiByteToWide( std::string const & multiByteString )
 {
     std::wstring ws( multiByteString.size(), L' ' );
     ws.resize( 
         std::mbstowcs( &ws[ 0 ], 
             multiByteString.c_str(), 
             multiByteString.size() ) );

     return ws;
 }

我在Windows 10上测试了此代码,至少出于我的目的,它似乎工作正常。如果这不考虑您可能需要处理的一些疯狂案例,请不要私自给我,我敢肯定,有更多经验的人可以在此方面做得更好! :-)

此外,还应付款:

Adapted for wideToMultiByte()

Copied for multiByteToWide

答案 14 :(得分:0)

如果您正在处理文件路径(就像我发现需要 wstring-to-string 时经常遇到的那样),您可以使用 filesystem::path(C++17 起):

#include <filesystem>

const std::wstring wPath = GetPath(); // some function that returns wstring
const std::string path = std::filesystem::path(wPath).string();

答案 15 :(得分:0)

#include <boost/locale.hpp>
namespace lcv = boost::locale::conv;

inline std::wstring fromUTF8(const std::string& s)
{ return lcv::utf_to_utf<wchar_t>(s); }

inline std::string toUTF8(const std::wstring& ws)
{ return lcv::utf_to_utf<char>(ws); }

答案 16 :(得分:-1)

我在下面使用将wstring转换为字符串。

std::string strTo;
char *szTo = new char[someParam.length() + 1];
szTo[someParam.size()] = '\0';
WideCharToMultiByte(CP_ACP, 0, someParam.c_str(), -1, szTo, (int)someParam.length(), NULL, NULL);
strTo = szTo;
delete szTo;

答案 17 :(得分:-3)

// Embarcadero C++ Builder 

// convertion string to wstring
string str1 = "hello";
String str2 = str1;         // typedef UnicodeString String;   -> str2 contains now u"hello";

// convertion wstring to string
String str2 = u"hello";
string str1 = UTF8string(str2).c_str();   // -> str1 contains now "hello"