Codificación Unicode para cadenas literales en C ++ 11

85

Siguiendo una pregunta relacionada , me gustaría preguntar sobre los nuevos tipos de caracteres y literales de cadena en C ++ 11. Parece que ahora tenemos cuatro tipos de caracteres y cinco tipos de cadenas literales. Los tipos de personajes:

char     a =  '\x30';         // character, no semantics
wchar_t  b = L'\xFFEF';       // wide character, no semantics
char16_t c = u'\u00F6';       // 16-bit, assumed UTF16?
char32_t d = U'\U0010FFFF';   // 32-bit, assumed UCS-4

Y los literales de cadena:

char     A[] =  "Hello\x0A";         // byte string, "narrow encoding"
wchar_t  B[] = L"Hell\xF6\x0A";      // wide string, impl-def'd encoding
char16_t C[] = u"Hell\u00F6";        // (1)
char32_t D[] = U"Hell\U000000F6\U0010FFFF"; // (2)
auto     E[] = u8"\u00F6\U0010FFFF"; // (3)

La pregunta es la siguiente: ¿Las referencias de caracteres \x/ \u/ \Use pueden combinar libremente con todos los tipos de cadenas? ¿Todos los tipos de cadenas son de ancho fijo, es decir, las matrices contienen exactamente tantos elementos como aparecen en el literal, o las referencias a \x/ \u/ \Use expanden en un número variable de bytes? ¿Las cadenas u""y u8""tienen semántica de codificación, por ejemplo, puedo decir char16_t x[] = u"\U0010FFFF", y el punto de código que no es BMP se codifica en una secuencia UTF16 de dos unidades? ¿Y de manera similar para u8? En (1), ¿puedo escribir sustitutos solitarios con \u? Finalmente, ¿alguna de las funciones de cadena es consciente de la codificación (es decir, son conscientes de los caracteres y pueden detectar secuencias de bytes no válidas)?

Esta es una pregunta un poco abierta, pero me gustaría obtener una imagen lo más completa posible de la nueva codificación UTF y las funciones de tipo del nuevo C ++ 11.

Kerrek SB
fuente
4
GCC codifica u"\U0010FFFF"en un par sustituto.
Kennytm

Respuestas:

57

¿Las referencias de caracteres \ x / \ u / \ U se pueden combinar libremente con todos los tipos de cadenas?

No. \xse puede utilizar en cualquier cosa, pero \uy \Usólo se puede utilizar en las cadenas que son específicamente UTF-codificado. Sin embargo, para cualquier cadena codificada en UTF, \uy \Use puede utilizar como mejor le parezca.

¿Todos los tipos de cadenas son de ancho fijo, es decir, las matrices contienen exactamente tantos elementos como aparecen en el literal, o las referencias \ x / \ u / \ U se expanden en un número variable de bytes?

No de la manera que quieres decir. \x, \uy \Use convierten en función de la codificación de la cadena. El número de esos char16_tvalores de "unidades de código" (utilizando términos Unicode. A es una unidad de código UTF-16) depende de la codificación de la cadena que los contiene. El literal u8"\u1024"crearía una cadena que contiene 2 chars más un terminador nulo. El literal u"\u1024"crearía una cadena que contiene 1 char16_tmás un terminador nulo.

El número de unidades de código utilizadas se basa en la codificación Unicode.

¿Las cadenas u "" y u8 "" tienen semántica de codificación, por ejemplo, puedo decir char16_t x [] = u "\ U0010FFFF", y el punto de código que no es BMP se codifica en una secuencia UTF16 de dos unidades?

u""crea una cadena codificada en UTF-16. u8""crea una cadena codificada en UTF-8. Se codificarán según la especificación Unicode.

En (1), ¿puedo escribir sustitutos solitarios con \ u?

Absolutamente no. La especificación prohíbe expresamente el uso de pares sustitutos UTF-16 (0xD800-0xDFFF) como puntos de código para \uo \U.

Finalmente, ¿alguna de las funciones de cadena es consciente de la codificación (es decir, son conscientes de los caracteres y pueden detectar secuencias de bytes no válidas)?

Absolutamente no. Bueno, permíteme reformular eso.

std::basic_stringno se ocupa de codificaciones Unicode. Ciertamente pueden almacenar cadenas codificadas en UTF. Pero sólo pueden pensar en ellos como secuencias de char, char16_to char32_t; no pueden pensar en ellos como una secuencia de puntos de código Unicode que están codificados con un mecanismo particular. basic_string::length()devolverá el número de unidades de código, no puntos de código. Y obviamente, las funciones de cadena de la biblioteca estándar de C son totalmente inútiles

Sin embargo, debe tenerse en cuenta que la "longitud" de una cadena Unicode no significa el número de puntos de código. Algunos puntos de código combinan "caracteres" (un nombre desafortunado), que se combinan con el punto de código anterior. Por lo tanto, se pueden asignar múltiples puntos de código a un solo carácter visual.

Iostreams puede, de hecho, leer / escribir valores codificados en Unicode. Para hacerlo, tendrá que usar una configuración regional para especificar la codificación e imbuirla adecuadamente en los distintos lugares. Es más fácil decirlo que hacerlo, y no tengo ningún código para mostrarte cómo.

Nicol Bolas
fuente
7
@Philipp: No, no lo son. Unicode los reserva específicamente para sustitutos UTF-16. Y, como se indicó, la especificación de C ++ 0x dice que la compilación fallará si intenta designar un punto de código en ese rango.
Nicol Bolas
12
Tu enlace prueba que ellos son puntos de código. Si no confía en Wikipedia, lea las definiciones 9 y 10 en el capítulo 3 del Estándar. Sin embargo, los puntos de código sustitutos en cadenas literales están prohibidos en C ++ 0x por la regla § 2.4 / 2.
Philipp
1
Después de leer, confirmo también que los puntos de código sustitutos se aceptan en literales de cadena.
George Kourtis
En C11, \xno se pueden usar con cualquier cosa, por ejemplo, U + 1F984 no funcionará con el \ x prefijo y, \uy \Uno se puede utilizar con los caracteres de control ASCII, al menos en Clang.
MarcusJ