C ++ Converter string (ou char *) em wstring (ou wchar_t *)

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

Como eu atribuiria o conteúdo de s a ws?

Pesquisou no Google e usou algumas técnicas, mas elas não podem atribuir o conteúdo exato. O conteúdo está distorcido.

Samir
fonte
7
Eu não acho que stringsaceite caracteres de 8 bits. Já está codificado em UTF-8?
Kennytm
3
Qual é a codificação do seu sistema para criar "おはよう"uma string codificada pelo sistema?
SBI
Acredito que o MSVC aceite isso e faça uma codificação multibyte, talvez UTF-8.
Potatoswatter
1
@ Potatoswatter: MSVC não usa UTF-8 por padrão para QUALQUER COISA. Se você inserir esses personagens, ele pede que a codificação para converter o arquivo para, e o padrão é página de código 1252.
Mooing Duck
2
@ Samir: mais importante é o que é a codificação do arquivo ? Você pode mover essa sequência para o início do arquivo e mostrar um hexdump dessa parte? Provavelmente, podemos identificar isso a partir disso.
Mooing Duck

Respostas:

239

Supondo que a string de entrada no seu exemplo (お は よ う) seja codificada em UTF-8 (o que não é, pelo que parece, mas vamos assumir que seja por causa desta explicação :-)) de uma string Unicode de seu interesse, seu problema poderá ser totalmente resolvido apenas com a biblioteca padrão (C ++ 11 e mais recente).

A versão 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);

Exemplo compilável e executável online mais longo:

(Todos mostram o mesmo exemplo. Existem muitos para redundância ...)

Nota (antiga) :

Conforme apontado nos comentários e explicado em https://stackoverflow.com/a/17106065/6345, há casos em que o uso da biblioteca padrão para converter entre UTF-8 e UTF-16 pode gerar diferenças inesperadas nos resultados em diferentes plataformas . Para uma melhor conversão, considere std::codecvt_utf8o descrito em http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Nota (nova) :

Como o codecvtcabeçalho foi descontinuado no C ++ 17, foram levantadas algumas preocupações com a solução apresentada nesta resposta. No entanto, o comitê de padrões do C ++ adicionou uma declaração importante em http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html dizendo

esse componente da biblioteca deve ser retirado do anexo D, ao lado, até que uma substituição adequada seja padronizada.

Portanto, no futuro próximo, a codecvtsolução nesta resposta é segura e portátil.

Johann Gerell
fonte
2
Verifique o que você codificar salvar arquivos VS com
Johann Gerell
9
Esteja ciente de que este é apenas C ++ 11!
bk138
1
No minGW (gcc / g ++ 4.8.1 e -std = c ++ 11), o cabeçalho codecvt não existe. Existe uma alternativa?
Brian Jack
1
Por favor, forneça um exemplo std::codecvt_utf8para iniciantes
Noitidart 23/02
15
Observe que <codecvt>está obsoleto desde o C ++ 17.
tambre
47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}
Pietro M
fonte
93
Isso funciona apenas se todos os caracteres tiverem um byte único, ou seja, ASCII ou ISO-8859-1 . Qualquer coisa com vários bytes falhará miseravelmente, incluindo o UTF-8. A pergunta contém claramente caracteres de vários bytes.
Mark Ransom
28
Essa resposta é claramente insuficiente e não faz nada além de copiar caracteres estreitos como em caracteres largos. Veja as outras respostas, particularmente a de Johann Gerell, para saber como passar corretamente de uma sequência de caracteres de bytes múltiplos ou utf8 para uma sequência utf16.
DLRdave
10
essa resposta é perigosa e provavelmente será interrompida no sistema não-ascii. ou seja, um nome de arquivo árabe será mutilado por esse hack.
Stephen Stephen
9
Essa resposta é útil se você ignorar as nuances do corpo da pergunta e se concentrar no título da pergunta, que foi o que me trouxe aqui do Google. Como é, o título da pergunta é extremamente enganador e deve ser alterado para refletir a verdadeira pergunta que está sendo feita
Anne Quinn
3
Isso funciona apenas para caracteres ASCII de 7 bits. Para latin1, ele funciona apenas se char estiver configurado como não assinado. Se o tipo char for assinado (o que geralmente ocorre), caracteres> 127 fornecerão resultados incorretos.
huyc 16/05
32

Sua pergunta está subespecificada. Estritamente, esse exemplo é um erro de sintaxe. No entanto, std::mbstowcsé provavelmente o que você está procurando.

É uma função da biblioteca C e opera em buffers, mas aqui está um idioma fácil de usar, cortesia de TBohne (anteriormente 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.
Potatoswatter
fonte
1
string s = "お は よ う"; wchar_t * buf = novo wchar_t [s.size ()]; tamanho_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = distorcido
Samir
1
@ Samir: Você precisa garantir que a codificação em tempo de execução seja a mesma que a codificação em tempo de compilação. Pode ser necessário setlocaleou ajustar os sinalizadores do compilador. Não sei porque não uso o Windows, mas é por isso que não é um recurso comum. Considere a outra resposta, se possível.
Potatoswatter 04/04
1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
Mooing Duck
2
@WaffleSouffle Isso está desatualizado. Implementações contíguas são necessárias desde 2011 e as implementações encerram esses truques muito antes disso.
Potatoswatter
1
e alguns ambientes como mingw ainda não têm o cabeçalho codecvt assim algumas das soluções 'melhores' mais cedo se não funcionam ou seja, este problema ainda não tem boas soluções em mingw mesmo em Dez 2014
Brian Jack
18

Somente API do Windows, antes da implementação do C ++ 11, caso alguém precise:

#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);
}
Alex Che
fonte
Você pode otimizar isso. Não é necessário fazer cópia dupla da string usando a vector. Basta reservar os caracteres na string fazendo wstring strW(charsNeeded + 1);e, em seguida, usá-lo como tampão para a conversão: &strW[0]. Por último garantir última nula está presente após a conversão, fazendostrW[charsNeeded] = 0;
C00000FD
1
@ c00000fd, tanto quanto eu sei, o buffer interno std :: basic_string precisa ser contínuo apenas desde o padrão C ++ 11. Meu código é anterior ao C ++ 11, conforme observado na parte superior da postagem. Portanto, o código & strW [0] não seria compatível com o padrão e poderá travar legitimamente no tempo de execução.
18780 Alex
13

Se você estiver usando o Windows / Visual Studio e precisar converter uma string em wstring, poderá usar:

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

O mesmo procedimento para converter uma string para string (às vezes você precisará especificar uma página de código ):

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

Você pode especificar uma página de código e até UTF8 (isso é muito bom ao trabalhar com JNI / Java ). Uma maneira padrão de converter um std :: wstring em utf8 std :: string é mostrada nesta resposta .

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

Se você quiser saber mais sobre páginas de código, há um artigo interessante sobre Joel no Software: O Mínimo Absoluto Todo desenvolvedor de software deve saber absolutamente, positivamente, positivamente sobre Unicode e conjuntos de caracteres .

Essas macros CA2W (Converter ANSI para Wide = unicode) fazem parte das macros de conversão de string ATL e MFC , incluindo exemplos.

Às vezes, você precisará desativar o aviso de segurança # 4995 ', não conheço outra solução alternativa (isso acontece quando eu compilei o WindowsXp no VS2012).

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

Edit: Bem, de acordo com este artigo, o artigo de Joel parece ser: "embora divertido, é bastante claro sobre detalhes técnicos reais". Artigo: O que todo programador precisa absolutamente e positivamente de saber sobre codificação e conjuntos de caracteres para trabalhar com texto .

lmiguelmh
fonte
Desculpe, eu não sou um falante nativo de inglês. Edite como achar melhor.
lmiguelmh
O que há com o downvoter? O que há de errado com a resposta?
Lmiguelmh
Provavelmente, o fato de promover código não portátil.
Pavel Minaev 24/08/2015
Sim, foi por isso que afirmei que isso funciona apenas no Windows / Visual Studio. Mas, pelo menos, esta solução é correta, e não um presente:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh
Nota adicional: CA2W está no espaço de nome do ATL. (ATL :: CA2W)
Val
12

Aqui está uma maneira de combinar string, wstringe constantes de cadeia mista para wstring. Use a wstringstreamclasse.

Isso NÃO funciona para codificações de caracteres de vários bytes. Esta é apenas uma maneira idiota de jogar fora a segurança de tipo e expandir os caracteres de 7 bits do std :: string para os 7 bits inferiores de cada caractere do std: wstring. Isso só é útil se você tiver uma sequência ASCII de 7 bits e precisar chamar uma API que exija uma sequência ampla.

#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();
Mark Lakata
fonte
A resposta parece interessante. Você poderia explicar um pouco: isso funcionará para codificações de vários bytes e por que / como?
wh1t3cat1k
esquemas de codificação são ortogonais à classe de armazenamento. stringarmazena caracteres de 1 byte e wstringarmazena caracteres de 2 bytes. algo como utf8 armazena caracteres de vários bytes como uma série de valores de 1 byte, ou seja, em a string. as classes de string não ajudam na codificação. Eu não sou um especialista em codificar classes em c ++.
Mark Lakata
2
Alguma razão para que essa não seja a melhor resposta, dada a curta e simples? Algum caso que não cobre?
Ryuu
@ MarkLakata, li sua resposta ao primeiro comentário, mas ainda não tenho certeza. Funcionará para caracteres de vários bytes? Em outras palavras, não é propenso à mesma armadilha que esta resposta ?
precisa saber é o seguinte
@ Marc.2377 Isso NÃO funciona para codificações de caracteres de vários bytes. Esta é apenas uma maneira idiota de jogar fora a segurança de tipo e expandir os caracteres de std::string7 bits dos 7 bits inferiores de cada caractere std:wstring. Isso é útil apenas se você tiver uma sequência ASCII de 7 bits e precisar chamar uma API que exija uma sequência ampla. Veja stackoverflow.com/a/8969776/3258851 se precisar de algo mais sofisticado.
Mark Lakata
11

De char*para wstring:

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

De stringpara wstring:

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

Observe que isso só funciona bem se a sequência que está sendo convertida contiver apenas caracteres ASCII.

Ghominejad
fonte
7
Porque isso só funciona se a codificação for Windows-1252, que nem pode conter as letras da pergunta.
Mooing Duck
3
essa é a maneira menos propensa a erros quando você sabe que está lidando com ASCII. Esse é um caso de uso proeminente ao portar aplicativos para APIs mais recentes.
Sid Sarasvati
Este não é o caminho. Se você estiver usando o Visual Studio, use atlconv.h. Verifique as outras respostas.
Lmiguelmh
7

usando Boost.Locale:

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

Essa variante é a minha favorita na vida real. Ele converte a entrada, se for UTF-8 válido , na respectiva wstring. Se a entrada estiver corrompida, ela wstringserá construída a partir dos bytes únicos. Isso é extremamente útil se você realmente não tiver certeza da qualidade dos seus dados de entrada.

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;
    }
}
Matthias Ronge
fonte
1
Eu só lançou esta pergunta com base em sua resposta stackoverflow.com/questions/49669048/... você pode gentilmente dar uma olhada
MistyD
2

Se você tem QT e tem preguiça de implementar uma função e outras coisas, pode usar

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

Kadir Erdem Demir
fonte
Quase, mas você deve começar com a QString, porque o QStringconstrutor não pode aceitar uma string por algum motivo.
bobsbeenjamin
1
Você pode usar doc.qt.io/qt-5/qstring.html#fromStdString
Kadir Erdem Demir
Isso é legal. Além disso, você pode usar .c_str () para permitir que o QString aceite sua string no construtor.
miep 01/07
1

O método s2ws funciona bem. A esperança ajuda.

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;
}
hahakubile
fonte
6
O que há com todas essas respostas alocando memória dinâmica de maneira insegura e copiando os dados do buffer para a string? Por que ninguém se livra do intermediário inseguro?
Mooing Duck
hahakubile, você pode ajudar por favor com algo semelhante para o ws2s?
cristian
1

Com base nos meus próprios testes (no Windows 8, vs2010), o mbstowcs pode danificar a string original, funciona apenas com a página de código ANSI. Se MultiByteToWideChar / WideCharToMultiByte também puder causar corrupção de string - mas eles tendem a substituir caracteres que não conhecem por '?' pontos de interrogação, mas o mbstowcs tende a parar quando encontra caracteres desconhecidos e corta a corda nesse exato momento. (Eu testei caracteres vietnamitas em janelas finlandesas).

Portanto, prefira a função api da Multi-windows em vez das funções analógicas ansi C.

Além disso, o que eu notei que a maneira mais curta de codificar string de uma página de código para outra não é usar as funções de API api da MultiByteToWideChar / WideCharToMultiByte, mas suas macros analógicas ATL: W2A / A2W.

Portanto, a função analógica, como mencionado acima, soa como:

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

_acp é declarado na macro USES_CONVERSION.

Ou também a função que frequentemente sinto falta ao realizar a conversão de dados antigos para um novo:

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

   _acp = CP_UTF8;
   return W2A( pw );
}

Mas observe que essas macro usam muito a pilha - não use para loops ou loops recursivos para a mesma função - depois de usar a macro W2A ou A2W - é melhor retornar o mais rápido possível, para que a pilha seja liberada da conversão temporária.

TarmoPikaro
fonte
1

String para wstring

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 para 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);
}
Isma Rekathakusuma
fonte
1
Este Str2Wstr tem um problema com a terminação 0. Não é possível concatenar mais as seqüências de caracteres geradas via "+" (como na sequência de caracteres s3 = s1 + s2). Vou postar uma resposta em breve resolvendo esse problema. Primeiro, é necessário testar alguns vazamentos de memória.
thewhiteambit
-2

string s = "おはよう"; é um erro.

Você deve usar o wstring diretamente:

wstring ws = L"おはよう";
Thomas Bonini
fonte
1
Isso também não vai funcionar. Você precisará converter esses caracteres não-BMP em seqüências de escape em C.
Dave Van den Eynde 04/04
3
@ Dave: ela não funciona se suas sustentações do compilador Unicode em arquivos de origem, e todos os da última década fazer (visual studio, gcc, ...)
Thomas Bonini
Olá, independentemente da codificação padrão do sistema (posso ter o árabe como codificação padrão do sistema, por exemplo), qual deve ser a codificação do arquivo de código-fonte para L "お は よ う" funcionar? deveria estar em UTF-16 ou posso ter UTF-8 sem BOM para a codificação de arquivo .cpp?
Afriza N. Arief 12/08
2
@afriza: isso realmente não importa, contanto que seus apoios compilá-lo
Thomas Bonini
2
Não é um erro; caracteres estendidos em uma cadeia "estreita" são definidos para mapear para seqüências multibyte. O compilador deve suportá-lo desde que o sistema operacional o faça, o mínimo que você pode pedir.
Potatoswatter
-2

use esse código para converter sua string em 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;
}
jaguar
fonte
3
Observe que a pergunta não menciona o Windows e esta resposta é somente para Windows.
Johann Gerell 27/08/2015
CP_ACPé certamente o argumento errado. De repente, o estado do ambiente do encadeamento em execução afeta o comportamento do código. Não aconselhável. Especifique uma codificação de caracteres fixa na sua conversão. (E considere o tratamento de erros.)
IInspectable