C ++将字符串(或char *)转换为wstring(或wchar_t *)


171
string s = "おはよう";
wstring ws = FUNCTION(s, ws);

我如何将s的内容分配给ws?

搜索谷歌,并使用了一些技巧,但他们不能分配确切的内容。内容失真。


7
我认为不strings接受> 8位字符。它已经用UTF-8编码了吗?
kennytm

3
您的系统编码是什么,它将构成"おはよう"系统编码的字符串?
2010年

我相信MSVC会接受这一点,并对其进行一些多字节编码,也许是UTF-8。
Potatoswatter

1
@Potatoswatter:默认情况下,MSVC不使用UTF-8。如果您输入这些字符,它要求其编码的文件进行转换,并默认为代码页1252
鸣叫鸭

2
@Samir:更重要的是文件的编码是什么?您可以将该字符串移动到文件的开头并显示该部分的十六进制转储吗?我们可能可以从中识别出它。
Mooing Duck 2013年

Answers:


239

假设示例(おはよう)中的输入字符串是UTF-8编码的(从外观上看不是,但是为了解释起见,我们假设它是:-))是Unicode字符串的表示形式只要您有兴趣,就可以单独使用标准库(C ++ 11和更高版本)完全解决您的问题。

TL; DR版本:

#include <locale>
#include <codecvt>
#include <string>

std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string narrow = converter.to_bytes(wide_utf16_source_string);
std::wstring wide = converter.from_bytes(narrow_utf8_source_string);

更长的在线可编译和可运行示例:

(它们都显示相同的示例。有很多冗余...)

注意(旧)

正如评论中指出的并在https://stackoverflow.com/a/17106065/6345中解释的那样,在某些情况下,使用标准库在UTF-8和UTF-16之间进行转换可能会在不同平台上产生意想不到的差异。为了获得更好的转换效果,请std::codecvt_utf8按照http://en.cppreference.com/w/cpp/locale/codecvt_utf8所述进行考虑

注意(新)

由于codecvt标头在C ++ 17中已弃用,因此引发了对该答案中提出的解决方案的某些担忧。然而,C ++标准委员会加入一个重要的声明http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html

该库组件应与一起退至附件D,直到将合适的替换标准化为止。

因此,在可预见的将来,codecvt此答案中的解决方案是安全且可移植的。


2
检查使用哪种编码保存VS文件
Johann Gerell 2013年

9
请注意,这仅适用于C ++ 11!
bk138 2014年

1
在minGW(gcc / g ++ 4.8.1和-std = c ++ 11)中,codecvt标头不存在。有其他选择吗?
布莱恩·杰克

1
请您std::codecvt_utf8为初学者提供一个例子
Noitidart '17

14
请注意,<codecvt>自C ++ 17起不推荐使用。
tambre

47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}

93
仅当所有字符均为单个字节(即ASCII或ISO-8859-1)时,此方法才有效。任何多字节都将惨遭失败,包括UTF-8。该问题显然包含多字节字符。
Mark Ransom

28
这个答案显然是不够的,除了将窄字符照原样复制为宽字符之外,什么也没有做。有关如何正确地将多字节或utf8编码的字符串转换为utf16 wstring的信息,请参见其他答案,尤其是Johann Gerell的答案。
DLRdave

10
这个答案很危险,可能会在非ASCII系统上中断。即,阿拉伯文文件名将被这种黑客破坏。
斯蒂芬

9
如果您忽略了问题正文的细微差别,而专注于问题标题,那么这个答案将非常有用。
Anne Quinn 2015年

3
这仅适用于7位ASCII字符。对于latin1,仅当char配置为unsigned时,它才有效。如果char类型是带符号的(多数情况下是这种情况),则字符> 127将给出错误的结果。
huyc

32

您的问题未指定。严格来说,该示例是语法错误。但是,std::mbstowcs可能正是您要找的东西。

它是一个C库函数,可在缓冲区上运行,但这是一个易于使用的习惯用法,由TBohne(原为Mooing Duck)提供:

std::wstring ws(s.size(), L' '); // Overestimate number of code points.
ws.resize(std::mbstowcs(&ws[0], s.c_str(), s.size())); // Shrink to fit.

1
字符串s =“おはよう”; wchar_t * buf = new wchar_t [s.size()]; size_t num_chars = mbstowcs(buf,s.c_str(),s.size()); wstring ws(buf,num_chars); // ws = Distorted
Samir '04

1
@Samir:您必须确保运行时编码与编译时编码相同。您可能需要setlocale或调整编译器标志。我不知道,因为我不使用Windows,但这就是为什么它不常见的原因。如果可能,请考虑其他答案。
Potatoswatter

1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
Mooing Duck 2013年

2
@WaffleSouffle那已经过时了。自2011年以来,就需要连续的实现,而在此之前,实现就已经退出了这些窍门。
Potatoswatter 2014年

1
并且某些环境(例如mingw)仍然没有codecvt标头,因此早期的一些“更好”的解决方案无法正常工作,这意味着即使在2014
Brian Jack

18

仅Windows API,C ++ 11之前的实现,以防有人需要:

#include <stdexcept>
#include <vector>
#include <windows.h>

using std::runtime_error;
using std::string;
using std::vector;
using std::wstring;

wstring utf8toUtf16(const string & str)
{
   if (str.empty())
      return wstring();

   size_t charsNeeded = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), NULL, 0);
   if (charsNeeded == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   vector<wchar_t> buffer(charsNeeded);
   int charsConverted = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), &buffer[0], buffer.size());
   if (charsConverted == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   return wstring(&buffer[0], charsConverted);
}

您可以对其进行优化。无需使用进行字符串的双重复制vector。只需保留字符串中的字符wstring strW(charsNeeded + 1);,然后将其用作转换缓冲区:&strW[0]。最后,通过执行以下操作确保转换后存在最后一个nullstrW[charsNeeded] = 0;
c00000fd

1
据我所知,@ c00000fd仅自C ++ 11标准以来才要求std :: basic_string内部缓冲区是连续的。我的代码是C ++ 11之前的版本,如文章顶部所述。因此,&strW [0]代码将不符合标准,并且可能在运行时合法崩溃。
Alex Che

13

如果您使用的是Windows / Visual Studio,并且需要将字符串转换为wstring,则可以使用:

#include <AtlBase.h>
#include <atlconv.h>
...
string s = "some string";
CA2W ca2w(s.c_str());
wstring w = ca2w;
printf("%s = %ls", s.c_str(), w.c_str());

将wstring转换为字符串的相同过程(有时您需要指定codepage):

#include <AtlBase.h>
#include <atlconv.h>
...
wstring w = L"some wstring";
CW2A cw2a(w.c_str());
string s = cw2a;
printf("%s = %ls", s.c_str(), w.c_str());

您可以指定一个代码页,甚至可以指定UTF8(使用JNI / Java时,这非常不错)。在此答案中显示将std :: wstring转换为utf8 std :: string标准方法。

// 
// using ATL
CA2W ca2w(str, CP_UTF8);

// 
// or the standard way taken from the answer above
#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);
}

如果您想了解有关代码页的更多信息,那么有一篇关于Joel on Software的有趣文章:每个软件开发人员绝对,肯定地必须绝对了解Unicode和字符集

这些CA2W(将Ansi转换为Wide = unicode)宏是ATL和MFC字符串转换宏(包括示例)的一部分。

有时您需要禁用安全警告#4995',我不知道其他解决方法(对我来说,这是在VS2012中为WindowsXp编译时发生的)。

#pragma warning(push)
#pragma warning(disable: 4995)
#include <AtlBase.h>
#include <atlconv.h>
#pragma warning(pop)

编辑: 那么,根据这篇文章的文章乔尔似乎是:“兼具娱乐,它是实际的技术细节很轻”。文章:每个程序员绝对肯定要了解与文本一起使用的编码和字符集


抱歉,我不是英语母语人士。请根据需要编辑。
lmiguelmh 2014年

下注者怎么了?答案有什么问题?
lmiguelmh 2015年

它促进非便携式代码的事实。
帕维尔·米纳夫

是的,这就是为什么我说这仅在Windows / Visual Studio中有效。但至少此解决方案是正确的,而不是正确的解决方案:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh

附加说明:CA2W在ATL的命名空间下。(ATL :: CA2W)
瓦尔

12

这是一种组合方式stringwstring并将字符串常量混合到wstring。使用wstringstream该类。

这不适用于多字节字符编码。这只是丢弃类型安全性并将std :: string的7位字符扩展为std:wstring的每个字符的低7位的愚蠢方法。仅当您具有7位ASCII字符串并且需要调用需要宽字符串的API时,此功能才有用。

#include <sstream>

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

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

答案似乎很有趣。您能否解释一下:这对多字节编码有效吗?为什么/如何做?
wh1t3cat1k 2015年

编码方案与存储类别正交。string存储1个字节的字符并wstring存储2个字节的字符。像utf8这样的东西会将多字节字符存储为一系列1字节值,即在string。中。字符串类对编码没有帮助。我不是用c ++编码类的专家。
Mark Lakata

2
考虑到它多么简短,有什么理由不是最佳答案?有没有涵盖的情况?

@MarkLakata,我阅读了您对第一条评论的回答,但仍不确定。它适用于多字节字符吗?换句话说,它不容易像这个答案一样陷入陷阱吗?
Marc.2377'9

@ Marc.2377这不适用于多字节字符编码。这只是丢弃类型安全性并将的7位字符从扩展std::string为的每个字符的低7位的愚蠢方法std:wstring。仅当您具有7位ASCII字符串并且需要调用需要宽字符串的API时,此功能才有用。如果您需要更高级的功能,请访问stackoverflow.com/a/8969776/3258851
Mark Lakata

11

char*wstring

char* str = "hello worlddd";
wstring wstr (str, str+strlen(str));

stringwstring

string str = "hello worlddd";
wstring wstr (str.begin(), str.end());

请注意,这仅在要转换的字符串仅包含ASCII字符时才有效。


7
因为这仅在编码为Windows-1252的情况下有效,该编码甚至不能容纳问题中的字母。
Mooing Duck

3
当您知道要处理ASCII时,这是最不容易出错的方式。将应用程序移植到较新的api时,这是一个突出的用例。
Sid Sarasvati 2014年

这是不是的方式。如果您使用的是Visual Studio,则应使用atlconv.h。检查其他答案。
lmiguelmh 2014年

7

使用Boost.Locale:

ws = boost::locale::conv::utf_to_utf<wchar_t>(s);

5

它的这种变体是我在现实生活中最喜欢的。它将输入(如果它是有效的 UTF-8)转换为相应的wstring。如果输入损坏,则从wstring单个字节中构造。如果您不能真正确定输入数据的质量,这将非常有用。

std::wstring convert(const std::string& input)
{
    try
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.from_bytes(input);
    }
    catch(std::range_error& e)
    {
        size_t length = input.length();
        std::wstring result;
        result.reserve(length);
        for(size_t i = 0; i < length; i++)
        {
            result.push_back(input[i] & 0xFF);
        }
        return result;
    }
}

1
我刚刚根据您的答案提出了这个问题stackoverflow.com/questions/49669048/…您能看一下

2

如果您有QT,并且懒于实现功能和东西,则可以使用

std :: string str; QString(str).toStdWString()


几乎可以,但是您应该以开头QString,因为QString构造器由于某种原因不能接受字符串。
bobsbeenjamin


很好 另外,您可以使用.c_str()让QString在构造函数中接受您的字符串。
miep

1

方法s2ws运作良好。希望会有所帮助。

std::wstring s2ws(const std::string& s) {
    std::string curLocale = setlocale(LC_ALL, ""); 
    const char* _Source = s.c_str();
    size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
    wchar_t *_Dest = new wchar_t[_Dsize];
    wmemset(_Dest, 0, _Dsize);
    mbstowcs(_Dest,_Source,_Dsize);
    std::wstring result = _Dest;
    delete []_Dest;
    setlocale(LC_ALL, curLocale.c_str());
    return result;
}

6
所有这些答案如何以不安全的方式分配动态内存,然后将数据从缓冲区复制到字符串?为什么没人摆脱不安全的中间人?
Mooing Duck 2013年

hahakubile,您能为ws2s提供类似的帮助吗?
cristian

1

根据我自己的测试(在Windows 8,vs2010中),mbstowcs实际上会损坏原始字符串,它仅适用于ANSI代码页。如果MultiByteToWideChar / WideCharToMultiByte也可能导致字符串损坏-但它们倾向于将不知道的字符替换为'?' 问号,但mbstowcs在遇到未知字符并在该点剪切字符串时往往会停止。(我已经在芬兰语窗口上测试了越南语字符)。

因此,与模拟ansi C函数相比,首选Multi * -windows api函数。

另外,我注意到将字符串从一个代码页编码到另一代码页的最短方法不是使用MultiByteToWideChar / WideCharToMultiByte api函数调用,而是使用其模拟ATL宏:W2A / A2W。

因此,如上所述的模拟功能听起来像:

wstring utf8toUtf16(const string & str)
{
   USES_CONVERSION;
   _acp = CP_UTF8;
   return A2W( str.c_str() );
}

_acp在USES_CONVERSION宏中声明。

或执行旧数据转换为新数据时经常会错过的功能:

string ansi2utf8( const string& s )
{
   USES_CONVERSION;
   _acp = CP_ACP;
   wchar_t* pw = A2W( s.c_str() );

   _acp = CP_UTF8;
   return W2A( pw );
}

但是请注意,这些宏占用大量堆栈-在使用W2A或A2W宏之后-请勿将循环用于同一功能的循环或递归循环-最好尽快返回,因此堆栈将免于临时转换。


1

串到串

std::wstring Str2Wstr(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

wstring到String

std::string Wstr2Str(const std::wstring& wstr)
{
    typedef std::codecvt_utf8<wchar_t> convert_typeX;
    std::wstring_convert<convert_typeX, wchar_t> converterX;
    return converterX.to_bytes(wstr);
}

1
该Str2Wstr的终止为0。无法再通过“ +”连接生成的wstring(就像在wstring s3 = s1 + s2中一样)。我将尽快解决此问题。首先必须对内存泄漏进行一些测试。
thewhiteambit

-2

string s = "おはよう"; 是一个错误。

您应该直接使用wstring:

wstring ws = L"おはよう";

1
那也不行。您必须将那些非BMP字符转换为C转义序列。
Dave Van den Eynde

3
@Dave:如果您的编译器支持源文件中的unicode,并且过去十年中的所有文件都支持unicode(Visual Studio,gcc等),它确实可以工作
Thomas Bonini 2010年

嗨,不管使用默认的系统编码(例如,我可能使用阿拉伯语作为默认的系统编码),L“おはよう”的源代码文件的编码应该如何工作?它应该是UTF-16,还是可以有不带BOM的UTF-8用于.cpp文件编码?
Afriza N. Arief

2
@afriza:没关系,只要您的编译支持它
Thomas Bonini 2010年

2
这不是错误;定义了“窄”字符串中的扩展字符以映射到多字节序列。只要操作系统支持,编译器就应该支持它,这是您要求的最低要求。
Potatoswatter

-2

使用此代码将您的字符串转换为wstring

std::wstring string2wString(const std::string& s){
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

int main(){
    std::wstring str="your string";
    std::wstring wStr=string2wString(str);
    return 0;
}

3
请注意,该问题没有提及Windows,并且此答案仅适用于Windows。
约翰·杰雷尔

CP_ACP无疑是错误的论点。突然之间,正在执行的线程的环境状态会影响代码的行为。不建议。在转换中指定固定的字符编码。(并考虑处理错误。)
IInspectable
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.