std :: wstring VS std :: cadena


742

No puedo entender las diferencias entre std::stringy std::wstring. Sé que wstringadmite caracteres anchos, como los caracteres Unicode. Tengo las siguientes preguntas:

  1. ¿Cuándo debo usar std::wstringmás std::string?
  2. ¿Puede std::stringcontener todo el conjunto de caracteres ASCII, incluidos los caracteres especiales?
  3. ¿Es std::wstringcompatible con todos los compiladores de C ++ populares?
  4. ¿Qué es exactamente un " personaje ancho "?

10
El conjunto de caracteres ASCII no tiene muchos caracteres "especiales", el más exótico es probablemente `(comilla). std :: string puede contener alrededor del 0.025% de todos los caracteres Unicode (generalmente, caracteres de 8 bits)
MSalters

3
Puede encontrar buena información sobre los caracteres anchos y qué tipo de uso usar aquí: programmers.stackexchange.com/questions/102205/…
Yariv

14
Bueno, y como estamos en 2012, se escribió utf8everywhere.org . Responde prácticamente todas las preguntas sobre derechos y errores con C ++ / Windows.
Pavel Radzivilovsky

42
@MSalters: std :: string puede contener el 100% de todos los caracteres Unicode, incluso si CHAR_BIT es 8. Depende de la codificación de std :: string, que puede ser UTF-8 en el nivel del sistema (como en casi todas partes excepto Windows) ) o en el nivel de su aplicación. ¿La codificación estrecha nativa no es compatible con Unicode? No hay problema, simplemente no lo use, use UTF-8 en su lugar.
Yakov Galka

8
Gran lectura sobre este tema: utf8everywhere.org
Timothy Shields

Respuestas:


993

string? wstring?

std::stringes una basic_stringplantilla en un char, y std::wstringen un wchar_t.

char vs. wchar_t

charse supone que tiene un carácter, generalmente un carácter de 8 bits.
wchar_tse supone que tiene un carácter ancho, y luego, las cosas se ponen difíciles:
en Linux, a wchar_tes de 4 bytes, mientras que en Windows es de 2 bytes.

¿Qué pasa con Unicode , entonces?

El problema es que ni chartampoco wchar_testá directamente vinculado a Unicode.

En Linux?

Tomemos un sistema operativo Linux: mi sistema Ubuntu ya es compatible con 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(ordinals)  :" ;

   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(ordinals) :" ;

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

genera el siguiente texto:

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

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

Verá que el texto "olé" charestá realmente construido por cuatro caracteres: 110, 108, 195 y 169 (sin contar el cero final). (Te dejaré estudiar el wchar_tcódigo como ejercicio)

Entonces, cuando trabajas con un charsistema operativo en Linux, generalmente debes terminar usando Unicode sin siquiera saberlo. Y como std::stringfunciona char, std::stringya está listo para Unicode.

Tenga en cuenta que std::string, al igual que la API de cadena C, 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 alguna 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 trabajando con chary en diferentes charsets / páginas de códigos producidos en todo el mundo, antes del advenimiento de Unicode.

Por lo tanto, su solución fue interesante: si una aplicación funciona char, las cadenas de caracteres se codifican / imprimen / muestran en las etiquetas de la GUI utilizando el juego de caracteres / página de códigos local 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 cirílico ("olé" si usa Windows-1251 ). Por lo tanto, las "aplicaciones históricas" generalmente seguirán funcionando de la misma manera.

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

Las aplicaciones que usan charse dicen "multibyte" (porque cada glifo está compuesto por uno o más chars), mientras que las aplicaciones que usan wchar_tse dicen "widechar" (porque cada glifo está compuesto por uno o dos wchar_t. Consulte MultiByteToWideChar y WideCharToMultiByte Win32 API de conversión para obtener más información.

Por lo tanto, si trabaja en Windows, desea usarlo wchar_t(a menos que use un marco que lo oculte, como GTK + o QT ...). El hecho es que detrás de escena, Windows trabaja con wchar_tcadenas, por lo que incluso las aplicaciones históricas tendrán sus charcadenas convertidas wchar_tcuando usen API como SetWindowText()(función API de bajo nivel para establecer la etiqueta en una GUI Win32).

Problemas de memoria?

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

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

Aún así, para otros idiomas (chino, japonés, etc.), la memoria utilizada será la misma o ligeramente mayor para UTF-8 que para UTF-16.

Con todo, UTF-16 utilizará principalmente 2 y ocasionalmente 4 bytes por carácter (a menos que se trate de algún tipo de glifos de lenguaje esotérico (Klingon? Élfico?), 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. ¿Cuándo 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 utilice un kit de herramientas / marco que indique lo contrario

  2. ¿Puede std::stringcontener todo el conjunto de caracteres ASCII, incluidos los caracteres especiales?

    Aviso: A std::stringes adecuado para mantener un búfer 'binario', donde unstd::wstring no lo es!

    En Linux? Si.
    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 ):
    a std::stringserá suficiente para manejar todas las charcadenas de base (cada charuna con un número del 0 al 255). Pero:

    1. Se supone que ASCII va de 0 a 127. Los valores superiores charNO son ASCII.
    2. A charde 0 a 127 se mantendrá correctamente
    3. un valor charde 128 a 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::wstringcompatible con casi todos los compiladores de C ++ populares?

    Principalmente, con la excepción de los compiladores basados ​​en GCC que se portan 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 carácter escrito wchar_tque es más grande que el chartipo de carácter simple . Se supone que se usa para poner dentro de caracteres cuyos índices (como los glifos Unicode) son mayores que 255 (o 127, dependiendo ...).


44
@gnud: Quizás se suponía que wchar_t era suficiente para manejar todos los caracteres UCS-2 (la mayoría de los caracteres UTF-16) antes del advenimiento de UTF-16 ... O tal vez Microsoft tenía otras prioridades además de POSIX, como dar fácil acceso a Unicode sin modificar el uso de codepaged char en Win32.
paercebal

44
@Sorin Sbarnea: UTF-8 podría tomar 1-6 bytes, pero aparentemente el estándar lo limita a 1-4. Consulte en.wikipedia.org/wiki/UTF8#Description para obtener más información.
paercebal

8
Si bien este ejemplo produce resultados diferentes en Linux y Windows, el programa C ++ contiene un comportamiento definido por la implementación en cuanto a si olèestá codificado como UTF-8 o no. Más aún, la razón por la que no se puede de forma nativa transmitir wchar_t *a std::coutse debe a que los tipos son incompatibles resultando en un programa mal formada y no tiene nada que ver con el uso de codificaciones. Vale la pena señalar que si usa std::stringo std::wstringdepende de su propia preferencia de codificación en lugar de la plataforma, especialmente si desea que su código sea portátil.
John Leidegren

14
Windows realmente usa UTF-16 y ha sido durante bastante tiempo, las versiones anteriores de Windows sí usaban UCS-2, pero este ya no es el caso. Mi único problema aquí es la conclusión de que std::wstringdebería usarse en Windows porque es una mejor opción para la API de Windows Unicode, que creo que es falaz. Si su única preocupación era llamar a la API de Windows Unicode y no ordenar cadenas, entonces seguro, pero no compro esto como el caso general.
John Leidegren

15
@ John Leidegren:: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureEntonces, estamos de acuerdo. Estoy codificando en C ++, no en JavaScript. Evitar la clasificación inútil o cualquier otro procesamiento potencialmente costoso en tiempo de ejecución cuando se puede hacer en tiempo de compilación es la esencia de ese lenguaje. La codificación contra WinAPI y el uso std::stringes solo una pérdida injustificada de recursos en tiempo de ejecución. Lo encuentras falaz, y está bien, ya que es tu punto de vista. La mía es que no escribiré código con pesimismo en Windows solo porque se ve mejor desde el lado de Linux.
paercebal

71

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

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

A menos que su aplicación esté centrada en llamadas de API, por ejemplo, principalmente en aplicaciones de IU, 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 doble para el desarrollo de múltiples plataformas y bibliotecas.

Y ahora, respondiendo tus preguntas:

  1. Algunas razones débiles. Existe por razones históricas, donde se creía que los widechars eran la forma adecuada de apoyar a Unicode. Ahora se usa para interconectar API que prefieren cadenas UTF-16. Los uso solo en la vecindad directa de tales llamadas API.
  2. Esto no tiene nada que ver con std :: string. Puede contener cualquier codificación que le pongas. La única pregunta es cómo usted trata a su contenido. Mi recomendación es UTF-8, por lo que podrá 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 carácter ancho es un nombre confuso. En los primeros días de Unicode, existía la creencia de que un personaje puede codificarse en dos bytes, de ahí el nombre. Hoy significa "cualquier parte del carácter que tenga dos bytes de longitud". UTF-16 se ve como una secuencia de tales pares de bytes (también conocidos como caracteres anchos). Un personaje en UTF-16 toma uno o dos pares.

37

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

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

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

  1. acepta que tienes que ser responsable por ti mismo de las cosas de codificación y conversión (y verás que gran parte es bastante trivial)

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

  3. acepte que dicho objeto UTF8String es solo un contenedor tonto, pero barato. Nunca acceda y / o manipule caracteres directamente en él (sin búsqueda, reemplazo, etc.). ¡Podría, pero realmente no quiere perder el tiempo escribiendo algoritmos de manipulación de texto para cadenas de varios bytes! Incluso si otras personas ya hicieron cosas tan estúpidas, ¡no hagas eso! ¡Déjalo ser! (Bueno, hay escenarios en los que tiene sentido ... solo use la biblioteca de la UCI para esos).

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

  5. use instancias UCS2String siempre que se requiera un acceso 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 de ida y vuelta 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. Utilice UTF8String donde la memoria sea valiosa y para todas las E / S UTF-8. Utilice UCS2String donde la cadena debe analizarse y / o manipularse. Puede convertir entre esas dos representaciones en cualquier momento.

Alternativas y mejoras

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

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

UCI u otras bibliotecas Unicode?

Para cosas avanzadas.


Dang, no es bueno saber que el soporte nativo de Unicode no está allí.
Mihai Danila

@ Frunsi, tengo curiosidad por saber si has probado Glib :: ustring y, si es así, ¿qué piensas?
Caroline Beltran

@CarolineBeltran: Conozco Glib, pero nunca lo usé, y probablemente nunca lo usaré, porque está bastante limitado a una plataforma objetivo bastante inespecífica (sistemas unixoides ...). Su puerto de Windows se basa en una capa externa win2unix, y en mi humilde opinión, no hay ninguna capa de compatibilidad con OSX. Todo esto se dirige claramente en una dirección incorrecta, al menos para mi código (en este nivel de arco ...) ;-) Entonces, Glib no es una opción
Frunsi

99
Buscar, reemplazar, etc. funciona bien en cadenas UTF-8 (una parte de la secuencia de bytes que representa un carácter nunca puede malinterpretarse como otro carácter). De hecho, UTF-16 y UTF-32 no facilitan esto en absoluto: las tres codificaciones son codificaciones multibyte en la práctica, porque un carácter percibido por el usuario (grupo de grafemas) puede tener cualquier número de puntos de código unicode. La solución pragmática es usar UTF-8 para todo y convertir a UTF-16 solo cuando se trata con la API de Windows.
Daniel

55
@Frunsi: Buscar y reemplazar funciona tan bien con UTF-8 como con UTF-32. Precisamente porque el procesamiento de texto adecuado para Unicode necesita tratar con 'caracteres' de múltiples puntos de código de todos modos, el uso de una codificación de longitud variable como UTF-8 no complica el procesamiento de cadenas. Así que solo usa UTF-8 en todas partes. Las funciones de cadena C normales funcionarán bien en UTF-8 (y se corresponden con las comparaciones ordinales en la cadena Unicode), y si necesita algo más que tenga en cuenta el lenguaje, tendrá que llamar a una biblioteca Unicode de todos modos, UTF-16/32 No puedo salvarte de eso.
Daniel

25
  1. Cuando desee tener caracteres anchos almacenados en su cadena. wideDepende de la implementación. Visual C ++ por defecto es de 16 bits si no recuerdo mal, mientras que GCC por defecto depende del objetivo. Tiene 32 bits de largo aquí. Tenga en cuenta que wchar_t (tipo de carácter ancho) no tiene nada que ver con unicode. Simplemente se garantiza que puede almacenar todos los miembros del juego de caracteres más grande que la implementación admite en sus configuraciones regionales, y al menos mientras char. También puede almacenar cadenas Unicode para std::stringusar la utf-8codificación. Pero no entenderá el significado de los puntos de código Unicode. Entoncesstr.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 envoltorio gtk / glib C ++ ha desarrollado una Glib::ustringclase que puede manejar utf-8.

    Si su wchar_t tiene 32 bits de largo, puede usarlo utf-32como codificación unicode, y puede almacenar y manejar cadenas unicode usando una codificación fija (utf-32 es de longitud fija). Esto significa de su wstring s.size()función será entonces devolver la cantidad correcta de elementos wchar_t y caracteres lógicos.

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

Tengo curiosidad sobre el # 2. ¿Pensé que 7 bits también serían técnicamente válidos? ¿O es necesario poder almacenar algo más allá de los caracteres ASCII de 7 bits?
jalf

1
si, jalf. c89 especifica rangos mínimos para tipos básicos en su documentación de los límites. h (para caracteres sin signo, eso es 0..255 min), y un sistema binario puro para tipos enteros. sigue a char, unsigned char yigned char tienen longitudes de bit mínimas de 8. c ++ hereda esas reglas.
Johannes Schaub - litb

15
"Esto significa que la función s.size () de su wstring devolverá la cantidad correcta de elementos wchar_t y caracteres lógicos". Esto no es del todo exacto, incluso para Unicode. Sería más preciso decir punto de código que "carácter lógico", incluso en UTF-32 un carácter dado puede estar compuesto de múltiples puntos de código.
Logan Capaldo

¿Están diciendo en esencia que C ++ no tiene soporte nativo para el juego de caracteres Unicode?
Mihai Danila

1
"Pero no entenderá el significado de los puntos de código Unicode". En windows, tampoco std::wstring.
Deduplicador

5

Con frecuencia uso std :: string para contener caracteres utf-8 sin ningún problema. Recomiendo encarecidamente hacer esto al interactuar con API que usan utf-8 como el tipo de cadena nativo también.

Por ejemplo, uso utf-8 cuando interactúo mi código con el intérprete Tcl.

La advertencia principal es la longitud de la cadena std ::, ya no es el número de caracteres en la cadena.


1
Juan: ¿Quieres decir que std :: string puede contener todos los caracteres unicode pero la longitud se informará incorrectamente? ¿Hay alguna razón por la que informa una longitud incorrecta?

3
Cuando se utiliza la codificación utf-8, un único carácter unicode puede estar compuesto por múltiples bytes. Esta es la razón por la cual la codificación utf-8 es más pequeña cuando se usan principalmente caracteres del conjunto ascii estándar. Debe usar funciones especiales (o rodar las suyas) para medir la cantidad de caracteres Unicode.

2
(Específico de Windows) La mayoría de las funciones esperarán que una cadena que usa bytes sea ASCII y 2 bytes sea Unicode, versiones anteriores MBCS. Lo que significa que si está almacenando unicode de 8 bits, tendrá que convertir a unicode de 16 bits para llamar a una función estándar de Windows (a menos que solo esté usando la parte ASCII).
Greg Domjan

2
Un std :: string no solo informará la longitud de forma incorrecta, sino que también generará la cadena incorrecta. Si algún carácter Unicode se representa en UTF-8 como múltiples bytes, que std :: string considera sus propios caracteres, entonces sus rutinas de manipulación típicamente std :: string probablemente generarán varios caracteres extraños que resultan de la mala interpretación de uno Carácter correcto.
Mihai Danila

2
Sugiero cambiar la respuesta para indicar que las cadenas deben considerarse como solo contenedores de bytes y, si los bytes son una codificación Unicode (UTF-8, UTF-16, ...), entonces debe usar bibliotecas específicas que entiendan ese. Las API estándar basadas en cadenas (longitud, substr, etc.) fallarán miserablemente con caracteres multibyte. Si se realiza esta actualización, eliminaré mi voto negativo.
Mihai Danila

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

11
std :: string puede contener 0 bien (solo tenga cuidado si llama al método c_str ())
Mr Fooz

3
Y estrictamente hablando, no se garantiza que un carácter sea de 8 bits. :) Su enlace en el n. ° 4 es de lectura obligatoria, pero no creo que responda la pregunta. Un carácter ancho no tiene nada que ver con unicode. Es simplemente un personaje más amplio. (Cuánto más ancho depende del sistema operativo, pero generalmente 16 o 32 bits)
jalf

2
  1. cuando desee utilizar cadenas Unicode y no solo ascii, útil para la internacionalización
  2. sí, pero no juega bien con 0
  3. no conozco ninguno que no
  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
2. Un std :: string puede contener un carácter NULL muy bien. También puede contener utf-8 y caracteres anchos.

@Juan: Eso me confundió nuevamente. Si std :: string puede mantener caracteres unicode, ¿qué tiene de especial std :: wstring?

1
@Appu: std :: string puede contener caracteres UTF-8 unicode. Hay una serie de estándares Unicode dirigidos a diferentes anchos de caracteres. UTf8 tiene 8 bits de ancho. También hay UTF-16 y UTF-32 en 16 y 32 bits de ancho respectivamente
Greg D

Con un std :: wstring. Cada carácter unicode puede ser un wchar_t cuando se usan las codificaciones de longitud fija. Por ejemplo, si elige usar el enfoque joel on software como Greg lo vincula. Entonces, la longitud de wstring es exactamente el número de caracteres unicode en la cadena. Pero ocupa más espacio

No dije que no podía contener un 0 '\ 0', y lo que quise decir con que no funciona bien es que algunos métodos pueden no darle un resultado esperado que contenga todos los datos de la cadena de caracteres. Tan duro con los votos negativos.
Greg Domjan

2

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 en varios idiomas que procesan grandes cantidades de texto generalmente usan caracteres anchos cuando procesan el texto, pero lo convierten a UTF-8 cuando lo almacenan en el disco.

La única diferencia entre ay stringa wstringes el tipo de datos de los caracteres que almacenan. Una cadena almacena chars cuyo tamaño está garantizado en al menos 8 bits, por lo que puede usar cadenas para procesar, por ejemplo, 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 juego 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 al usar cadenas en UTF-8 o alguna otra codificación de longitud variable, es que los índices y las longitudes se miden en bytes, no en caracteres.

El tipo de datos de una 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 carácter, generalmente 16 bits o 32 bits. wstring se puede utilizar para procesar texto en la codificación de caracteres anchos definida por la implementación. Debido a que la codificación no está definida en el estándar, no es sencillo convertir entre cadenas y wstrings. Tampoco se puede suponer que wstrings tenga una codificación de longitud fija.

Si no necesita soporte en varios idiomas, puede usar solo cadenas regulares. Por otro lado, si está escribiendo una aplicación gráfica, a menudo es el caso de que la API solo admite caracteres anchos. Entonces, probablemente 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()que devuelve 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.


Entonces, parafraseando el primer párrafo: la aplicación que necesita más de 256 caracteres debe usar una codificación multibyte o una codificación maybe_multibyte.
Deduplicador

Sin embargo, generalmente las codificaciones de 16 y 32 bits, como UCS-2 y UCS-4, no se denominan codificaciones multibyte. El estándar C ++ distingue entre codificaciones multibyte y caracteres anchos. Una representación de caracteres anchos utiliza un número fijo (generalmente más de 8) bits por carácter. Las codificaciones que usan un solo byte para codificar los caracteres más comunes, y múltiples bytes para codificar el resto del conjunto de caracteres, se denominan codificaciones multibyte.
Seppo Enarvi

Lo siento, comentario descuidado. Debería haber dicho codificación de longitud variable. UTF-16 es una codificación de longitud variable, al igual que UTF-8. Pretender que no lo es es una mala idea.
Deduplicador

Ese es un buen punto. No hay ninguna razón por la que wstrings no se pueda usar para almacenar UTF-16 (en lugar de UCS-2), pero luego se pierde la conveniencia de una codificación de longitud fija.
Seppo Enarvi

2

Una buena pregunta! Creo que la CODIFICACIÓN DE DATOS (a veces un CHARSET también involucrado) es un MECANISMO DE EXPRESIÓN DE MEMORIA para guardar datos en un archivo o transferir datos a través de una red, por lo que respondo esta pregunta como:

1. ¿Cuándo debo usar std :: wstring sobre std :: string?

Si la plataforma de programación o la función API es de un solo byte, y deseamos procesar o analizar algunos datos Unicode, por ejemplo, leer desde el archivo Windows'.REG o la transmisión de red de 2 bytes, deberíamos declarar la variable std :: wstring fácilmente procesarlos por ejemplo: wstring ws = L "中国 a" (memoria de 6 octetos: 0x4E2D 0x56FD 0x0061), podemos usar ws [0] para obtener el carácter '中' y ws [1] para obtener el carácter '国' y ws [2] a obtener el carácter 'a', etc.

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

Si. Pero tenga en cuenta: ASCII estadounidense, significa que cada octeto 0x00 ~ 0xFF representa un carácter, incluido el texto imprimible como "123abc & * _ &" y usted dijo que uno especial, en su mayoría lo imprime como '.' evite confundir editores o terminales. Y algunos otros países extienden su propio juego de caracteres "ASCII", por ejemplo, chino, usan 2 octetos para representar un carácter.

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

Quizás, o mayormente. He usado: VC ++ 6 y GCC 3.3, SÍ

4. ¿Qué es exactamente un "personaje ancho"?

un carácter ancho indica principalmente el uso de 2 octetos o 4 octetos para contener los caracteres de todos los países. 2 octetos UCS2 es una muestra representativa y, por ejemplo, en inglés 'a', su memoria es de 2 octetos de 0x0061 (en comparación con ASCII 'la memoria de a es de 1 octeto 0x61)


0

Aquí hay algunas respuestas muy buenas, pero creo que hay un par de cosas que puedo agregar con respecto a Windows / Visual Studio. Esto se basa en mi experiencia con VS2015. En Linux, básicamente la respuesta es usar UTF-8 codificado en std::stringtodas partes. En Windows / VS se vuelve más complejo. Aquí es por qué. Windows espera que las cadenas almacenadas con chars se codifiquen con la página de códigos de configuración regional. Este es casi siempre el conjunto de caracteres ASCII seguido de otros 128 caracteres especiales dependiendo de su ubicación. Permítanme decir que esto no solo cuando se usa la API de Windows, hay otros tres lugares importantes donde estas cadenas interactúan con C ++ estándar. Estos son literales de cadena, enviados a std::coutusar <<y pasando un nombre de archivo a std::fstream.

Aquí voy a decir que soy programador, no especialista en idiomas. Aprecio que USC2 y UTF-16 no sean lo mismo, pero para mis propósitos están lo suficientemente cerca como para ser intercambiables y los uso como tales aquí. En realidad, no estoy seguro de qué Windows usa, pero generalmente tampoco necesito saberlo. He dicho UCS2 en esta respuesta, lo siento de antemano si molesto a alguien con mi ignorancia de este asunto y estoy feliz de cambiarlo si tengo algo mal.

Literales de cadena

Si ingresa literales de cadena que contienen solo caracteres que pueden ser representados por su página de códigos, entonces VS los almacena en su archivo con 1 byte por codificación de caracteres basado en su página de códigos. Tenga en cuenta que si cambia su página de códigos o le da su fuente a otro desarrollador usando una página de códigos diferente, entonces creo (pero no lo he probado) que el personaje terminará siendo diferente. Si ejecuta su código en una computadora usando una página de códigos diferente, entonces no estoy seguro de si el carácter también cambiará.

Si ingresa algún literal de cadena que no pueda ser representado por su página de códigos, VS le pedirá que guarde el archivo como Unicode. El archivo se codificará como UTF-8. Esto significa que todos los caracteres no ASCII (incluidos los que están en su página de códigos) estarán representados por 2 o más bytes. Esto significa que si le das tu fuente a otra persona, la fuente se verá igual. Sin embargo, antes de pasar la fuente al compilador, VS convierte el texto codificado UTF-8 en texto codificado en la página de códigos y los caracteres que faltan en la página de códigos se reemplazan con? .

La única forma de garantizar la representación correcta de un literal de cadena Unicode en VS es preceder al literal de cadena con un Lliteral de cadena ancha. En este caso, VS convertirá el texto codificado UTF-8 del archivo a UCS2. Luego debe pasar este literal de cadena a un std::wstringconstructor o debe convertirlo a utf-8 y ponerlo en a std::string. O si lo desea, puede usar las funciones de la API de Windows para codificarlo usando su página de códigos para ponerlo en un std::string, pero es posible que tampoco haya usado un literal de cadena ancha.

std :: cout

Cuando salga a la consola usando <<solo puede usar std::string, no, std::wstringy el texto debe codificarse usando su página de códigos de configuración regional. Si tiene un std::wstringarchivo, debe convertirlo usando una de las funciones de la API de Windows y los caracteres que no estén en su página de códigos serán reemplazados por ?(tal vez pueda cambiar el carácter, no recuerdo).

std :: nombres de archivos fstream

El sistema operativo Windows usa UCS2 / UTF-16 para sus nombres de archivo, por lo que sea cual sea su página de códigos, puede tener archivos con cualquier carácter Unicode. Pero esto significa que para acceder o crear archivos con caracteres que no están en su página de códigos debe usar std::wstring. No hay otra manera. Esta es una extensión específica de Microsoft para std::fstreamque probablemente no se compile en otros sistemas. Si usa std :: string, solo puede utilizar nombres de archivo que solo incluyan caracteres en su página de códigos.

Sus opciones

Si solo está trabajando en Linux, probablemente no haya llegado tan lejos. Simplemente use UTF-8 en std::stringtodas partes.

Si solo está trabajando en Windows, use UCS2 en std::wstringtodas partes. Algunos puristas pueden decir que use UTF8 y luego convierta cuando sea necesario, pero ¿por qué molestarse con la molestia?

Si eres multiplataforma, es un desastre ser sincero. Si intenta usar UTF-8 en todas partes en Windows, entonces debe tener mucho cuidado con los literales de cadena y la salida a la consola. Puede corromper fácilmente sus cadenas allí. Si usa std::wstringtodas partes en Linux, es posible que no tenga acceso a la versión amplia de std::fstream, por lo que debe hacer la conversión, pero no hay riesgo de corrupción. Así que personalmente creo que esta es una mejor opción. Muchos no estarían de acuerdo, pero no estoy solo: es el camino tomado por wxWidgets, por ejemplo.

Otra opción podría ser typedef unicodestringcomo std::stringen Linux y std::wstringen Windows, y tienen una macro llamada UNI () que prefija L en Windows y en Linux nada, entonces el código

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

estaría bien en cualquier plataforma, creo.

Respuestas

Entonces para responder a sus preguntas

1) Si está programando para Windows, todo el tiempo, si es multiplataforma, tal vez todo el tiempo, a menos que desee lidiar con posibles problemas de corrupción en Windows o escribir algún código con plataforma específica #ifdefspara solucionar las diferencias, si solo usa Linux entonces nunca.

2) sí. Además en Linux, también puede usarlo para todos los Unicode. En Windows, solo puede usarlo para todos los Unicode si elige codificar manualmente con UTF-8. Pero la API de Windows y las clases estándar de C ++ esperarán std::stringque se codifique utilizando la página de códigos de la configuración regional. Esto incluye todos los ASCII más otros 128 caracteres que cambian dependiendo de la página de códigos que su computadora esté configurada para usar.

3) Creo que sí, pero si no, es solo un simple typedef de 'std :: basic_string' usando en wchar_tlugar dechar

4) Un carácter ancho es un tipo de carácter que es más grande que el chartipo estándar de 1 byte . En Windows es de 2 bytes, en Linux es de 4 bytes.


1
Con respecto a "Sin embargo, antes de pasar la fuente al compilador, VS convierte el texto codificado UTF-8 en texto codificado en la página de códigos y los caracteres que faltan en la página de códigos se reemplazan con?". -> No creo que esto sea cierto cuando el compilador usa la codificación UTF-8 (uso /utf-8).
Roi Danton el

No estaba al tanto de esto como una opción. Desde este enlace docs.microsoft.com/en-us/cpp/build/reference/… parece que no hay una casilla para seleccionar en las propiedades del proyecto, debe agregarlo como una opción de línea de comando adicional. ¡Buen lugar!
Phil Rosenberg


-6

¿Cuándo NO deberías usar caracteres anchos?

Cuando escribes código antes del año 1990.

Obviamente, estoy siendo flip, pero realmente, es el siglo XXI ahora. 127 personajes han dejado de ser suficientes desde hace mucho tiempo. Sí, puede usar UTF8, pero ¿por qué molestarse con los dolores de cabeza?


16
@dave: No sé qué dolor de cabeza crea UTF-8, que es mayor que el de Widechars (UTF-16). en UTF-16, también tiene caracteres de varios caracteres.
Pavel Radzivilovsky

El problema es que si estás en otro lugar que no sea el país de habla inglesa, DEBES usar wchar_t. Sin mencionar que algunos alfabetos tienen muchos más caracteres de los que puede caber en un byte. Estuvimos allí, en DOS. Página de códigos esquizofrenia, no, gracias, no más ..
Swift - Friday Pie

1
@Swift El problema wchar_tes que su tamaño y significado son específicos del sistema operativo. Simplemente intercambia los viejos problemas con los nuevos. Mientras que a chares un charsistema operativo independiente (al menos en plataformas similares). Por lo tanto, también podríamos usar UTF-8, empaquetar todo en secuencias de chars y lamentar cómo C ++ nos deja completamente solos sin ningún método estándar para medir, indexar, encontrar, etc., dentro de tales secuencias.
underscore_d

1
@Swift Parece que lo tienes completamente al revés. wchar_tes un tipo de datos de ancho fijo, por lo que una matriz de 10 wchar_tsiempre ocupará sizeof(wchar_t) * 10bytes de la plataforma. Y UTF-16 es una codificación de ancho variable en la que los caracteres pueden estar formados por 1 o 2 puntos de código de 16 bits (y s / 16/8 / g para UTF-8).
underscore_d

1
@SteveHollasch wchar_t La representación de cadena en Windows codificaría caracteres mayores que FFFF como un par sustituto especial, otro tomaría solo un elemento wchar_t. Por lo tanto, esa representación no será compatible con la representación creada por el compilador gnu (donde todos los caracteres inferiores a FFFF tendrán cero palabras delante de ellos). Lo que se almacena en wchar_t está determinado por el programador y el compilador, no por algún acuerdo
Swift - Friday Pie
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.