c++ - print - unicode en c
¿Cómo uso los caracteres Unicode de 3 y 4 bytes con cadenas estándar de C++? (5)
El tamaño y el significado de wchar_t
está definido por la implementación. En Windows es 16 bits como dices, en los sistemas tipo Unix a menudo es de 32 bits, pero no siempre.
Para el caso, un compilador puede hacer lo suyo y elegir un tamaño diferente para wchar_t
que lo que dice el sistema, simplemente no será compatible con ABI con el resto del sistema.
C ++ 11 proporciona std::u32string
, que es para representar cadenas de puntos de código Unicode. Creo que los compiladores de Microsoft lo suficientemente recientes lo incluyen. Tiene un uso algo limitado ya que las funciones del sistema de Microsoft esperan caracteres de 16 bits de ancho (también conocidos como UTF-16le), no puntos de código Unicode de 32 bits (también conocidos como UTF-32, UCS-4).
Sin embargo, mencionas UTF-8: los datos codificados en UTF-8 se pueden almacenar en una std::string
. Por supuesto, dado que es una codificación de longitud variable, no se puede acceder a los puntos de código Unicode por índice, solo se puede acceder a los bytes por índice. Pero normalmente escribirías tu código para no tener que acceder a los puntos de código por índice de todos modos, incluso si usas u32string
. Los puntos de código Unicode no corresponden 1-1 con caracteres imprimibles ("grafemas") debido a la existencia de marcas combinadas en Unicode, por lo que muchos de los pequeños trucos que juegas con cadenas al aprender a programar (invertirlos, buscar subcadenas) no trabaje tan fácilmente con datos Unicode, sin importar en qué lo almacene.
El personaje 𤭢 es, como dices, / u24B62. Está codificado en UTF-8 como una serie de cuatro bytes, no tres: F0 A4 AD A2. La traducción entre los datos codificados UTF-8 y los puntos de código Unicode es esfuerzo (no es una gran cantidad de esfuerzo y las funciones de la biblioteca lo harán por usted). Lo mejor es considerar "datos codificados" y "datos Unicode" como cosas separadas. Puede usar la representación que le parezca más conveniente hasta el punto en que necesite (por ejemplo) representar el texto en la pantalla. En ese punto, debe (re) codificarlo en una codificación que entienda su destino de salida.
En C ++ estándar, tenemos char
y wchar_t
para almacenar caracteres. char
puede almacenar valores entre 0x00 y 0xFF . Y wchar_t
puede almacenar valores entre 0x0000
y 0xFFFF
. std::string
usa char
, por lo que solo puede almacenar caracteres de 1 byte. std::wstring
usa wchar_t
, por lo que puede almacenar caracteres de hasta 2 bytes de ancho. Esto es lo que sé sobre cadenas en C ++. Por favor corrígeme si dije algo mal hasta este punto.
Leí el artículo para UTF-8 en Wikipedia, y aprendí que algunos personajes Unicode consumen hasta 4 bytes de espacio. Por ejemplo, el carácter chino 𤭢 tiene un punto de código Unicode 0x24B62 , que consume espacio de 3 bytes en la memoria.
¿Hay un contenedor de cadenas STL para tratar con este tipo de personajes? Estoy buscando algo como std::string32
. Además, teníamos main()
para el punto de entrada ASCII, wmain()
para el punto de entrada con soporte de caracteres de 16 bits; ¿Qué punto de entrada usamos para el código compatible con Unicode de 3 y 4 bytes?
¿Puedes agregar un pequeño ejemplo?
(Mi sistema operativo: Windows 7 x64)
Windows usa UTF-16 . Cualquier punto de código en el rango de U + 0000 a U + D7FF y U + E000 a U + FFFF se almacenará directamente; cualquiera fuera de esos rangos se dividirá en dos valores de 16 bits de acuerdo con las reglas de codificación UTF-16.
Por ejemplo, 0x24B62 se codificará como 0xd892,0xdf62.
Puede convertir las cadenas para que funcionen de la forma que desee, pero la API de Windows aún querrá y entregará UTF-16, por lo que probablemente sea la más conveniente.
En C ++ estándar tenemos char y wchar_t para almacenar caracteres? char puede almacenar valores entre 0x00 y 0xFF. Y wchar_t puede almacenar valores entre 0x0000 y 0xFFFF
No exactamente:
sizeof(char) == 1 so 1 byte per character.
sizeof(wchar_t) == ? Depends on your system
(for unix usually 4 for Windows usually 2).
Los caracteres Unicode consumen hasta 4 bytes de espacio.
No exactamente. Unicode no es una codificación. Unicode es un estándar que define lo que es cada punto de código y los puntos de código están restringidos a 21 bits. Los primeros 16 bits definieron la posición del carácter en un código simple, mientras que los siguientes 5 bits definen en qué plano se encuentra el carácter.
Hay varias codificaciones Unicode (UTF-8, UTF-16 y UTF-32 son las más comunes) así es como se almacenan los caracteres en la memoria. Hay diferencias prácticas entre los tres.
UTF-8: Great for storage and transport (as it is compact) Bad because it is variable length UTF-16: Horrible in nearly all regards It is always large and it is variable length (anything not on the BMP needs to be encoded as surrogate pairs) UTF-32: Great for in memory representations as it is fixed size Bad because it takes 4 bytes for each character which is usually overkill
Personalmente uso UTF-8 para transporte y almacenamiento y UTF-32 para representación de texto en memoria.
char
y wchar_t
no son los únicos tipos de datos utilizados para las cadenas de texto. C ++ 11 introduce los nuevos tipos de datos char16_t
y char32_t
y las respectivas STL std::u16string
y std::u32string
de std::basic_string
, para abordar la ambigüedad del tipo wchar_t
, que tiene diferentes tamaños y codificaciones en diferentes plataformas. wchar_t
es de 16 bits en algunas plataformas, adecuado para la codificación UTF-16, pero es de 32 bits en otras plataformas, adecuado para la codificación UTF-32 en su lugar. char16_t
es específicamente de 16 bits y UTF-16, y char32_t
es específicamente de 32 bits y UTF-32, en todas las plataformas.
Primero necesita una mejor comprensión de Unicode. Las respuestas específicas a sus preguntas se encuentran en la parte inferior.
Conceptos
Necesita un conjunto de conceptos más matizado de lo que se requiere para el manejo de texto muy simple como se enseña en los cursos introductorios de programación.
- byte
- unidad de código
- punto de código
- personaje abstracto
- personaje percibido por el usuario
Un byte es la unidad de memoria direccionable más pequeña. Por lo general, 8 bits hoy en día, capaz de almacenar hasta 256 valores diferentes. Por definición, un char es un byte.
Una unidad de código es la unidad de datos de tamaño fijo más pequeña utilizada para almacenar texto. Cuando realmente no te importa el contenido del texto y solo quieres copiarlo en algún lugar o calcular cuánta memoria está usando el texto, entonces te importan las unidades de código. De lo contrario, las unidades de código no son de mucha utilidad.
Un punto de código representa un miembro distinto de un juego de caracteres. Cualesquiera que sean los "personajes" en un conjunto de caracteres, a todos ellos se les asigna un número único, y cada vez que veas un número particular codificado, entonces sabrás con qué miembro del conjunto de personajes estás tratando.
Un carácter abstracto es una entidad con significado en un sistema lingüístico, y es distinta de su representación o cualquier punto de código asignado a ese significado.
Los personajes percibidos por el usuario son como suenan; lo que el usuario piensa como personaje en cualquier sistema lingüístico que esté usando.
En los viejos tiempos, char
representaba todas estas cosas: un char
es por definición un byte, en caracteres char*
las unidades de código son caracteres, los conjuntos de caracteres eran pequeños, por lo que los 256 valores representables por caracteres eran suficientes para representar a cada miembro, y los sistemas lingüísticos admitidos eran simples, por lo que los miembros de los conjuntos de caracteres representaban principalmente los caracteres que los usuarios querían usar directamente.
Pero este sistema simple con char
representa prácticamente todo no era suficiente para soportar sistemas más complejos.
El primer problema encontrado fue que algunos lenguajes usan mucho más de 256 caracteres. Así que se introdujeron caracteres ''anchos''. Los caracteres anchos todavía usan un solo tipo para representar cuatro de los conceptos anteriores, unidades de código, puntos de código, caracteres abstractos y caracteres percibidos por el usuario. Sin embargo, los caracteres anchos ya no son bytes únicos. Se pensó que este era el método más simple de admitir conjuntos de caracteres grandes.
El código podría ser el mismo, excepto que trataría con caracteres anchos en lugar de caracteres.
Sin embargo, resulta que muchos sistemas lingüísticos no son tan simples. En algunos sistemas, tiene sentido no tener necesariamente cada carácter percibido por el usuario representado por un único carácter abstracto en el juego de caracteres. Como resultado, el texto que usa el conjunto de caracteres Unicode a veces representa los caracteres percibidos por el usuario usando múltiples caracteres abstractos, o usa un solo carácter abstracto para representar múltiples caracteres percibidos por el usuario.
Los personajes anchos tienen otro problema. Como aumentan el tamaño de la unidad de código, aumentan el espacio utilizado para cada personaje. Si se desea tratar con texto que podría representarse de manera adecuada mediante unidades de código de un solo byte, pero se debe usar un sistema de caracteres anchos, la cantidad de memoria utilizada es mayor que en el caso de las unidades de código de un solo byte. Como tal, se deseaba que los personajes anchos no fueran demasiado anchos. Al mismo tiempo, los caracteres anchos deben ser lo suficientemente amplios como para proporcionar un valor único para cada miembro del juego de caracteres.
Unicode actualmente contiene aproximadamente 100,000 caracteres abstractos. Esto requiere caracteres anchos que son más anchos de lo que a la mayoría de la gente le importa usar. Como resultado, un sistema de caracteres anchos; donde las unidades de código más grandes que un byte se usan para almacenar directamente los valores de punto de código resulta ser indeseable.
Para resumir, originalmente no había necesidad de distinguir entre bytes, unidades de código, puntos de código, caracteres abstractos y caracteres percibidos por el usuario. Con el tiempo, sin embargo, se hizo necesario distinguir entre cada uno de estos conceptos.
Codificaciones
Antes de lo anterior, los datos de texto eran simples de almacenar. Todos los caracteres percibidos por el usuario correspondían a un carácter abstracto, que tenía un valor de punto de código. Había pocos caracteres suficientes que 256 valores eran suficientes. Así que uno simplemente almacenaba los números de puntos de código correspondientes a los caracteres deseados percibidos por el usuario directamente como bytes. Más tarde, con caracteres anchos, los valores correspondientes a los caracteres percibidos por el usuario se almacenaron directamente como enteros de tamaños más grandes, 16 bits, por ejemplo.
Pero como el almacenamiento de texto Unicode de esta manera usaría más memoria de la que la gente está dispuesta a gastar (tres o cuatro bytes por cada carácter), las ''codificaciones'' Unicode almacenan texto no almacenando los valores del punto de código directamente, sino usando una función reversible para calcular algunos número de valores de unidad de código para almacenar para cada punto de código.
La codificación UTF-8, por ejemplo, puede tomar los puntos de código Unicode más comúnmente utilizados y representarlos usando una sola unidad de código de un byte. Los puntos de código menos comunes se almacenan usando dos unidades de código de un byte. Los puntos de código que todavía son menos comunes se almacenan usando tres o cuatro unidades de código.
Esto significa que el texto común generalmente se puede almacenar con la codificación UTF-8 usando menos memoria que los esquemas de caracteres de 16 bits, pero también que los números almacenados no se corresponden necesariamente con los valores de puntos de código de los caracteres abstractos. En cambio, si necesita saber qué caracteres abstractos están almacenados, debe "decodificar" las unidades de código almacenadas. Y si necesita conocer los caracteres percibidos por el usuario, debe convertir los caracteres abstractos en caracteres percibidos por el usuario.
Hay muchas codificaciones diferentes, y para convertir datos usando esas codificaciones en caracteres abstractos, debe conocer el método correcto de decodificación. Los valores almacenados son realmente insignificantes si no se sabe qué codificación se utilizó para convertir los valores del punto de código en unidades de código.
Una implicación importante de las codificaciones es que necesita saber si las manipulaciones particulares de los datos codificados son válidas o significativas.
Por ejemplo, si quiere obtener el ''tamaño'' de una cadena, ¿está contando bytes, unidades de código, caracteres abstractos o caracteres percibidos por el usuario? std::string::size()
cuenta unidades de código, y si necesita un conteo diferente, entonces tiene que usar otro método.
Como otro ejemplo, si divide una cadena codificada, necesita saber si lo está haciendo de tal manera que el resultado aún sea válido en esa codificación y que el significado de los datos no haya cambiado involuntariamente. Por ejemplo, puede dividir entre unidades de código que pertenecen al mismo punto de código, produciendo una codificación no válida. O puede dividir entre puntos de código que deben combinarse para representar un carácter percibido por el usuario y así producir datos que el usuario verá como incorrectos.
Respuestas
Hoy, char
y wchar_t
solo pueden considerarse unidades de código. El hecho de que char
sea de un solo byte no le impide representar puntos de código que toman dos, tres o cuatro bytes. Simplemente tiene que usar dos, tres o cuatro caracteres en secuencia. Así es como se pretendía que UTF-8 funcionara. Del mismo modo, las plataformas que usan dos bytes wchar_t
para representar a UTF-16 simplemente usan dos wchar_t
en una fila cuando es necesario. Los valores reales de char
y wchar_t
no representan puntos de código Unicode individualmente. Representan valores de unidad de código que resultan de codificar los puntos de código. Por ejemplo, el punto de código Unicode U + 0400 está codificado en dos unidades de código en UTF-8 -> 0xD0 0x80
. El punto de código Unicode U + 24B62 se codifica de manera similar como cuatro unidades de código 0xF0 0xA4 0xAD 0xA2
.
Entonces puede usar std::string
para contener datos codificados en UTF-8.
En Windows main()
soporta no solo ASCII, sino cualquiera que sea la codificación de caracteres del sistema. Lamentablemente, Windows no es compatible con UTF-8 ya que la codificación de sistema es similar a la de otras plataformas, por lo que está limitado a codificaciones heredadas como cp1252 o lo que sea que su sistema esté configurado para usar. Sin embargo, puede utilizar una llamada API de Win32 para acceder directamente a los parámetros de la línea de comando UTF-16 en lugar de utilizar los parámetros argc
y argv
de main()
. Ver GetCommandLineW()
y CommandLineToArgvW
.
El parámetro argv
wmain()
es completamente compatible con Unicode. Las unidades de código de 16 bits almacenadas en wchar_t
en Windows son unidades de código UTF-16. La API de Windows usa UTF-16 de forma nativa, por lo que es bastante fácil trabajar con Windows. wmain()
no es estándar, así que confiar en esto no será portátil.