问题是如何将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

但是without只是:

std::wstring =    Hello

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

EDIT

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

#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转换为string。


我相信官方的方法仍然是使用codecvt facet(您需要某种语言环境感知的转换),例如

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

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


代码有两个问题:

The conversion in const std::string s( ws.begin(), ws.end() ); is not required to correctly map the wide characters to their narrow counterpart. Most likely, each wide character will just be typecast to char. The resolution to this problem is already given in the answer by kem and involves the narrow function of the locale's ctype facet. You are writing output to both std::cout and std::wcout in the same program. Both cout and wcout are associated with the same stream (stdout) and the results of using the same stream both as a byte-oriented stream (as cout does) and a wide-oriented stream (as wcout does) are not defined. The best option is to avoid mixing narrow and wide output to the same (underlying) stream. For stdout/cout/wcout, you can try switching the orientation of stdout when switching between wide and narrow output (or vice versa): #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; }


下面是一个基于其他建议的解决方案:

#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上产生问题。


在写这个答案的时候,第一个谷歌搜索“转换字符串wstring”会让你进入这个页面。我的回答展示了如何将字符串转换为wstring,虽然这不是实际的问题,我应该删除这个答案,但这被认为是糟糕的形式。您可能希望跳转到此StackOverflow答案,该答案现在的排名高于此页面。


这是一种将字符串,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();

你也可以直接使用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]);
}

而不是包括locale和所有那些花哨的东西,如果你知道为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';
}

这里有一个活生生的例子


来自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;

更新(2021年):但是,至少在MSVC的最新版本上,这可能会生成wchar_t到char截断警告。警告可以通过使用std::transform代替转换函数中的显式转换来消除,例如:

std::wstring wide( L"Wide" );

std::string str;
std::transform(wide.begin(), wide.end(), std::back_inserter(str), [] (wchar_t c) {
    return (char)c;
});

或者如果你喜欢预分配而不使用back_inserter:

std::string str(wide.length(), 0);
std::transform(wide.begin(), wide.end(), str.begin(), [] (wchar_t c) {
    return (char)c;
});

参见这里各种编译器的示例。


注意,这里根本没有进行字符集转换。它所做的只是将每个迭代的wchar_t赋值给一个char—截断转换。它使用std::string c'tor:

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

如评论所述:

值0-127在几乎所有编码中都是相同的,因此要截断 所有小于127的值都会生成相同的文本。输入一个 汉字,你会看到失败。

windows codepage 1252的值128-255 (windows English 默认值)和unicode的值128-255基本相同,因此如果 这就是你所使用的大部分字符的代码页 截断为正确的值。(我完全期待á和õ能正常工作, 我知道我们的代码在工作中依赖于é,我将很快修复)

请注意,Win1252中0x80 - 0x9F范围内的代码点将无法工作。这包括€,,,,…


正如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)

有一句话可以这样说(谢谢你的评论):

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


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

我与使用new, delete[]做相同的函数,但它比这更慢。

这可以帮助如何:在各种字符串类型之间转换

EDIT

然而,在转换为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);
}

// 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"

如果其他人感兴趣的话:我需要一个可以在任何期望使用string或wstring的地方互换使用的类。下面的类convertible_string,基于dk123的解决方案,可以用string, char const*, wstring或wchar_t const*进行初始化,并且可以被赋值或隐式转换为string或wstring(因此可以传递给接受其中任何一种的函数)。

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_;
};

这个解决方案的灵感来自dk123的解决方案,但是使用了一个依赖于地区的codecvt facet。结果是区域编码的字符串而不是UTF-8(如果它没有设置为区域设置):

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);
}

我一直在找,但我找不到。最后,我发现我可以从std::locale使用std::use_facet()函数与正确的typename获得正确的facet。希望这能有所帮助。


#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); }

我使用下面的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;

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

当编译多字节字符集Visual Studio和Win API时,假设UTF8(实际上是windows编码,即windows -28591)。 当为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;
}

我花了很多悲伤的日子,试图为c++ 17找到一种方法来做到这一点,它已经弃用了code_cvt facet,这是我通过组合来自几个不同来源的代码所能想到的最好的方法:

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上测试了这段代码,至少就我的目的而言,它似乎运行良好。如果这没有考虑到你可能需要处理的一些疯狂的边缘情况,请不要对我进行私刑,我相信有更多经验的人可以改进这一点!: -)

此外,在该表扬的地方表扬:

适用于wideToMultiByte()

复制multiByteToWide


如果你正在处理文件路径(当我发现需要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();