Saltar al contenido

¿Cómo uso correctamente std :: string en UTF-8 en C ++?

Solución:

Glosario Unicode

Unicode es un tema vasto y complejo. No deseo profundizar demasiado allí, sin embargo, es necesario un glosario rápido:

  1. Puntos de código: Los puntos de código son los bloques de construcción básicos de Unicode, un punto de código es solo un número entero mapeado a un sentido. La porción entera cabe en 32 bits (bueno, 24 bits en realidad), y el significado puede ser una letra, un diacrítico, un espacio en blanco, un signo, un emoticón, la mitad de una bandera, … e incluso puede ser “el la siguiente parte se lee de derecha a izquierda “.
  2. Clústeres de grafemas: Los grupos de grafemas son grupos de puntos de código relacionados semánticamente, por ejemplo, una bandera en unicode se representa asociando dos puntos de código; cada uno de esos dos, de forma aislada, no tiene significado, pero asociados juntos en un Grapheme Cluster representan una bandera. Los grupos de grafemas también se utilizan para emparejar una letra con un diacrítico en algunos guiones.

Este es el básico de Unicode. La distinción entre Code Point y Grapheme Cluster se puede pasar por alto en su mayoría porque para la mayoría de los lenguajes modernos cada “carácter” se asigna a un único Code Point (hay formas acentuadas dedicadas para las combinaciones de letras y diacríticos de uso común). Aún así, si se aventura en emoticonos, banderas, etc … entonces es posible que deba prestar atención a la distinción.


Imprimación UTF

Luego, se debe codificar una serie de puntos de código Unicode; las codificaciones comunes son UTF-8, UTF-16 y UTF-32, las dos últimas existentes en las formas Little-Endian y Big-Endian, para un total de 5 codificaciones comunes.

En UTF-X, X es el tamaño en bits del Unidad de código, cada Punto de Código se representa como una o varias Unidades de Código, dependiendo de su magnitud:

  • UTF-8: 1 a 4 unidades de código,
  • UTF-16: 1 o 2 unidades de código,
  • Unidad de código UTF-32: 1.

std::string y std::wstring.

  1. No utilice std::wstring si te importa la portabilidadwchar_t es de solo 16 bits en Windows); usar std::u32string en su lugar (también conocido como std::basic_string<char32_t>).
  2. La representación en memoria (std::string o std::wstring) es independiente de la representación en disco (UTF-8, UTF-16 o UTF-32), así que prepárese para tener que convertir en el límite (lectura y escritura).
  3. Mientras que un 32 bits wchar_t asegura que una Unidad de Código representa un Punto de Código completo, todavía no representa un Clúster de Grafema completo.

Si solo está leyendo o componiendo cadenas, no debería tener pequeños problemas con std::string o std::wstring.

Los problemas comienzan cuando comienza a cortar y cortar en cubitos, luego debe prestar atención a (1) límites de puntos de código (en UTF-8 o UTF-16) y (2) límites de grupos de grafemas. El primero se puede manejar con bastante facilidad por su cuenta, el segundo requiere el uso de una biblioteca compatible con Unicode.


Cosecha std::string o std::u32string?

Si el rendimiento es una preocupación, es probable que std::string funcionará mejor debido a su menor huella de memoria; aunque el uso intensivo del chino puede cambiar el trato. Como siempre, perfil.

Si los clústeres de Grapheme no son un problema, entonces std::u32string tiene la ventaja de simplificar las cosas: 1 Unidad de Código -> 1 Punto de Código significa que no puede dividir accidentalmente Puntos de Código, y todas las funciones de std::basic_string trabajar fuera de la caja.

Si interactúa con el software tomando std::string o char*/char const*, luego apégate a std::string para evitar conversiones de ida y vuelta. De lo contrario, será un dolor.


UTF-8 en std::string.

UTF-8 funciona bastante bien en std::string.

La mayoría de las operaciones funcionan desde el primer momento porque la codificación UTF-8 se sincroniza automáticamente y es compatible con ASCII.

Debido a la forma en que se codifican los puntos de código, la búsqueda de un punto de código no puede coincidir accidentalmente con el medio de otro punto de código:

  • str.find('n') obras,
  • str.find("...") obras para hacer coincidir byte por byte1,
  • str.find_first_of("rn") obras si busca caracteres ASCII.

Similar, regex la mayoría de las veces debería funcionar fuera de la caja. Como una secuencia de caracteres ("haha") es solo una secuencia de bytes ("哈"), los patrones de búsqueda básicos deberían funcionar desde el primer momento.

Sin embargo, tenga cuidado con las clases de personajes (como [:alphanum:]), ya que dependiendo del tipo de expresión regular y la implementación, puede coincidir o no con los caracteres Unicode.

Del mismo modo, tenga cuidado con la aplicación de repetidores a “caracteres” que no sean ASCII, "哈?" solo puede considerar que el último byte es opcional; use paréntesis para delinear claramente la secuencia repetida de bytes en tales casos: "(哈)?".

1 Los conceptos clave para la búsqueda son la normalización y la recopilación; esto afecta a todas las operaciones de comparación. std::string siempre comparará (y por lo tanto clasificará) byte por byte, sin tener en cuenta las reglas de comparación específicas de un idioma o uso. Si necesita manejar la normalización / clasificación completa, necesita una biblioteca Unicode completa, como ICU.

std::string y los amigos son agnósticos a la codificación. La única diferencia entre std::wstring y std::string son esos std::wstring usos wchar_t como elemento individual, no char. Para la mayoría de los compiladores, este último es de 8 bits. Se supone que el primero es lo suficientemente grande como para contener cualquier carácter Unicode, pero en la práctica en algunos sistemas no lo es (el compilador de Microsoft, por ejemplo, usa un tipo de 16 bits). No puede almacenar UTF-8 en std::wstring; eso no es para lo que está diseñado. Está diseñado para ser un equivalente de UTF-32, una cadena en la que cada elemento es un único punto de código Unicode.

Si desea indexar cadenas UTF-8 por punto de código Unicode o glifo Unicode compuesto (o alguna otra cosa), cuente la longitud de una cadena UTF-8 en puntos de código Unicode o algún otro objeto Unicode, o busque por punto de código Unicode, ya está necesitará usar algo que no sea la biblioteca estándar. ICU es una de las bibliotecas en el campo; puede haber otros.

Algo que probablemente vale la pena señalar es que si está buscando caracteres ASCII, puede tratar una corriente de bytes UTF-8 como si fuera byte a byte. Cada carácter ASCII codifica lo mismo en UTF-8 que en ASCII, y se garantiza que cada unidad multibyte en UTF-8 no incluirá ningún byte en el rango ASCII.

Ambos std::string y std::wstring debe utilizar codificación UTF para representar Unicode. En macOS específicamente, std::string es UTF-8 (unidades de código de 8 bits), y std::wstring es UTF-32 (unidades de código de 32 bits); tenga en cuenta que el tamaño de wchar_t depende de la plataforma.

Para ambos, size rastrea el número de unidades de código en lugar del número de puntos de código o grupos de grafemas. (Un punto de código es una entidad denominada Unicode, uno o más de los cuales forman un grupo de grafemas. Los grupos de grafemas son los caracteres visibles con los que interactúan los usuarios, como letras o emojis).

Aunque no estoy familiarizado con la representación Unicode del chino, es muy posible que cuando use UTF-32, la cantidad de unidades de código sea a menudo muy cercana a la cantidad de grupos de grafemas. Sin embargo, obviamente, esto tiene el costo de usar hasta 4 veces más memoria.

La solución más precisa sería utilizar una biblioteca Unicode, como ICU, para calcular las propiedades Unicode que busca.

Por último, las cadenas UTF en lenguajes humanos que no utilizan caracteres de combinación suelen funcionar bastante bien con find/regex. No estoy seguro sobre el chino, pero el inglés es uno de ellos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *