Pregunta ¿Qué está "mal" con C ++ wchar_t y wstrings? ¿Cuáles son algunas alternativas a los personajes anchos?


He visto a mucha gente en la comunidad de C ++ (particularmente ## c ++ en freenode) resentir el uso de wstrings y wchar_ty su uso en la API de Windows. ¿Qué es exactamente "incorrecto" con wchar_t y wstring, y si quiero apoyar la internacionalización, ¿cuáles son algunas alternativas a los personajes anchos?


76
2018-06-19 19:00


origen


Respuestas:


¿Qué es wchar_t?

wchar_t se define de tal manera que la codificación de caracteres de cualquier localidad se puede convertir a una representación wchar_t donde cada wchar_t representa exactamente un punto de código:

Escriba wchar_t es un tipo distinto cuyos valores pueden representar códigos distintos para todos los miembros del conjunto de caracteres ampliado más grande especificado entre las configuraciones regionales admitidas (22.3.1).

- C ++ [basic.fundamental] 3.9.1 / 5

Esta no requiere que wchar_t sea lo suficientemente grande como para representar cualquier carácter de todas las configuraciones regionales simultáneamente. Es decir, la codificación utilizada para wchar_t puede diferir entre las configuraciones regionales. Lo que significa que no necesariamente puede convertir una cadena a wchar_t usando una configuración regional y luego convertir de nuevo a char usando otra configuración regional.1

Dado que el uso de wchar_t como una representación común entre todos los locales parece ser el uso principal de wchar_t en la práctica, es posible que te preguntes para qué sirve si no es así.

La intención y el propósito original de wchar_t era simplificar el procesamiento del texto al definirlo de manera tal que requiera un mapeo uno a uno de las unidades de código de una cadena a los caracteres del texto, permitiendo así el uso de los mismos algoritmos simples que se usan con cadenas ASCII para trabajar con otros idiomas.

Desafortunadamente, la redacción de la especificación de wchar_t presupone un mapeo uno a uno entre caracteres y puntos de código para lograr esto. Unicode rompe esa suposición2, por lo que tampoco puede usar wchar_t de forma segura para algoritmos de texto simples.

Esto significa que el software portátil no puede usar wchar_t como una representación común de texto entre configuraciones regionales, o para permitir el uso de algoritmos de texto simples.

¿De qué sirve wchar_t hoy?

No mucho, para el código portátil de todos modos. Si __STDC_ISO_10646__ se define entonces los valores de wchar_t representan directamente los puntos de código Unicode con los mismos valores en todas las configuraciones regionales. Eso hace que sea seguro hacer las conversiones entre locaciones mencionadas anteriormente. Sin embargo, no puede confiar únicamente en que decida que puede usar wchar_t de esta manera porque, aunque la mayoría de las plataformas de UNIX lo definen, Windows no lo hace aunque Windows usa la misma configuración regional wchar_t en todas las configuraciones regionales.

La razón por la que Windows no define __STDC_ISO_10646__ es porque Windows usa UTF-16 como su codificación wchar_t, y porque UTF-16 usa pares de sustitución para representar puntos de código mayores que U + FFFF, lo que significa que UTF-16 no cumple los requisitos para __STDC_ISO_10646__.

Para el código específico de la plataforma, wchar_t puede ser más útil. Esencialmente se requiere en Windows (por ejemplo, algunos archivos simplemente no se pueden abrir sin usar nombres de archivo wchar_t), aunque Windows es la única plataforma donde esto es cierto hasta donde yo sé (así que tal vez podamos pensar en wchar_t como 'Windows_char_t').

En retrospectiva, wchar_t claramente no es útil para simplificar el manejo de texto, o como almacenamiento para texto independiente de la configuración regional. El código portátil no debe intentar usarlo para estos fines. El código no portátil puede resultar útil simplemente porque alguna API lo requiere.

Alternativas

La alternativa que me gusta es usar cadenas C codificadas en UTF-8, incluso en plataformas que no sean especialmente amigables con UTF-8.

De esta manera, uno puede escribir código portátil usando una representación de texto común en todas las plataformas, usar tipos de datos estándar para su propósito previsto, obtener el soporte del lenguaje para esos tipos (por ejemplo, literales de cadena, aunque algunos trucos son necesarios para que funcione para algunos compiladores), algunos soporte de biblioteca estándar, soporte de depurador (pueden ser necesarios más trucos), etc. Con caracteres anchos, generalmente es más difícil o imposible obtener todo esto, y puede obtener diferentes piezas en diferentes plataformas.

Una cosa que UTF-8 no proporciona es la capacidad de usar algoritmos de texto simples, como los que son posibles con ASCII. En este UTF-8 no es peor que cualquier otra codificación Unicode. De hecho, se puede considerar que es mejor porque las representaciones de unidades de código múltiple en UTF-8 son más comunes y por lo tanto los errores en el manejo del código tales representaciones de ancho variable de los caracteres son más notorias y fijas que si intentas seguir con UTF -32 con NFC o NFKC.

Muchas plataformas usan UTF-8 como su codificación char nativa y muchos programas no requieren ningún procesamiento de texto significativo, por lo que escribir un programa internacionalizado en esas plataformas es muy diferente de escribir código sin considerar la internacionalización. Escribir códigos más ampliamente portátiles o escribir en otras plataformas requiere insertar conversiones en los límites de las API que usan otras codificaciones.

Otra alternativa utilizada por algunos software es elegir una representación multiplataforma, como arreglos cortos sin firmar que contengan datos UTF-16, y luego suministrar todo el soporte de la biblioteca y simplemente vivir con los costos de soporte de idiomas, etc.

C ++ 11 agrega nuevos tipos de caracteres anchos como alternativas a wchar_t, char16_t y char32_t con características de lenguaje / biblioteca concomitantes. En realidad, no se garantiza que sean UTF-16 y UTF-32, pero no creo que ninguna implementación importante use otra cosa. C ++ 11 también mejora el soporte UTF-8, por ejemplo con literales de cadena UTF-8, por lo que no será necesario engañar a VC ++ para que produzca cadenas codificadas en UTF-8 (aunque puedo continuar haciéndolo en lugar de usar el u8 prefijo).

Alternativas para evitar

TCHAR: TCHAR es para migrar programas antiguos de Windows que asumen codificaciones heredadas de char a wchar_t, y se lo olvidará a menos que su programa haya sido escrito en algún milenio anterior. No es portátil y es inherentemente inespecífico sobre su codificación e incluso su tipo de datos, por lo que no se puede usar con ninguna API que no esté basada en TCHAR. Dado que su propósito es la migración a wchar_t, que hemos visto anteriormente no es una buena idea, no hay ningún valor en el uso de TCHAR.


1. Los caracteres que son representables en cadenas wchar_t pero que no son compatibles con ninguna configuración regional no se requieren para ser representados con un solo valor wchar_t. Esto significa que wchar_t podría usar una codificación de ancho variable para ciertos caracteres, otra clara violación de la intención de wchar_t. Aunque es discutible que un personaje sea representable por wchar_t es suficiente para decir que la configuración regional "admite" ese carácter, en cuyo caso las codificaciones de ancho variable no son legales y el uso de UTF-16 por parte de Windows no es conforme.

2. Unicode permite que muchos caracteres se representen con múltiples puntos de código, lo que crea los mismos problemas para los algoritmos de texto simples que las codificaciones de ancho variable. Incluso si uno mantiene estrictamente una normalización compuesta, algunos caracteres aún requieren múltiples puntos de código. Ver: http://www.unicode.org/standard/where/


106
2018-06-25 21:52



No hay nada "incorrecto" con wchar_t. El problema es que, en NT 3.x días, Microsoft decidió que Unicode era Bueno (lo es) y que implementó Unicode como caracteres wchar_t de 16 bits. Así que la mayoría de la literatura de Microsoft de mediados de los 90 casi equiparó a Unicode == utf16 == wchar_t.

Lo cual, por desgracia, no es en absoluto el caso. "Caracteres anchos" son no necesariamente 2 bytes, en todas las plataformas, bajo cualquier circunstancia.

Este es uno de los mejores iniciadores en "Unicode" (independientemente de esta pregunta, independiente de C ++) que he visto alguna vez: altamente lo recomiendo:

Y, sinceramente, creo que la mejor manera de lidiar con "ASCII de 8 bits" frente a "caracteres anchos de Win32" frente a "wchar_t-en-general" es simplemente aceptar que "Windows es diferente" ... y codificar en consecuencia.

EN MI HUMILDE OPINIÓN...

PD:

Estoy totalmente de acuerdo con jamesdlin arriba:

En Windows, realmente no tienes opción. Sus API internas eran   diseñado para UCS-2, que era razonable en ese momento, ya que era   antes de las codificaciones UTF-8 y UTF-16 de longitud variable   estandarizado. Pero ahora que admiten UTF-16, terminaron con   lo peor de ambos mundos.


16
2018-06-19 23:39



Lectura obligatoria:

El mínimo absoluto de cada desarrollador de software Absolutamente, definitivamente debe saber sobre Unicode y juegos de caracteres (¡Sin excusas!)

Si programa en Java o .Net (VB.Net o C #), en gran medida no es un problema: ambos son Unicode por defecto. Si programa en la API "clásica" de Win32), su mejor opción es usar las macros de TCHAR y _T () (en lugar de usar explícitamente wchar).

Todos los compiladores de Microsoft VS2005 y posteriores, creo, de forma predeterminada a 16 bits para C / C ++ de todos modos (parte de la razón por la que todavía uso MSVS 6.0 siempre que puedo;)).

Otro enlace bueno (aunque algo anticuado):


-4