C ++ Convertir cadena (o char *) a wstring (o wchar_t *)

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

¿Cómo asignaría el contenido de s a ws?

Busqué en Google y usé algunas técnicas, pero no pueden asignar el contenido exacto. El contenido está distorsionado.

Samir
fuente
77
No creo que stringsacepte caracteres de> 8 bits. ¿Ya está codificado en UTF-8?
kennytm
3
¿Cuál es la codificación de su sistema que haría "おはよう"una cadena codificada por el sistema?
sbi
Creo que MSVC aceptará eso y lo convertirá en una codificación multibyte, tal vez UTF-8.
Potatoswatter
1
@Potatoswatter: MSVC no usa UTF-8 por defecto para NADA. Si ingresa esos caracteres, le pregunta a qué codificación convertir el archivo y por defecto a la página de códigos 1252.
Mooing Duck
2
@Samir: lo más importante es ¿cuál es la codificación del archivo ? ¿Puedes mover esa cadena al comienzo del archivo y mostrar un hexdump de esa parte? Probablemente podamos identificarlo a partir de eso.
Mooing Duck

Respuestas:

239

Suponiendo que la cadena de entrada en su ejemplo (お は よ う) es una codificación UTF-8 (que no lo es, por lo que parece, pero supongamos que es por el bien de esta explicación :-)) representación de una cadena Unicode de su interés, entonces su problema puede resolverse completamente con la biblioteca estándar (C ++ 11 y más reciente) solo.

La versión 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);

Ejemplo compilable y ejecutable en línea más largo:

(Todos muestran el mismo ejemplo. Hay muchos para la redundancia ...)

Nota (antigua) :

Como se señaló en los comentarios y se explicó en https://stackoverflow.com/a/17106065/6345, hay casos en que el uso de la biblioteca estándar para convertir entre UTF-8 y UTF-16 podría dar diferencias inesperadas en los resultados en diferentes plataformas . Para una mejor conversión, considere std::codecvt_utf8como se describe en http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Nota (nueva) :

Dado que el codecvtencabezado está en desuso en C ++ 17, surgieron algunas preocupaciones sobre la solución presentada en esta respuesta. Sin embargo, el comité de estándares de C ++ agregó una declaración importante en http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html diciendo

este componente de la biblioteca debe retirarse del Anexo D, junto a él, hasta que se estandarice un reemplazo adecuado.

Entonces, en el futuro previsible, la codecvtsolución en esta respuesta es segura y portátil.

Johann Gerell
fuente
2
Compruebe con qué codificación guarda los archivos VS
Johann Gerell
9
¡Tenga en cuenta que esto es solo para C ++ 11!
bk138
1
En minGW (gcc / g ++ 4.8.1 y -std = c ++ 11) el encabezado codecvt no existe. ¿Hay una alternativa?
Brian Jack
1
¿Podría dar un ejemplo std::codecvt_utf8para principiantes?
Noitidart
15
Tenga en cuenta que <codecvt>está en desuso desde 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
fuente
93
Esto solo funciona si todos los caracteres son de un solo byte, es decir, ASCII o ISO-8859-1 . Cualquier cosa de varios bytes fallará miserablemente, incluido UTF-8. La pregunta contiene claramente caracteres de varios bytes.
Mark Ransom
28
Esta respuesta es claramente insuficiente y no hace nada más que copiar caracteres estrechos tal como está en caracteres anchos. Vea las otras respuestas, particularmente la de Johann Gerell, para saber cómo pasar de una cadena codificada de múltiples bytes o utf8 a una cadena de caracteres utf16.
DLRdave
10
Esta respuesta es peligrosa y probablemente se romperá en un sistema que no sea ASCII. es decir, este nombre pirateará un nombre de archivo árabe.
Stephen
9
Esta respuesta es útil si ignora los matices del cuerpo de la pregunta y se enfoca en el título de la pregunta, que es lo que me trajo aquí de Google. Tal como está, el título de la pregunta es extremadamente engañoso y debe modificarse para reflejar la verdadera pregunta que se hace
Anne Quinn,
3
Esto funciona solo para caracteres ASCII de 7 bits. Para latin1, funciona solo si char está configurado como unsigned. Si el tipo char está firmado (que suele ser el caso), los caracteres> 127 darán resultados incorrectos.
huyc
32

Su pregunta no está especificada. Estrictamente, ese ejemplo es un error de sintaxis. Sin embargo, std::mbstowcses probablemente lo que estás buscando.

Es una función de biblioteca C y funciona en buffers, pero aquí hay un modismo fácil de usar, cortesía 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.
Agua de patata
fuente
1
cadena s = "お は よ う"; wchar_t * buf = nuevo wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); wstring ws (buf, num_chars); // ws = distorsionado
Samir
1
@Samir: debe asegurarse de que la codificación en tiempo de ejecución sea la misma que la codificación en tiempo de compilación. Es posible que necesite setlocaleo ajuste los indicadores del compilador. No lo sé porque no uso Windows, pero es por eso que no es una característica común. Considere la otra respuesta si es posible.
Potatoswatter
1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
Mooing Duck
2
@WaffleSouffle Eso está desactualizado. Se han requerido implementaciones contiguas desde 2011 y las implementaciones abandonaron dichos trucos mucho antes de eso.
Potatoswatter
1
y algunos entornos como mingw todavía no tienen el encabezado codecvt, por lo que algunas de las 'mejores' soluciones anteriores no funcionan, lo que significa que este problema todavía no tiene buenas soluciones en mingw, incluso a partir de diciembre de 2014
Brian Jack,
18

Solo API de Windows, implementación previa a C ++ 11, en caso de que alguien lo necesite:

#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
fuente
Puedes optimizarlo. No hay necesidad de hacer una doble copia de la cadena usando a vector. Simplemente reservar los caracteres de la cadena haciendo wstring strW(charsNeeded + 1);y luego usarlo como memoria intermedia para la conversión: &strW[0]. Por último, asegúrese de que el último nulo esté presente después de la conversión haciendostrW[charsNeeded] = 0;
c00000fd
1
@ c00000fd, que yo sepa, el buffer interno std :: basic_string debe ser continuo solo desde el estándar C ++ 11. Mi código es pre C ++ 11, como se indica en la parte superior de la publicación. Por lo tanto, el código & strW [0] no sería estándar y podría bloquearse legítimamente en tiempo de ejecución.
Alex Che
13

Si está utilizando Windows / Visual Studio y necesita convertir una cadena a wstring, puede 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());

El mismo procedimiento para convertir un wstring a cadena (a veces necesitará especificar una página de códigos ):

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

Puede especificar una página de códigos e incluso UTF8 (eso es bastante bueno cuando se trabaja con JNI / Java ). En esta respuesta se muestra una forma estándar de convertir un std :: wstring a 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);
}

Si desea saber más acerca de las páginas de códigos, hay un artículo interesante sobre Joel sobre software: el mínimo absoluto que todo desarrollador de software debe saber absolutamente, positivamente sobre Unicode y los conjuntos de caracteres .

Estas macros CA2W (Convertir Ansi a Wide = unicode) son parte de las macros de conversión de cadenas ATL y MFC , incluidos los ejemplos.

A veces necesitará deshabilitar la advertencia de seguridad # 4995 ', no sé de otra solución (para mí sucede cuando compilé para Windows XP en VS2012).

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

Editar: Bueno, de acuerdo con este artículo, el artículo de Joel parece ser: "aunque entretenido, es bastante ligero en detalles técnicos reales". Artículo: Lo que todo programador absolutamente necesita saber sobre la codificación y los juegos de caracteres para trabajar con texto .

lmiguelmh
fuente
Lo siento, no soy un hablante nativo de inglés. Edite como mejor le parezca.
lmiguelmh
¿Qué pasa con el votante? ¿Qué hay de malo en la respuesta?
lmiguelmh
Probablemente el hecho de que promueve código no portátil.
Pavel Minaev
Sí, por eso dije que esto solo funciona en Windows / Visual Studio. Pero al menos esta solución es correcta, y no esta:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh
Nota adicional: CA2W está bajo el espacio de nombres de ATL. (ATL :: CA2W)
Val
12

Aquí hay una forma de combinar string, wstringy constantes de cadena mixtas para wstring. Usa la wstringstreamclase.

Esto NO funciona para codificaciones de caracteres de varios bytes. Esta es solo una manera tonta de desechar la seguridad de tipos y expandir caracteres de 7 bits desde std :: string a los 7 bits inferiores de cada carácter de std: wstring. Esto solo es útil si tiene cadenas ASCII de 7 bits y necesita llamar a una API que requiere cadenas anchas.

#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
fuente
La respuesta parece interesante. ¿Podría explicar un poco: esto funcionará para codificaciones de varios bytes y por qué / cómo?
wh1t3cat1k
Los esquemas de codificación son ortogonales a la clase de almacenamiento. stringalmacena caracteres de 1 byte y wstringalmacena 2 caracteres de byte. algo así como utf8 almacena caracteres de mulitbytes como una serie de valores de 1 byte, es decir, en a string. Las clases de cadena no ayudan con la codificación. No soy un experto en codificar clases en c ++.
Mark Lakata
2
¿Alguna razón por la cual esta no es la mejor respuesta, dado lo breve y simple que es? ¿Algún caso que no cubra?
Ryuu
@ MarkLakata, leí tu respuesta al primer comentario pero todavía no estoy seguro. ¿Funcionará para caracteres de varios bytes? En otras palabras, ¿no es propenso a la misma trampa que esta respuesta ?
Marc.2377
@ Marc.2377 Esto NO funciona para codificaciones de caracteres de varios bytes. Esta es solo una manera tonta de desechar la seguridad de tipo y expandir caracteres de 7 bits std::stringhacia los 7 bits inferiores de cada carácter de std:wstring. Esto solo es útil si tiene cadenas ASCII de 7 bits y necesita llamar a una API que requiere cadenas anchas. Mire stackoverflow.com/a/8969776/3258851 si necesita algo más sofisticado.
Mark Lakata
11

De char*a wstring:

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

De stringa wstring:

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

Tenga en cuenta que esto solo funciona bien si la cadena que se convierte contiene solo caracteres ASCII.

Ghominejad
fuente
77
Porque esto solo funciona si la codificación es Windows-1252, que ni siquiera puede contener las letras de la pregunta.
Mooing Duck
3
esta es la forma de hacerlo menos propensa a errores, cuando sabes que estás tratando con ASCII. Lo cual es un caso de uso prominente al portar aplicaciones a las API más nuevas.
Sid Sarasvati
Este no es el camino. Si está utilizando Visual Studio, debe usarlo atlconv.h. Verifica las otras respuestas.
lmiguelmh
7

usando Boost.Locale:

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

Esta variante es mi favorita en la vida real. Convierte la entrada, si es válida UTF-8, a la respectiva wstring. Si la entrada está dañada, wstringse construye a partir de los bytes individuales. Esto es extremadamente útil si realmente no puede estar seguro de la calidad de sus datos 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
fuente
1
Acabo de lanzar esta pregunta en función de su respuesta stackoverflow.com/questions/49669048/… ¿ pueden echar un vistazo
MistyD
2

Si tienes QT y eres flojo para implementar una función y otras cosas, puedes usar

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

Kadir Erdem Demir
fuente
Casi, pero deberías comenzar con a QString, porque el QStringconstructor no puede aceptar una cadena por alguna razón.
bobsbeenjamin
1
Puede usar doc.qt.io/qt-5/qstring.html#fromStdString
Kadir Erdem Demir
Esto es bonito. Además, puede usar .c_str () para permitir que QString acepte su cadena en el constructor.
miep
1

El método s2ws funciona bien. La esperanza ayuda.

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
fuente
66
¿Qué sucede con todas estas respuestas asignando memoria dinámica de una manera insegura y luego copiando los datos del búfer a la cadena? ¿Por qué nadie se deshace del intermediario inseguro?
Mooing Duck
jajakubile, ¿puedes ayudarme con algo similar para ws2s?
cristian
1

Según mis propias pruebas (en Windows 8, vs2010), mbstowcs puede dañar la cadena original, solo funciona con la página de códigos ANSI. Si MultiByteToWideChar / WideCharToMultiByte también puede causar daños en la cadena, pero tienden a reemplazar los caracteres que no conocen con '?' los signos de interrogación, pero mbstowcs tiende a detenerse cuando encuentra caracteres desconocidos y corta cadenas en ese mismo punto. (He probado caracteres vietnamitas en ventanas finlandesas).

Por lo tanto, prefiera la función Multi * -windows api sobre las funciones analógicas ansi C.

Además, lo que he notado de la manera más corta para codificar cadenas de una página de códigos a otra no es usar llamadas de función api MultiByteToWideChar / WideCharToMultiByte sino sus macros analógicas ATL: W2A / A2W.

Entonces, la función analógica como se mencionó anteriormente sonaría así:

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

_acp se declara en la macro USES_CONVERSION.

O también la función que a menudo echo de menos al realizar una conversión de datos antigua a una nueva:

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

   _acp = CP_UTF8;
   return W2A( pw );
}

Pero tenga en cuenta que esas macros usan mucho stack - no las use para bucles o bucles recursivos para la misma función - después de usar la macro W2A o A2W - mejor devolver ASAP, por lo que la pila se liberará de la conversión temporal.

TarmoPikaro
fuente
1

Cadena a 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 a 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
fuente
1
Este Str2Wstr tiene un problema con la terminación 0. Ya no es posible concatenar las cadenas w generadas a través de "+" (como en wstring s3 = s1 + s2). Pronto publicaré una respuesta para resolver este problema. Primero tiene que hacer algunas pruebas para detectar pérdidas de memoria.
thewhiteambit
-2

string s = "おはよう"; es un error

Debe usar wstring directamente:

wstring ws = L"おはよう";
Thomas Bonini
fuente
1
Eso tampoco va a funcionar. Tendrá que convertir esos caracteres no BMP en secuencias de escape C.
Dave Van den Eynde
3
@Dave: funciona si su compilador admite unicode en los archivos fuente, y todos los de la última década lo hacen (visual studio, gcc, ...)
Thomas Bonini
Hola, independientemente de la codificación predeterminada del sistema (por ejemplo, puedo tener el árabe como mi codificación predeterminada del sistema), ¿cuál debería ser la codificación del archivo de código fuente para que funcione L "お は よ う"? ¿debería estar en UTF-16, o puedo tener UTF-8 sin BOM para la codificación del archivo .cpp?
Afriza N. Arief
2
@afriza: realmente no importa mientras su compilación lo admita
Thomas Bonini
2
No es un error; Los caracteres extendidos en una cadena "estrecha" se definen para mapear a secuencias multibyte. El compilador debe admitirlo siempre que el sistema operativo lo haga, que es lo mínimo que puede pedir.
Potatoswatter
-2

usa este código para convertir tu cadena a 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
fuente
3
Tenga en cuenta que la pregunta no menciona a Windows y esta respuesta es solo para Windows.
Johann Gerell
CP_ACPEs sin duda el argumento equivocado. De repente, el estado del entorno del hilo en ejecución tiene un efecto en el comportamiento del código. No recomendable. Especifique una codificación de caracteres fija en su conversión. (Y considere los errores de manejo.)
Inspectable