Pregunta std :: wstring VS std :: string


No soy capaz de entender las diferencias entre std::string y std::wstring. Lo sé wstring admite caracteres anchos como caracteres Unicode. Tengo las siguientes preguntas:

  1. Cuando debería usar std::wstring encima std::string?
  2. Poder std::string mantener todo el conjunto de caracteres ASCII, incluidos los caracteres especiales?
  3. Es std::wstring compatible con todos los compiladores populares de C ++?
  4. ¿Qué es exactamente un "personaje ancho"¿?"

643
2017-12-31 04:08


origen


Respuestas:


string? wstring?

std::string es un basic_string plantilla en un chary std::wstring en un wchar_t.

char vs. wchar_t

char se supone que tiene un personaje, generalmente un personaje de 1 byte. wchar_t se supone que tiene un personaje ancho, y luego, las cosas se ponen difíciles: en Linux, un wchar_t es de 4 bytes, mientras que en Windows, es de 2 bytes

qué pasa Unicode, ¿entonces?

El problema es que ni char ni wchar_t está directamente relacionado con Unicode.

En Linux?

Tomemos un sistema operativo Linux: mi sistema Ubuntu ya es consciente de unicode. Cuando trabajo con una cadena de caracteres, está codificada de forma nativa en UTF-8 (es decir, cadena de caracteres Unicode). El siguiente código:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(bytes)     :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(bytes)    :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

da como resultado el siguiente texto:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(bytes)     : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(bytes)    : 111 108 233

Verás el texto "olé" en char está realmente construido con cuatro caracteres: 110, 108, 195 y 169 (sin contar el cero posterior). (Te dejaré estudiar el wchar_t código como ejercicio)

Por lo tanto, cuando se trabaja con un char en Linux, generalmente debería terminar usando Unicode sin siquiera saberlo. Y como std :: string funciona con char, std :: string ya está listo para Unicode.

Tenga en cuenta que std :: string, al igual que la cadena C API, considerará que la cadena "olé" tiene 4 caracteres, no tres. Por lo tanto, debe tener cuidado al truncar / jugar con caracteres Unicode porque está prohibida una combinación de caracteres en UTF-8.

En Windows?

En Windows, esto es un poco diferente. Win32 tuvo que soportar una gran cantidad de aplicaciones que trabajan con char y en diferentes Conjuntos/páginas de códigos producido en todo el mundo, antes del advenimiento de Unicode.

Entonces su solución fue interesante: si una aplicación funciona con char, entonces las cadenas de caracteres se codifican / imprimen / muestran en las etiquetas de la GUI usando el juego de caracteres local / página de códigos en la máquina. Por ejemplo, "olé" sería "olé" en un Windows localizado en francés, pero sería algo diferente en un Windows localizado en cirílico ("olй" si usa Windows-1251) Por lo tanto, las "aplicaciones históricas" generalmente seguirán funcionando de la misma manera.

Para aplicaciones basadas en Unicode, Windows usa wchar_t, que tiene 2 bytes de ancho y está codificado en UTF-16, que es codificado en Unicode en caracteres de 2 bytes (o al menos, el UCS-2 más compatible, que es casi lo mismo que IIRC).

Aplicaciones que usan char se dice "multibyte" (porque cada glifo está compuesto por uno o más chars), mientras que las aplicaciones que usan wchar_t se dice "ancho" (porque cada glifo está compuesto por uno o dos wchar_t. Ver MultiByteToWideChar y WideCharToMultiByte Win32 conversion API para más información.

Por lo tanto, si trabajas en Windows, mal quiero usar wchar_t (a menos que use un marco que lo oculte, como GTK + o QT...). El hecho es que detrás de escena, Windows funciona con wchar_t cadenas, por lo que incluso las aplicaciones históricas tendrán su char cadenas convertidas en wchar_t cuando se utiliza API como SetWindowText (función de API de bajo nivel para establecer la etiqueta en una GUI de Win32).

Problemas de memoria?

UTF-32 tiene 4 bytes por caracteres, por lo que no hay mucho que agregar, solo que un texto UTF-8 y texto UTF-16 siempre usarán menos o la misma cantidad de memoria que un texto UTF-32 (y usualmente menos )

Si hay un problema de memoria, entonces debe saber que para la mayoría de los idiomas occidentales, el texto UTF-8 usará menos memoria que la misma UTF-16.

Sin embargo, para otros idiomas (chino, japonés, etc.), la memoria utilizada será la misma o mayor para UTF-8 que para UTF-16.

Con todo, UTF-16 utilizará principalmente 2 bytes por caracteres (a menos que esté tratando con algún tipo de glifos de lenguaje esotérico (Klingon? Elvish?), Mientras que UTF-8 gastará de 1 a 4 bytes.

Ver http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 para más información.

Conclusión

1. Cuando debería usar std :: wstring sobre std :: string?

En Linux? Casi nunca (§).
En Windows? Casi siempre (§).
En código multiplataforma? Depende de su kit de herramientas ...

(§): a menos que use un conjunto de herramientas / marco que diga lo contrario

2. ¿Puede std :: string contener todo el conjunto de caracteres ASCII incluyendo caracteres especiales?

Aviso: Una std :: string es adecuada para contener un búfer 'binario', ¡donde std :: wstring no lo es!

En Linux? Sí.
En Windows? Solo caracteres especiales disponibles para la configuración regional actual del usuario de Windows.

Editar (Después de un comentario de Johann Gerell) una std :: string será suficiente para manejar todas las cadenas basadas en char (cada char es un número del 0 al 255). Pero:

  1. Se supone que ASCII va de 0 a 127. Los caracteres superiores no son ASCII.
  2. un carácter de 0 a 127 se mantendrá correctamente
  3. un carácter entre 128 y 255 tendrá un significado dependiendo de su codificación (unicode, no unicode, etc.), pero podrá contener todos los glifos Unicode siempre que estén codificados en UTF-8.

3. ¿Es std :: wstring compatible con casi todos los compiladores populares de C ++?

En su mayoría, con la excepción de los compiladores basados ​​en GCC que se transfieren a Windows
Funciona en mi g ++ 4.3.2 (bajo Linux), y utilicé la API Unicode en Win32 desde Visual C ++ 6.

4. ¿Qué es exactamente un personaje ancho?

En C / C ++, es un tipo de personaje escrito wchar_t que es más grande que el simple char tipo de caracter. Se supone que debe usarse para poner caracteres internos cuyos índices (como glifos Unicode) son mayores que 255 (o 127, dependiendo ...)


884
2017-12-31 12:47



Recomiendo evitar std::wstring en Windows o en otro lugar, excepto cuando lo requiera la interfaz, o en cualquier lugar cerca de las llamadas a la API de Windows y las respectivas conversiones de codificación como azúcar sintáctico.

Mi punto de vista se resume en http://utf8everywhere.org de la cual soy coautor.

A menos que su aplicación esté centrada en las llamadas API, p. principalmente la aplicación de interfaz de usuario, la sugerencia es almacenar cadenas Unicode en std :: string y codificadas en UTF-8, realizando conversiones cerca de llamadas API. Los beneficios descritos en el artículo superan la aparente molestia de la conversión, especialmente en aplicaciones complejas. Esto es doblemente para el desarrollo de múltiples plataformas y bibliotecas.

Y ahora, respondiendo tus preguntas:

  1. Algunas pocas razones débiles. Existe por razones históricas, donde se creía que los widechars eran la forma correcta de soportar Unicode. Ahora se usa para interfaces API que prefieren cadenas UTF-16. Los uso solo en las inmediaciones de tales llamadas API.
  2. Esto no tiene nada que ver con std :: string. Puede contener cualquier codificación que pongas en él. La única pregunta es cómo  tratar su contenido Mi recomendación es UTF-8, por lo que será capaz de contener todos los caracteres Unicode correctamente. Es una práctica común en Linux, pero creo que los programas de Windows también deberían hacerlo.
  3. No.
  4. El personaje ancho es un nombre confuso. En los primeros días de Unicode, existía la creencia de que el carácter se puede codificar en dos bytes, de ahí el nombre. Hoy, representa "cualquier parte del personaje que tenga dos bytes de longitud". UTF-16 se ve como una secuencia de dichos pares de bytes (también conocidos como caracteres anchos). Un personaje en UTF-16 toma uno o dos pares.

47
2017-12-29 16:14



Entonces, cada lector aquí debería tener una comprensión clara de los hechos, la situación. Si no entonces debes leer la respuesta extraordinariamente completa de paercebal [por cierto: ¡gracias!].

Mi conclusión pragmática es sorprendentemente simple: todo lo que C ++ (y STL) "codificación de caracteres" está sustancialmente roto e inútil. La culpa es de Microsoft o no, eso no ayudará de todos modos.

Mi solución, después de una investigación profunda, mucha frustración y las experiencias consiguientes, es la siguiente:

  1. Acepte, que usted debe ser responsable por su propia codificación y conversión (y verá que gran parte es bastante trivial)

  2. use std :: string para cualquier cadena codificada en UTF-8 (solo una typedef std::string UTF8String)

  3. acepta que un objeto UTF8String es simplemente un contenedor tonto, pero barato. Nunca acceda y / o manipule caracteres en él directamente (sin buscar, reemplazar, etc.). Podrías, pero realmente realmente, ¡realmente no quieres perder el tiempo escribiendo algoritmos de manipulación de texto para cadenas de múltiples bytes! Incluso si otras personas ya hicieron cosas tan estúpidas, ¡no hagas eso! ¡Déjalo ser! (Bueno, hay escenarios donde tiene sentido ... solo use la biblioteca de la ICU para eso).

  4. use std :: wstring para cadenas codificadas UCS-2 (typedef std::wstring UCS2String) - esto es un compromiso, y una concesión al lío que introdujo la API de WIN32). UCS-2 es suficiente para la mayoría de nosotros (más sobre eso más adelante ...).

  5. use instancias de UCS2String siempre que se requiera un acceso de carácter por carácter (lectura, manipulación, etc.). Cualquier procesamiento basado en caracteres debe hacerse en una representación no multibyte. Es simple, rápido, fácil.

  6. agregue dos funciones de utilidad para convertir entre UTF-8 y UCS-2:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );
    

Las conversiones son sencillas, Google debería ayudar aquí ...

Eso es. Use UTF8String donde la memoria sea valiosa y para todas las E / S UTF-8. Use UCS2String donde la cadena debe ser analizada y / o manipulada. Puede convertir entre esas dos representaciones en cualquier momento.

Alternativas y mejoras

  • las conversiones de y a codificaciones de caracteres de un solo byte (por ejemplo, ISO-8859-1) se pueden realizar con la ayuda de tablas de traducción simple, p. const wchar_t tt_iso88951[256] = {0,1,2,...}; y el código apropiado para la conversión a y desde UCS2.

  • si UCS-2 no es suficiente, entonces cambie a UCS-4 (typedef std::basic_string<uint32_t> UCS2String)

ICU u otras bibliotecas Unicode?

Para cosas avanzadas.


35
2017-11-07 06:07



  1. Cuando desee tener caracteres anchos almacenados en su cadena. wide depende de la implementación. Visual C ++ tiene un valor predeterminado de 16 bits si recuerdo correctamente, mientras que GCC se establece de forma predeterminada según el objetivo. Tiene 32 bits de largo aquí. Tenga en cuenta que wchar_t (tipo de caracteres anchos) no tiene nada que ver con unicode. Simplemente se garantiza que puede almacenar todos los miembros del conjunto de caracteres más grande que la implementación admite en sus configuraciones regionales, y al menos siempre que sea char. Usted puede almacenar Unicode encaja perfectamente en std::string utilizando el utf-8 codificación también. Pero no comprenderá el significado de los puntos de código Unicode. Asi que str.size() no le dará la cantidad de caracteres lógicos en su cadena, sino simplemente la cantidad de elementos char o wchar_t almacenados en esa cadena / wstring. Por esa razón, la gente del contenedor gtk / glib C ++ ha desarrollado un Glib::ustring clase que puede manejar utf-8.

    Si tu wchar_t tiene 32 bits de largo, entonces puedes usar utf-32 como una codificación Unicode, y puedes almacenar y Manejar cadenas Unicode usando una codificación fija (utf-32 es de longitud fija). Esto significa que tu wstring s.size() la función entonces devuelve la cantidad correcta de elementos wchar_t y personajes lógicos.

  2. Sí, char siempre tiene al menos 8 bits de largo, lo que significa que puede almacenar todos los valores ASCII.
  3. Sí, todos los principales compiladores lo admiten.

23
2017-12-31 11:48



Con frecuencia utilizo std :: string para mantener los caracteres utf-8 sin ningún problema. Recomiendo encarecidamente hacer esto cuando interactúas con API que también usan utf-8 como el tipo de cadena nativa.

Por ejemplo, uso utf-8 al conectar mi código con el intérprete Tcl.

La principal advertencia es la longitud de std :: string, ya no es la cantidad de caracteres en la cadena.


5
2017-12-31 04:33



  1. Cuando desee almacenar caracteres "anchos" (Unicode).
  2. Sí: 255 de ellos (excluyendo 0).
  3. Sí.
  4. Aquí hay un artículo introductorio: http://www.joelonsoftware.com/articles/Unicode.html

3
2017-12-31 04:16



Las aplicaciones que no están satisfechas con solo 256 caracteres diferentes tienen la opción de usar caracteres anchos (más de 8 bits) o una codificación de longitud variable (una codificación multibyte en terminología C ++) como UTF-8. Los caracteres anchos generalmente requieren más espacio que una codificación de longitud variable, pero son más rápidos de procesar. Las aplicaciones de varios idiomas que procesan grandes cantidades de texto generalmente usan caracteres anchos al procesar el texto, pero lo convierten a UTF-8 cuando lo guardan en el disco.

La única diferencia entre un string y un wstring es el tipo de datos de los personajes que almacenan. Una cadena de tiendas chars cuyo tamaño garantizado sea de al menos 8 bits, por lo que puede usar cadenas para procesar, p. Texto ASCII, ISO-8859-15 o UTF-8. El estándar no dice nada sobre el conjunto de caracteres o la codificación.

Prácticamente todos los compiladores usan un conjunto de caracteres cuyos primeros 128 caracteres corresponden con ASCII. Este es también el caso de los compiladores que usan codificación UTF-8. Lo importante a tener en cuenta cuando se usan cadenas en UTF-8 u otra codificación de longitud variable es que los índices y longitudes se miden en bytes, no en caracteres.

El tipo de datos de un wstring es wchar_t, cuyo tamaño no está definido en el estándar, excepto que tiene que ser al menos tan grande como un char, generalmente 16 bits o 32 bits. wstring se puede utilizar para procesar texto en la implementación de codificación de caracteres anchos definida. Debido a que la codificación no está definida en el estándar, no es fácil convertir cadenas y wstrings. Tampoco se puede suponer que wstrings tenga una codificación de longitud fija.

Si no necesita soporte multilingüe, puede usar cadenas normales. Por otro lado, si está escribiendo una aplicación gráfica, a menudo sucede que la API solo admite caracteres anchos. Entonces es probable que desee utilizar los mismos caracteres anchos al procesar el texto. Tenga en cuenta que UTF-16 es una codificación de longitud variable, lo que significa que no puede asumir length() para devolver el número de caracteres. Si la API utiliza una codificación de longitud fija, como UCS-2, el procesamiento se vuelve fácil. La conversión entre caracteres anchos y UTF-8 es difícil de hacer de forma portátil, pero, una vez más, su API de interfaz de usuario probablemente sea compatible con la conversión.


2
2017-09-11 09:28



  1. cuando quieres usar cadenas Unicode y no solo ascii, útiles para la internacionalización
  2. sí, pero no funciona bien con 0
  3. no consciente de ninguno que no lo haga
  4. el carácter ancho es la forma específica del compilador de manejar la representación de longitud fija de un carácter Unicode, para MSVC es un carácter de 2 bytes, para gcc entiendo que es de 4 bytes. y un +1 para http://www.joelonsoftware.com/articles/Unicode.html

1
2017-12-31 04:16



1) Como menciona Greg, wstring es útil para la internacionalización, es cuando lanzarás tu producto en otros idiomas que no sean el inglés

4) Mira esto para ver el personaje ancho http://en.wikipedia.org/wiki/Wide_character


1
2017-12-31 04:24