Tenga en cuenta que no se trata de una "programación Unicode estricta" per se, sino de algo de experiencia práctica.
Lo que hicimos en mi empresa fue crear una biblioteca contenedora alrededor de la biblioteca ICU de IBM. La biblioteca contenedora tiene una interfaz UTF-8 y se convierte a UTF-16 cuando es necesario llamar a ICU. En nuestro caso, no nos preocupamos demasiado por los golpes de rendimiento. Cuando el rendimiento era un problema, también proporcionamos interfaces UTF-16 (utilizando nuestro propio tipo de datos).
Las aplicaciones pueden permanecer en gran parte como están (usando char), aunque en algunos casos deben ser conscientes de ciertos problemas. Por ejemplo, en lugar de strncpy () usamos un contenedor que evita cortar secuencias UTF-8. En nuestro caso, esto es suficiente, pero también se podrían considerar controles para combinar caracteres. También tenemos envoltorios para contar la cantidad de puntos de código, la cantidad de grafemas, etc.
Al interactuar con otros sistemas, a veces necesitamos hacer una composición de caracteres personalizada, por lo que es posible que necesite algo de flexibilidad allí (según su aplicación).
No usamos wchar_t. El uso de ICU evita problemas inesperados en la portabilidad (pero no otros problemas inesperados, por supuesto :-).
strncpy
usa correctamente, es perfectamente seguro usarlo con UTF-8.strcpy
(que de hecho es seguro de usar con UTF-8). Las personas que lo usanstrncpy
probablemente lo hagan porque no saben si el búfer de destino es lo suficientemente grande, por lo que quieren pasar un número máximo de bytes para copiar, lo que de hecho puede crear secuencias UTF-8 no válidas.C99 o anterior
El estándar C (C99) proporciona caracteres anchos y caracteres multibyte, pero como no hay garantía sobre lo que pueden contener esos caracteres anchos, su valor es algo limitado. Para una implementación dada, brindan soporte útil, pero si su código debe poder moverse entre implementaciones, no hay garantía suficiente de que serán útiles.
En consecuencia, el enfoque sugerido por Hans van Eck (que consiste en escribir un resumen de la biblioteca ICU - Componentes internacionales para Unicode -) es sólido, en mi opinión.
La codificación UTF-8 tiene muchos méritos, uno de los cuales es que si no se mete con los datos (truncándolos, por ejemplo), puede ser copiado por funciones que no son plenamente conscientes de las complejidades de UTF-8 codificación. Este no es categóricamente el caso de
wchar_t
.Unicode en su totalidad es un formato de 21 bits. Es decir, Unicode reserva puntos de código de U + 0000 a U + 10FFFF.
Una de las cosas útiles de los formatos UTF-8, UTF-16 y UTF-32 (donde UTF significa Formato de transformación Unicode; consulte Unicode ) es que puede convertir entre las tres representaciones sin pérdida de información. Cada uno puede representar cualquier cosa que los demás puedan representar. Tanto UTF-8 como UTF-16 son formatos de varios bytes.
UTF-8 es bien conocido por ser un formato multibyte, con una estructura cuidadosa que hace posible encontrar el inicio de los caracteres en una cadena de manera confiable, comenzando en cualquier punto de la cadena. Los caracteres de un solo byte tienen el bit alto establecido en cero. Los caracteres de varios bytes tienen el primer carácter que comienza con uno de los patrones de bits 110, 1110 o 11110 (para caracteres de 2 bytes, 3 bytes o 4 bytes), y los bytes siguientes siempre comienzan con 10. Los caracteres de continuación siempre están en el rango 0x80 .. 0xBF. Existen reglas que establecen que los caracteres UTF-8 deben representarse en el formato mínimo posible. Una consecuencia de estas reglas es que los bytes 0xC0 y 0xC1 (también 0xF5..0xFF) no pueden aparecer en datos UTF-8 válidos.
U+0000 .. U+007F 1 byte 0xxx xxxx U+0080 .. U+07FF 2 bytes 110x xxxx 10xx xxxx U+0800 .. U+FFFF 3 bytes 1110 xxxx 10xx xxxx 10xx xxxx U+10000 .. U+10FFFF 4 bytes 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
Originalmente, se esperaba que Unicode fuera un conjunto de códigos de 16 bits y que todo encajara en un espacio de código de 16 bits. Desafortunadamente, el mundo real es más complejo y tuvo que expandirse a la codificación actual de 21 bits.
Por tanto, UTF-16 es un conjunto de códigos de una sola unidad (palabra de 16 bits) para el 'Plano multilingüe básico', es decir, los caracteres con puntos de código Unicode U + 0000 .. U + FFFF, pero utiliza dos unidades (32 bits) para caracteres fuera de este rango. Por lo tanto, el código que funciona con la codificación UTF-16 debe poder manejar codificaciones de ancho variable, al igual que UTF-8. Los códigos para los caracteres de doble unidad se denominan sustitutos.
UTF-32, por supuesto, puede codificar cualquier punto de código Unicode en una sola unidad de almacenamiento. Es eficiente para la computación pero no para el almacenamiento.
Puede encontrar mucha más información en los sitios web de ICU y Unicode.
C11 y
<uchar.h>
El estándar C11 cambió las reglas, pero no todas las implementaciones se han puesto al día con los cambios incluso ahora (mediados de 2017). El estándar C11 resume los cambios para el soporte Unicode como:
Lo que sigue es un resumen mínimo de la funcionalidad. La especificación incluye:
(Traduciendo las referencias cruzadas:
<stddef.h>
definesize_t
,<wchar.h>
definembstate_t
y<stdint.h>
defineuint_least16_t
yuint_least32_t
). El<uchar.h>
encabezado también define un conjunto mínimo de funciones de conversión (reiniciables):Existen reglas sobre qué caracteres Unicode se pueden usar en identificadores usando notaciones
\unnnn
o\U00nnnnnn
. Es posible que deba activar activamente el soporte para dichos caracteres en identificadores. Por ejemplo, GCC requiere-fextended-identifiers
permitir estos en identificadores.Tenga en cuenta que macOS Sierra (10.12.5), por nombrar solo una plataforma, no es compatible
<uchar.h>
.fuente
wchar_t
y amigos un poco cortos aquí. Estos tipos son esenciales para permitir que la biblioteca C maneje texto en cualquier codificación (incluidas las codificaciones que no son Unicode). Sin los amplios tipos de caracteres y funciones, la biblioteca C requeriría un conjunto de funciones de manejo de texto para cada codificación admitida: imagine tener koi8len, koi8tok, koi8printf solo para texto codificado en KOI-8 y utf8len, utf8tok, utf8printf para UTF-8 texto. En cambio, tenemos la suerte de tener un conjunto de estas funciones (sin contar los originales ASCII):wcslen
,wcstok
, ywprintf
.mbstowcs
y amigos) para convertir cualquier codificación compatible awchar_t
. Una vez enwchar_t
formato, el programador puede utilizar el conjunto único de funciones de manejo de texto amplio que proporciona la biblioteca C. Una buena implementación de la biblioteca C admitirá prácticamente cualquier codificación que la mayoría de los programadores necesitarán (en uno de mis sistemas, tengo acceso a 221 codificaciones únicas).wchar_t
sea lo suficientemente amplia para contener cualquier carácter soportado por la implementación. Esto significa (con posiblemente una excepción notable) que la mayoría de las implementaciones se asegurarán de que sean lo suficientemente amplias como para que un programa que las utilicewchar_t
maneje cualquier codificación admitida por el sistema (la de Microsoftwchar_t
tiene solo 16 bits de ancho, lo que significa que su implementación no es totalmente compatible con todas las codificaciones, más notablemente las diversas codificaciones UTF, pero la suya es la excepción, no la regla).Estas preguntas frecuentes son una gran cantidad de información. Entre esa página y este artículo de Joel Spolsky , tendrá un buen comienzo.
Una conclusión a la que llegué en el camino:
wchar_t
es de 16 bits en Windows, pero no necesariamente de 16 bits en otras plataformas. Creo que es un mal necesario en Windows, pero probablemente se pueda evitar en otros lugares. La razón por la que es importante en Windows es que necesita usar archivos que tienen caracteres que no son ASCII en el nombre (junto con la versión W de las funciones).Tenga en cuenta que las API de Windows que toman
wchar_t
cadenas esperan codificación UTF-16. Tenga en cuenta también que esto es diferente a UCS-2. Tome nota de los pares sustitutos. Esta página de prueba tiene pruebas esclarecedoras.Si usted es la programación en Windows, no se puede utilizar
fopen()
,fread()
,fwrite()
, etc, ya que sólo tienenchar *
y no entienden codificación UTF-8. Hace que la portabilidad sea dolorosa.fuente
f*
y sus amigos trabajan conchar *
en todas las plataformas porque el estándar lo dice: usewcs*
en su lugar para wchar_t.Para realizar una programación Unicode estricta:
strlen
,strcpy
, ... pero sus homólogos WideStringwstrlen
,wsstrcpy
, ...)Las secuencias de caracteres de varios bytes es una codificación anterior a la codificación UTF-16 (la que se usa normalmente con
wchar_t
) y me parece que es más bien solo para Windows.Nunca he oído hablar de eso
wint_t
.fuente
Lo más importante es siempre hacer una clara distinción entre texto y datos binarios . Trate de seguir el modelo de Python 3.x
str
vs.bytes
o SQLTEXT
vsBLOB
.Desafortunadamente, C confunde el problema al usar
char
tanto "carácter ASCII" comoint_least8_t
. Querrás hacer algo como:typedef char UTF8; // for code units of UTF-8 strings typedef unsigned char BYTE; // for binary data
Es posible que también desee typedefs para unidades de código UTF-16 y UTF-32, pero esto es más complicado porque la codificación de
wchar_t
no está definida. Necesitará solo un preprocesador#if
. Algunas macros útiles en C y C ++ 0x son:__STDC_UTF_16__
- Si está definido, el tipo_Char16_t
existe y es UTF-16.__STDC_UTF_32__
- Si está definido, el tipo_Char32_t
existe y es UTF-32.__STDC_ISO_10646__
- Si está definido, entonceswchar_t
es UTF-32._WIN32
- En Windows,wchar_t
es UTF-16, aunque rompe el estándar.WCHAR_MAX
- Se puede usar para determinar el tamaño dewchar_t
, pero no si el sistema operativo lo usa para representar Unicode.Ver también:
No. UTF-8 es una codificación Unicode perfectamente válida que usa
char*
cadenas. Tiene la ventaja de que si su programa es transparente a bytes que no son ASCII (por ejemplo, un convertidor de final de línea que actúa\r
y\n
pasa a través de otros caracteres sin cambios), ¡no necesitará hacer ningún cambio!Si va con UTF-8, deberá cambiar todas las suposiciones que
char
= carácter (por ejemplo, no llamartoupper
en un bucle) ochar
= columna de pantalla (por ejemplo, para ajustar texto).Si elige UTF-32, tendrá la simplicidad de los caracteres de ancho fijo (pero no los grafemas de ancho fijo , pero deberá cambiar el tipo de todas sus cadenas).
Si elige UTF-16, tendrá que descartar tanto la suposición de caracteres de ancho fijo como la suposición de unidades de código de 8 bits, lo que hace que esta sea la ruta de actualización más difícil de las codificaciones de un solo byte.
Recomendaría evitarlo activamente
wchar_t
porque no es multiplataforma: a veces es UTF-32, a veces es UTF-16 y, a veces, es una codificación de Asia oriental pre-Unicode. Recomendaría usartypedefs
Aún más importante, evite
TCHAR
.fuente
char *
pueden tener problemas si se pasan unaconst char *
última que recuerdo (pero soy vago sobre esto y qué funciones, así que tómalo con una pizca de sal). El hecho de que sea más complicado con otros lenguajes no significa que sea un mal diseño.No confiaría en ninguna implementación de biblioteca estándar. Simplemente use sus propios tipos Unicode.
#include <windows.h> typedef unsigned char utf8_t; typedef unsigned short utf16_t; typedef unsigned long utf32_t; int main ( int argc, char *argv[] ) { int msgBoxId; utf16_t lpText[] = { 0x03B1, 0x0009, 0x03B2, 0x0009, 0x03B3, 0x0009, 0x03B4, 0x0000 }; utf16_t lpCaption[] = L"Greek Characters"; unsigned int uType = MB_OK; msgBoxId = MessageBoxW( NULL, lpText, lpCaption, uType ); return 0; }
fuente
Básicamente, desea tratar las cadenas en la memoria como
wchar_t
matrices en lugar de char. Cuando realiza cualquier tipo de E / S (como leer / escribir archivos), puede codificar / decodificar usando UTF-8 (esta es probablemente la codificación más común) que es lo suficientemente simple de implementar. Solo busca en Google las RFC. Entonces, en la memoria, nada debe ser de varios bytes. Unowchar_t
representa un personaje. Sin embargo, cuando se trata de serializar, es cuando necesita codificar en algo como UTF-8, donde algunos caracteres están representados por varios bytes.También tendrá que escribir nuevas versiones de
strcmp
etc. para las cadenas de caracteres anchas, pero esto no es un gran problema. El mayor problema será la interoperabilidad con bibliotecas / código existente que solo aceptan matrices de caracteres.Y cuando se trata de
sizeof(wchar_t)
(necesitará 4 bytes si quiere hacerlo bien) siempre puede redefinirlo a un tamaño más grande contypedef
/macro
hacks si lo necesita.fuente
Por lo que sé, wchar_t depende de la implementación (como se puede ver en este artículo de wiki ). Y no es Unicode.
fuente