2008-09-29 20 views
65

¿Es posible convertir cadenas UTF8 en std :: string a std :: wstring y viceversa de una manera independiente de plataforma? En una aplicación de Windows usaría MultiByteToWideChar y WideCharToMultiByte. Sin embargo, el código está compilado para múltiples sistemas operativos y estoy limitado a la biblioteca estándar de C++.UTF8 a/desde conversión de ancho de pila en STL

+3

A propósito, la biblioteca estándar de C++ no se llama STL; el STL es solo una pequeña subsección de la biblioteca estándar de C++. En este caso, creo que está pidiendo funcionalidad en la biblioteca estándar de C++, y he respondido en consecuencia. –

+5

No ha especificado con qué codificación desea terminar. wstring no especifica ninguna codificación particular. Por supuesto, sería natural convertir a utf32 en plataformas donde wchar_t tiene 4 bytes de ancho y utf16 si wchar_t tiene 2 bytes. ¿Es eso lo que quieres? – jalf

Respuesta

2

Puede usar el codecvt locale facet. Hay una especialización específica definida, codecvt<wchar_t, char, mbstate_t> que puede ser útil para usted, aunque el comportamiento de eso es específico del sistema y no garantiza la conversión a UTF-8 de ninguna manera.

+1

Hacer la codificación/decodificación según la configuración regional es una mala idea. Tal como dijiste: "no garantiza". –

+0

@TylerLong, obviamente, uno debe configurar la instancia de std :: locale específicamente para la conversión requerida. – Basilevs

+0

@Basilevs Sigo pensando que utilizar la configuración regional para codificar/decodificar es incorrecto. La forma correcta es configurar 'encoding' en lugar de' locale'. Por lo que puedo decir, no existe una localización tal que pueda representar ** cada ** único carácter Unicode. Digamos que quiero codificar una cadena que contenga todos los caracteres Unicode, ¿qué entorno me sugieres que configure? Arrántrame si estoy equivocado –

-1

No creo que haya una forma portátil de hacerlo. C++ no conoce la codificación de sus caracteres multibyte.

Como sugirió Chris, su mejor opción es jugar con codecvt.

+0

La pregunta dice "UTF8", por lo que "la codificación de sus caracteres multibyte" es conocida. –

23

Puede extraer utf8_codecvt_facet de Boost serialization library.

Su ejemplo de uso:

typedef wchar_t ucs4_t; 

    std::locale old_locale; 
    std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>); 

    // Set a New global locale 
    std::locale::global(utf8_locale); 

    // Send the UCS-4 data out, converting to UTF-8 
    { 
    std::wofstream ofs("data.ucd"); 
    ofs.imbue(utf8_locale); 
    std::copy(ucs4_data.begin(),ucs4_data.end(), 
      std::ostream_iterator<ucs4_t,ucs4_t>(ofs)); 
    } 

    // Read the UTF-8 data back in, converting to UCS-4 on the way in 
    std::vector<ucs4_t> from_file; 
    { 
    std::wifstream ifs("data.ucd"); 
    ifs.imbue(utf8_locale); 
    ucs4_t item = 0; 
    while (ifs >> item) from_file.push_back(item); 
    } 

Busque utf8_codecvt_facet.hpp y utf8_codecvt_facet.cpp archivos de fuentes de impulso.

+0

Pensé que tenía que imbuir la secuencia antes de abrirla, de lo contrario, se ignora el imbue. –

+0

Martin, parece funcionar con Visual Studio 2005: 0x41a se convierte satisfactoriamente a la secuencia {0xd0, 0x9a} UTF-8. – Constantin

13

Existen varias formas de hacerlo, pero los resultados dependen de las codificaciones de los caracteres en las variables string y wstring.

Si conoce la string es ASCII, sólo tiene que utilizar el constructor iterador wstring 's:

string s = "This is surely ASCII."; 
wstring w(s.begin(), s.end()); 

Si su string tiene alguna otra codificación, sin embargo, obtendrá resultados muy malos. Si la codificación es Unicode, puede echarle un vistazo al ICU project, que proporciona un conjunto de bibliotecas multiplataforma que convierte desde y hacia todo tipo de codificaciones Unicode.

Si su string contiene caracteres en una página de códigos, entonces puede $ DEITY tener piedad de su alma.

+2

ICU se convierte también/de cada codificación de caracteres que he encontrado. Es enorme. –

14

La definición del problema establece explícitamente que la codificación de caracteres de 8 bits es UTF-8. Eso hace que esto sea un problema trivial; todo lo que requiere es un poco de tiempo para convertir una especificación UTF a otra.

Solo mira las codificaciones en estas páginas de Wikipedia para UTF-8, UTF-16 y UTF-32.

El principio es simple: revise la entrada y ensamble un punto de código Unicode de 32 bits según una especificación UTF, luego emita el punto de código de acuerdo con la otra especificación. Los puntos de código individuales no necesitan traducción, como se requeriría con cualquier otra codificación de caracteres; eso es lo que hace que esto sea un problema simple.

Aquí hay una implementación rápida de wchar_t para la conversión UTF-8 y viceversa. Asume que la entrada ya está codificada correctamente; aquí se aplica el viejo dicho "Basura adentro, basura afuera". Creo que verificar la codificación se realiza mejor como un paso separado.

std::string wchar_to_UTF8(const wchar_t * in) 
{ 
    std::string out; 
    unsigned int codepoint = 0; 
    for (in; *in != 0; ++in) 
    { 
     if (*in >= 0xd800 && *in <= 0xdbff) 
      codepoint = ((*in - 0xd800) << 10) + 0x10000; 
     else 
     { 
      if (*in >= 0xdc00 && *in <= 0xdfff) 
       codepoint |= *in - 0xdc00; 
      else 
       codepoint = *in; 

      if (codepoint <= 0x7f) 
       out.append(1, static_cast<char>(codepoint)); 
      else if (codepoint <= 0x7ff) 
      { 
       out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f))); 
       out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f))); 
      } 
      else if (codepoint <= 0xffff) 
      { 
       out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f))); 
       out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f))); 
       out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f))); 
      } 
      else 
      { 
       out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07))); 
       out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f))); 
       out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f))); 
       out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f))); 
      } 
      codepoint = 0; 
     } 
    } 
    return out; 
} 

El código anterior funciona tanto para la entrada UTF-16 y UTF-32, simplemente porque la gama d800 través de dfff son puntos de código no válido; indican que está decodificando UTF-16. Si sabe que wchar_t es de 32 bits, entonces podría eliminar algún código para optimizar la función.

std::wstring UTF8_to_wchar(const char * in) 
{ 
    std::wstring out; 
    unsigned int codepoint; 
    while (*in != 0) 
    { 
     unsigned char ch = static_cast<unsigned char>(*in); 
     if (ch <= 0x7f) 
      codepoint = ch; 
     else if (ch <= 0xbf) 
      codepoint = (codepoint << 6) | (ch & 0x3f); 
     else if (ch <= 0xdf) 
      codepoint = ch & 0x1f; 
     else if (ch <= 0xef) 
      codepoint = ch & 0x0f; 
     else 
      codepoint = ch & 0x07; 
     ++in; 
     if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff)) 
     { 
      if (sizeof(wchar_t) > 2) 
       out.append(1, static_cast<wchar_t>(codepoint)); 
      else if (codepoint > 0xffff) 
      { 
       out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10))); 
       out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff))); 
      } 
      else if (codepoint < 0xd800 || codepoint >= 0xe000) 
       out.append(1, static_cast<wchar_t>(codepoint)); 
     } 
    } 
    return out; 
} 

Una vez más si sabe que wchar_t es de 32 bits que podría quitar algo de código de esta función, pero en este caso no debe hacer ninguna diferencia. La expresión sizeof(wchar_t) > 2 es conocida en tiempo de compilación, por lo que cualquier compilador decente reconocerá el código muerto y lo eliminará.

+0

No veo nada relacionado con std :: string que contenga cadenas codificadas en UTF-8 en la pregunta original: "¿Es posible convertir std :: string en std :: wstring y viceversa de una manera independiente de la plataforma?" –

+1

UTF-8 se especifica en el título de la publicación. Tiene razón en que falta en el cuerpo del texto. –

+0

Doh, tienes razón. UTF8-CPP luego :) –

4

ConvertUTF.h ConvertUTF.c

crédito a bames53 para proporcionar versiones actualizadas

+2

Se puede descargar desde [aquí] (http://www.koders.com/c/fid112E1609930ADAE7CA143040A834BAC0165DCD65.aspx?s=ConvertUTF.h) –

1

UTFConverter - echa un vistazo a esta biblioteca. Hace tal conversión, pero también necesita la clase ConvertUTF - Lo he encontrado here

38

Hace 5 años hice esta pregunta. Este hilo fue muy útil para mí en ese momento, llegué a una conclusión y luego continué con mi proyecto. Es gracioso que necesité algo similar recientemente, totalmente ajeno a ese proyecto del pasado. Mientras buscaba posibles soluciones, tropecé con mi propia pregunta :)

La solución que elegí ahora se basa en C++ 11. Las bibliotecas de impulso que menciona Constantin en his answer ahora son parte del estándar. Si reemplazamos std :: wstring con el nuevo std :: string tipo u16string, a continuación, las conversiones se vería así:

UTF-8 a UTF-16

std::string source; 
... 
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert; 
std::u16string dest = convert.from_bytes(source);  

UTF-16 a UTF-8

std::u16string source; 
... 
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert; 
std::string dest = convert.to_bytes(source);  

Como se ve en las otras respuestas, hay varios enfoques del problema. Es por eso que me abstengo de elegir una respuesta aceptada.

+0

wstring implica 2 o 4 bytes en lugar de caracteres de un solo byte. ¿Dónde está la pregunta para cambiar de codificación utf8? –

+1

Tengo un rendimiento pobremente extraño con codecvt, mira aquí para más detalles: http://stackoverflow.com/questions/26196686/utf8-utf16-codecvt-poor-performance –

+0

Creo que deberías aceptar esta respuesta. Seguro que hay varias formas de resolver esto, pero esta es la única solución portátil que no necesita una biblioteca. – Navin

Cuestiones relacionadas