¿Debe UTF-16 considerarse nocivo?


432

Voy a preguntar lo que probablemente sea una pregunta bastante controvertida: "¿Debería una de las codificaciones más populares, UTF-16, considerarse nociva?"

¿Por qué hago esta pregunta?

¿Cuántos programadores son conscientes del hecho de que UTF-16 es en realidad una codificación de longitud variable? Con esto quiero decir que hay puntos de código que, representados como pares sustitutos, toman más de un elemento.

Lo sé; muchas aplicaciones, frameworks y API usan UTF-16, como String de Java, String de C #, Win32 API, bibliotecas Qt GUI, la biblioteca ICU Unicode, etc. Sin embargo, con todo eso, hay muchos errores básicos en el procesamiento de caracteres fuera de BMP (caracteres que deben codificarse utilizando dos elementos UTF-16).

Por ejemplo, intente editar uno de estos caracteres:

  • 𝄞 ( U + 1D11E ) SÍMBOLO MUSICAL G CLEF
  • 𝕥 ( U + 1D565 ) MATEMÁTICA DE DOBLE ESTRUCTURA PEQUEÑA T
  • 𝟶 ( U + 1D7F6 ) MONOESPACIO DIGITAL MATEMÁTICO CERO
  • 𠂊 ( U + 2008A ) Personaje Han

Puede perder algunos, dependiendo de las fuentes que haya instalado. Todos estos personajes están fuera del BMP (Plano multilingüe básico). Si no puede ver estos caracteres, también puede intentar mirarlos en la referencia de caracteres Unicode .

Por ejemplo, intente crear nombres de archivo en Windows que incluyan estos caracteres; intente eliminar estos caracteres con un "espacio de retroceso" para ver cómo se comportan en diferentes aplicaciones que usan UTF-16. Hice algunas pruebas y los resultados son bastante malos:

  • Opera tiene problemas para editarlos (eliminar requiere 2 prensas en el espacio de retroceso)
  • El Bloc de notas no puede manejarlos correctamente (eliminar las 2 pulsaciones requeridas en el espacio de retroceso)
  • Edición de nombres de archivo en cuadros de diálogo de Windows en roto (eliminar requiere 2 prensas en el espacio de retroceso)
  • Todas las aplicaciones QT3 no pueden lidiar con ellas: muestra dos cuadrados vacíos en lugar de un símbolo.
  • Python codifica dichos caracteres incorrectamente cuando se usa directamente u'X'!=unicode('X','utf-16')en algunas plataformas cuando X en caracteres fuera de BMP.
  • Python 2.5 unicodedata no puede obtener propiedades en dichos caracteres cuando python se compila con cadenas Unicode UTF-16.
  • StackOverflow parece eliminar estos caracteres del texto si se editan directamente como caracteres Unicode (estos caracteres se muestran usando escapes Unicode HTML).
  • WinForms TextBox puede generar una cadena no válida cuando se limita con MaxLength.

Parece que tales errores son extremadamente fáciles de encontrar en muchas aplicaciones que usan UTF-16.

Entonces ... ¿Crees que UTF-16 debería considerarse dañino?


64
Realmente no es correcto. Explico que si escribe "שָׁ" el carácter compuesto que consiste en "ש", "ָ" y "ׁ", vovels, entonces la eliminación de cada uno de ellos es lógico, elimina un punto de código cuando presiona " retroceso "y elimine todos los caracteres, incluidos los vovels, cuando presione" del ". Pero nunca se produce un estado de texto ilegal: puntos de código ilegales. Por lo tanto, la situación cuando presiona la tecla de retroceso y obtiene texto ilegítimo es incorrecta.

41
CiscoIPPhone: si un error es "reportado varias veces por diferentes personas", y luego, un par de años después, un desarrollador escribe en un blog de desarrollo que "¡Créalo o no, el comportamiento es principalmente intencional!", Entonces (para poner suavemente) Tiendo a pensar que probablemente no sea la mejor decisión de diseño jamás tomada. :-) Solo porque sea intencional no significa que no sea un error.

145
Buena publicación. UTF-16 es de hecho el "peor de los dos mundos": UTF8 es de longitud variable, cubre todo Unicode, requiere un algoritmo de transformación hacia y desde puntos de código sin procesar, se restringe a ASCII y no tiene problemas de endianness. UTF32 es de longitud fija, no requiere transformación, pero ocupa más espacio y tiene problemas de resistencia. Hasta ahora todo bien, puede usar UTF32 internamente y UTF8 para la serialización. Pero UTF16 no tiene beneficios: depende de endian, es de longitud variable, ocupa mucho espacio, no es compatible con ASCII. El esfuerzo necesario para lidiar con UTF16 correctamente podría gastarse mejor en UTF8.
Kerrek SB

26
@ Ian: UTF-8 NO tiene las mismas advertencias que UTF-8. No puede tener sustitutos en UTF-8. UTF-8 no se disfraza como algo que no es, pero la mayoría de los programadores que usan UTF-16 lo están usando mal. Lo sé. Los he visto una y otra y otra y otra vez.
tchrist

18
Además, UTF-8 no tiene el problema porque todos lo tratan como una codificación de ancho variable. La razón por la que UTF-16 tiene el problema es porque todos lo tratan como una codificación de ancho fijo.
Christoffer Hammarström

Respuestas:


340

Esta es una vieja respuesta.
Vea UTF-8 Everywhere para las últimas actualizaciones.

Opinión: Sí, UTF-16 debe considerarse dañino . La razón por la que existe es porque hace algún tiempo solía haber una creencia equivocada de que widechar será lo que UCS-4 es ahora.

A pesar del "anglocentrismo" de UTF-8, debe considerarse la única codificación útil para el texto. Uno puede argumentar que los códigos fuente de programas, páginas web y archivos XML, nombres de archivos del sistema operativo y otras interfaces de texto de computadora a computadora nunca deberían haber existido. Pero cuando lo hacen, el texto no es solo para lectores humanos.

Por otro lado, los gastos generales UTF-8 son un pequeño precio a pagar mientras que tienen ventajas significativas. Ventajas como la compatibilidad con el código inconsciente con el que simplemente pasa cadenas char*. Esto es una gran cosa Hay pocos personajes útiles que son más cortos en UTF-16 que en UTF-8.

Creo que todas las demás codificaciones morirán eventualmente. Esto implica que MS-Windows, Java, ICU, Python dejan de usarlo como su favorito. Después de largas investigaciones y discusiones, las convenciones de desarrollo en mi compañía prohíben el uso de UTF-16 en cualquier lugar, excepto las llamadas a la API del sistema operativo, y esto a pesar de la importancia del rendimiento en nuestras aplicaciones y el hecho de que usamos Windows. Las funciones de conversión se desarrollaron para convertir los UTF8 siempre asumidos std::stringa UTF-16 nativo, que Windows en sí mismo no admite correctamente .

A las personas que dicen " usar lo que se necesita donde se necesita ", les digo: hay una gran ventaja en usar la misma codificación en todas partes, y no veo razón suficiente para hacerlo de otra manera. En particular, creo que agregar wchar_ta C ++ fue un error, y también lo son las adiciones de Unicode a C ++ 0x. Lo que debe ser exigido a las implementaciones STL es sin embargo que todos los std::stringo char*parámetro podría considerarse compatible con Unicode.

También estoy en contra del enfoque de " usa lo que quieras ". No veo ninguna razón para tal libertad. Hay suficiente confusión sobre el tema del texto, lo que resulta en todo este software dañado. Dicho lo anterior, estoy convencido de que los programadores finalmente deben llegar a un consenso sobre UTF-8 como una forma adecuada. (Vengo de un país que no habla ascii y crecí en Windows, por lo que se esperaba que atacara el UTF-16 por motivos religiosos).

Me gustaría compartir más información sobre cómo escribo texto en Windows y lo que recomiendo a todos los demás para la corrección Unicode comprobada en tiempo de compilación, la facilidad de uso y una mejor multiplataforma del código. La sugerencia difiere sustancialmente de lo que generalmente se recomienda como la forma correcta de usar Unicode en Windows. Sin embargo, la investigación en profundidad de estas recomendaciones resultó en la misma conclusión. Entonces aquí va:

  • No utilice wchar_tni std::wstringen ningún otro lugar que no sea un punto adyacente a las API que aceptan UTF-16.
  • No use _T("")ni L""literales UTF-16 (estos deben ser IMO eliminados del estándar, como parte de la desaprobación UTF-16).
  • No utilice tipos, funciones o sus derivados que sean sensibles a la _UNICODEconstante, como LPTSTRo CreateWindow().
  • Sin embargo, _UNICODEsiempre definido, para evitar pasar char*cadenas a WinAPI que se compila silenciosamente
  • std::stringsy char*en cualquier parte del programa se consideran UTF-8 (si no se dice lo contrario)
  • Todas mis cadenas son std::string, aunque puedes pasar char * o cadena literal a convert(const std::string &).
  • utilice únicamente funciones de Win32 que aceptan widechars ( LPWSTR). Nunca los que aceptan LPTSTRo LPSTR. Pase los parámetros de esta manera:

    ::SetWindowTextW(Utils::convert(someStdString or "string litteral").c_str())
    

    (La política utiliza las funciones de conversión a continuación).

  • Con cadenas MFC:

    CString someoneElse; // something that arrived from MFC. Converted as soon as possible, before passing any further away from the API call:
    
    std::string s = str(boost::format("Hello %s\n") % Convert(someoneElse));
    AfxMessageBox(MfcUtils::Convert(s), _T("Error"), MB_OK);
    
  • Trabajando con archivos, nombres de archivos y fstream en Windows:

    • Nunca pase std::stringo const char*nombre argumentos a la fstreamfamilia. MSVC STL no admite argumentos UTF-8, pero tiene una extensión no estándar que debe usarse de la siguiente manera:
    • Convierta std::stringargumentos a std::wstringcon Utils::Convert:

      std::ifstream ifs(Utils::Convert("hello"),
                        std::ios_base::in |
                        std::ios_base::binary);
      

      Tendremos que eliminar manualmente la conversión, cuando la actitud de MSVC a los fstreamcambios.

    • Este código no es multiplataforma y puede que tenga que cambiarse manualmente en el futuro
    • Vea el fstreamcaso de investigación / discusión Unicode 4215 para más información.
    • Nunca produzca archivos de salida de texto con contenido que no sea UTF8
    • Evite usar fopen()por razones RAII / OOD. Si es necesario, use las _wfopen()convenciones de WinAPI anteriores.

// For interface to win32 API functions
std::string convert(const std::wstring& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

std::wstring convert(const std::string& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

// Interface to MFC
std::string convert(const CString &mfcString)
{
#ifdef UNICODE
    return Utils::convert(std::wstring(mfcString.GetString()));
#else
    return mfcString.GetString();   // This branch is deprecated.
#endif
}

CString convert(const std::string &s)
{
#ifdef UNICODE
    return CString(Utils::convert(s).c_str());
#else
    Exceptions::Assert(false, "Unicode policy violation. See W569"); // This branch is deprecated as it does not support unicode
    return s.c_str();   
#endif
}

39
No puedo estar de acuerdo Las ventajas de utf16 sobre utf8 para muchos idiomas asiáticos dominan por completo los puntos que destaca. Es ingenuo esperar que los japoneses, tailandeses, chinos, etc. renuncien a esta codificación. Los choques problemáticos entre charsets son cuando los charsets en su mayoría parecen similares, excepto con diferencias. Sugiero estandarizar en: fijo de 7 bits: iso-irv-170; Variable de 8 bits: utf8; Variable de 16 bits: utf16; 32 bits fijo: ucs4.

82
@ Charles: gracias por tu aporte. Es cierto que algunos caracteres BMP son más largos en UTF-8 que en UTF-16. Pero, seamos sinceros: el problema no está en los bytes que toman los caracteres chinos BMP, sino en la complejidad del diseño del software que surge. Si un programador chino tiene que diseñar para caracteres de longitud variable de todos modos, parece que UTF-8 sigue siendo un pequeño precio a pagar en comparación con otras variables en el sistema. Podría usar UTF-16 como algoritmo de compresión si el espacio es tan importante, pero aun así no será rival para LZ, y después de que LZ u otra compresión genérica tengan el mismo tamaño y entropía.

32
Lo que básicamente digo es que la simplificación ofrecida al tener una codificación One que también es compatible con los programas char * existentes, y que también es la más popular hoy en día, es inimaginable. Es casi como en los viejos tiempos de "texto sin formato". ¿Quieres abrir un archivo con un nombre? No es necesario preocuparse por el tipo de Unicode que está haciendo, etc., etc. Sugiero que nosotros, los desarrolladores, confinemos UTF-16 a casos muy especiales de optimización severa en los que un pequeño rendimiento vale la pena por meses de trabajo.

17
Linux ha tenido un requisito específico al elegir usar UTF-8 internamente: compatibilidad con Unix. Windows no necesitaba eso y, por lo tanto, cuando los desarrolladores implementaron Unicode, agregaron versiones UCS-2 de casi todas las funciones que manejan texto e hicieron que los multibyte simplemente se convirtieran a UCS-2 y llamaran a los demás. Luego reemplaza UCS-2 con UTF-16. Linux, por otro lado, mantuvo las codificaciones de 8 bits y, por lo tanto, usó UTF-8, ya que es la opción adecuada en ese caso.
Mircea Chirea

34
@Pavel Radzivilovsky: Por cierto, sus escritos sobre "Creo que todas las demás codificaciones morirán eventualmente. Esto implica que MS-Windows, Java, ICU, Python dejan de usarlo como su favorito". y "En particular, creo que agregar wchar_t a C ++ fue un error, y también lo son las adiciones unicode a C ++ Ox". son bastante ingenuos o muy muy arrogantes. Y esto viene de alguien que codifica en casa con un Linux y que está contento con los caracteres UTF-8. Para decirlo sin rodeos: no va a suceder .
paercebal

157

¡Los puntos de código Unicode no son caracteres! A veces ni siquiera son glifos (formas visuales).

Algunos ejemplos:

  • Puntos de código de números romanos como "ⅲ". (Un solo personaje que se parece a "iii".)
  • Caracteres acentuados como "á", que se pueden representar como un solo carácter combinado "\ u00e1" o como un carácter y un signo diacrítico separado "\ u0061 \ u0301".
  • Caracteres como sigma en minúscula griega, que tienen diferentes formas para el medio ("σ") y el final ("ς") de las posiciones de las palabras, pero que deben considerarse sinónimos para la búsqueda.
  • Guión discrecional Unicode U + 00AD, que puede mostrarse visualmente o no, según el contexto, y que se ignora para la búsqueda semántica.

Las únicas formas de realizar correctamente la edición Unicode es utilizar una biblioteca escrita por un experto , o convertirse en un experto y escribir uno usted mismo. Si solo estás contando puntos de código, estás viviendo en un estado de pecado.


19
Esta. Mucho esto. UTF-16 puede causar problemas, pero incluso el uso de UTF-32 puede causar problemas (y lo hará).
bcat

11
¿Qué es un personaje? Puede definir un punto de código como un personaje y sobrevivir bastante bien. Si te refieres a un glifo visible para el usuario, eso es otra cosa.
tchrist

77
@tchrist seguro de asignar espacio que la definición está bien, pero para cualquier otra cosa? No tanto. Si maneja un carácter de combinación como un único carácter (es decir, para una operación de eliminar o "tomar los primeros N caracteres") obtendrá un comportamiento extraño e incorrecto. Si un punto de código solo tiene significado cuando se combina con al menos otro, no puede manejarlo de manera sensata.
Voo

66
@Pacerier, esto es tarde para la fiesta, pero tengo que comentar sobre eso. Algunos idiomas tienen conjuntos muy grandes de combinaciones potenciales de diacríticos (cf. vietnamita, es decir, mệt đừ). Tener combinaciones en lugar de un carácter por diacrítico es muy útil.
asthasr

21
una pequeña nota sobre la terminología: los puntos de código no corresponden a los caracteres Unicode ; de lo que habla Daniel aquí es de los personajes percibidos por el usuario , que corresponden a grupos de grafemas unicode
Christoph

54

Hay una regla general simple sobre qué formulario de transformación Unicode (UTF) usar: - utf-8 para almacenamiento y comunicación - utf-16 para procesamiento de datos - puede usar utf-32 si la mayor parte de la API de plataforma que usa es utf-32 (común en el mundo UNIX).

La mayoría de los sistemas actuales usan utf-16 (Windows, Mac OS, Java, .NET, ICU, Qt). Consulte también este documento: http://unicode.org/notes/tn12/

Volviendo a "UTF-16 como dañino", diría: definitivamente no.

Las personas que tienen miedo a los sustitutos (pensando que transforman Unicode en una codificación de longitud variable) no entienden las otras complejidades (mucho más grandes) que hacen que el mapeo entre caracteres y un punto de código Unicode sea muy complejo: combinar caracteres, ligaduras, selectores de variación , personajes de control, etc.

Simplemente lea esta serie aquí http://www.siao2.com/2009/06/29/9800913.aspx y vea cómo UTF-16 se convierte en un problema fácil.


26
¡Agregue algunos ejemplos donde UTF-32 es común en el mundo UNIX!
maxschlepzig

48
No, no desea utilizar UTF-16 para el procesamiento de datos. Es un dolor en el culo. Tiene todas las desventajas de UTF-8 pero ninguna de sus ventajas. Tanto UTF-8 como UTF-32 son claramente superiores al hack vicioso anteriormente conocido como la Sra. UTF-16, cuyo apellido de soltera era UCS-2.
tchrist

34
Ayer acabo de encontrar un error en el equalsIgnoreCasemétodo de la clase String core de Java (también otros en la clase string) que nunca hubiera estado allí si Java hubiera usado UTF-8 o UTF-32. Hay millones de estas bombas para dormir en cualquier código que use UTF-16, y estoy harto de ellas. UTF-16 es una viruela viciosa que plaga nuestro software con errores insidiosos para siempre. Es claramente dañino, y debe ser desaprobado y prohibido.
tchrist

77
@tchrist Wow, por lo que una función consciente no sustituta (porque se escribió cuando no había ninguna y está tristemente documentada de tal manera que probablemente sea imposible adaptarla: especifica .toUpperCase (char)) dará como resultado un comportamiento incorrecto. ¿Sabe que una función UTF-32 con un mapa de puntos de código obsoleto no manejaría esto mejor? Además, toda la API de Java maneja los sustitutos no especialmente bien y los puntos más complejos sobre Unicode no lo son en absoluto, y con el posterior la codificación utilizada no importaría en absoluto.
Voo

8
-1: Un incondicional .Substring(1)en .NET es un ejemplo trivial de algo que rompe el soporte para todos los Unicode que no son BMP. Todo lo que usa UTF-16 tiene este problema; es demasiado fácil tratarlo como una codificación de ancho fijo y rara vez ve problemas. Eso lo convierte en una codificación activamente dañina si desea admitir Unicode.
Roman Starkov

43

Si, absolutamente.

¿Por qué? Tiene que ver con el ejercicio del código .

Si observa estas estadísticas de uso de puntos de código en un corpus grande de Tom Christiansen, verá que los puntos de código BMP trans-8bit se usan en varias órdenes si su magnitud supera los puntos de código que no son BMP:

 2663710 U+002013 ‹–›  GC=Pd    EN DASH
 1065594 U+0000A0 ‹ ›  GC=Zs    NO-BREAK SPACE
 1009762 U+0000B1 ‹±›  GC=Sm    PLUS-MINUS SIGN
  784139 U+002212 ‹−›  GC=Sm    MINUS SIGN
  602377 U+002003 ‹ ›  GC=Zs    EM SPACE

 544 U+01D49E ‹𝒞›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL C
 450 U+01D4AF ‹𝒯›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL T
 385 U+01D4AE ‹𝒮›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL S
 292 U+01D49F ‹𝒟›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL D
 285 U+01D4B3 ‹𝒳›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL X

Tome la frase TDD: "El código no probado es código roto", y reformúlelo como "el código no ejercitado es código roto", y piense con qué frecuencia los programadores tienen que lidiar con puntos de código que no son BMP.

Los errores relacionados con no tratar con UTF-16 como una codificación de ancho variable son mucho más propensos a pasar desapercibidos que los errores equivalentes en UTF-8 . Algunos lenguajes de programación aún no garantizan darle UTF-16 en lugar de UCS-2, y algunos de los llamados lenguajes de programación de alto nivel ofrecen acceso a unidades de código en lugar de puntos de código (incluso se supone que C le da acceso a puntos de código si los usa wchar_t, independientemente de lo que puedan hacer algunas plataformas).


16
"Los errores relacionados con no tratar con UTF-16 como una codificación de ancho variable tienen muchas más probabilidades de pasar desapercibidos que los errores equivalentes en UTF-8". Este es el núcleo del problema y, por lo tanto, la respuesta correcta.
Sean McMillan

3
Precisamente. Si su manejo UTF-8 está descifrado, será inmediatamente obvio. Si su manejo de UTF-8 está alterado, solo notará si coloca caracteres Han o símbolos matemáticos poco comunes.
Caracol mecánico el

1
Muy cierto, pero por otro lado, ¿para qué son las pruebas unitarias si debe depender de la suerte para encontrar errores en casos menos frecuentes?
musiphil

@musiphil: entonces, ¿cuándo fue la última vez que creó una prueba unitaria para caracteres que no son BMP?
ninjalj

1
Para explicar mi afirmación anterior: incluso con UTF-8, no puede estar seguro de que ha cubierto todos los casos después de ver solo algunos ejemplos de trabajo. Lo mismo con UTF-16: debe probar si su código funciona tanto con no sustitutos como con sustitutos. (Alguien podría incluso argumentar que UTF-8 tiene al menos cuatro casos principales, mientras que UTF-16 tiene solo dos.)
musiphil

40

Sugeriría que pensar que UTF-16 podría considerarse dañino dice que necesita obtener una mayor comprensión de Unicode .

Como me han rechazado por presentar mi opinión sobre una pregunta subjetiva, permítanme explicarlo. ¿Qué es exactamente lo que te molesta de UTF-16? ¿Preferiría que todo estuviera codificado en UTF-8? UTF-7? ¿O qué tal UCS-4? Por supuesto, ciertas aplicaciones no están diseñadas para manejar códigos de caracteres únicos, pero son necesarias, especialmente en el dominio de información global de hoy en día, para la comunicación entre fronteras internacionales.

Pero realmente, si cree que UTF-16 debe considerarse dañino porque es confuso o puede implementarse de manera incorrecta (unicode ciertamente puede serlo), entonces, ¿qué método de codificación de caracteres se consideraría no dañino?

EDITAR: Para aclarar: ¿Por qué considerar las implementaciones inadecuadas de un estándar como un reflejo de la calidad del estándar en sí? Como otros han señalado posteriormente, el simple hecho de que una aplicación utilice una herramienta de manera inapropiada no significa que la herramienta en sí sea defectuosa. Si ese fuera el caso, probablemente podríamos decir cosas como "palabra clave var considerada dañina" o "threading considerado dañino". Creo que la pregunta confunde la calidad y la naturaleza del estándar con las dificultades que muchos programadores tienen para implementarlo y usarlo adecuadamente, lo que creo que se debe más a su falta de comprensión de cómo funciona Unicode, en lugar de a Unicode en sí.


33
-1: ¿Qué hay de abordar algunas de las objeciones de Artyom, en lugar de solo patrocinarlo?

8
Por cierto: cuando comencé a escribir este artículo, casi quería escribir "¿Joel en el artículo de Unicode de Softeare debería considerarse dañino" porque hay muchos errores. Por ejemplo: la codificación utf-8 toma hasta 4 caracteres y no 6. Además, no distingue entre UCS-2 y UTF-16 que son realmente diferentes y realmente causan los problemas de los que hablo.

32
Además, debe tenerse en cuenta que cuando Joel escribió ese artículo, el estándar UTF-8 ERA 6 bytes, no 4. RFC 3629 cambió el estándar a 4 bytes varios meses DESPUÉS de que escribió el artículo. Como casi cualquier cosa en Internet, vale la pena leer de más de una fuente y estar al tanto de la antigüedad de sus fuentes. El enlace no pretendía ser el "fin de que todo sea todo", sino más bien un punto de partida.

77
Me gustaría pic: utf-8 o utf-32 que son: codificación de longitud variable en casi todos los casos (incluido BMP) o codificación de longitud fija siempre.

18
@iconiK: No seas tonto. UTF-16 no es absolutamente el estándar de facto para procesar texto. Muéstrame un lenguaje de programación más adecuado para el procesamiento de texto que Perl, que siempre (bueno, durante más de una década) siempre ha usado caracteres abstractos con una representación interna UTF-8 subyacente. Debido a esto, todos los programas de Perl manejan automáticamente todos los Unicode sin que el usuario tenga que andar constantemente con sustitutos idiotas. La longitud de una cadena es su recuento en puntos de código, no unidades de código. Cualquier otra cosa es pura estupidez, lo que pone la compatibilidad con versiones anteriores.
tchrist

37

No hay nada malo con la codificación Utf-16. Pero los lenguajes que tratan las unidades de 16 bits como caracteres probablemente deberían considerarse mal diseñados. Tener un tipo llamado ' char' que no siempre representa un personaje es bastante confuso. Dado que la mayoría de los desarrolladores esperarán que un tipo char represente un punto o carácter de código, es probable que gran parte del código se rompa cuando se exponga a caracteres entre BMP.

Sin embargo, tenga en cuenta que incluso el uso de utf-32 no significa que cada punto de código de 32 bits siempre represente un carácter. Debido a la combinación de caracteres, un carácter real puede consistir en varios puntos de código. Unicode nunca es trivial.

Por cierto. Probablemente existe la misma clase de errores con plataformas y aplicaciones que esperan que los caracteres sean de 8 bits, que se alimentan con Utf-8.


12
En el caso de Java, si observa su línea de tiempo ( java.com/en/javahistory/timeline.jsp ), verá que el desarrollo principal de String ocurrió mientras Unicode tenía 16 bits (cambió en 1996). Tuvieron que aprovechar la capacidad de manejar puntos de código no BMP, de ahí la confusión.
Kathy Van Stone

10
@Kathy: Sin embargo, no es realmente una excusa para C #. En general, estoy de acuerdo en que debe haber un CodePointtipo, que contenga un único punto de código (21 bits), un CodeUnittipo, que contenga una única unidad de código (16 bits para UTF-16) y un Charactertipo idealmente debería soportar un grafema completo. Pero eso lo hace funcionalmente equivalente a un String...
Joey

1
Esta respuesta tiene casi dos años, pero no puedo evitar comentarla. "Tener un tipo llamado 'char' que no siempre representa un personaje es bastante confuso". Y, sin embargo, las personas lo usan todo el tiempo en C y similares para representar datos enteros que se pueden almacenar en un solo byte.
JAB

Y he visto mucho código C que no maneja la codificación de caracteres correctamente.
dan04

1
C # tiene una excusa diferente: fue diseñado para Windows y Windows fue construido en UCS-2 (es muy molesto que incluso hoy las API de Windows no puedan soportar UTF-8). Además, creo que Microsoft quería compatibilidad Java (.NET 1.0 tenía una biblioteca de compatibilidad de Java, pero abandonado el soporte de Java muy rápidamente - Estoy adivinando esto es debido a la demanda de sol contra la EM?)
Qwertie

20

Mi elección personal es usar siempre UTF-8. Es el estándar en Linux para casi todo. Es compatible con muchas aplicaciones heredadas. Hay una sobrecarga mínima en términos de espacio adicional utilizado para caracteres no latinos frente a los otros formatos UTF, y hay un ahorro significativo en espacio para caracteres latinos. En la web, los idiomas latinos reinan, y creo que lo harán en el futuro previsible. Y para abordar uno de los principales argumentos en la publicación original: casi todos los programadores son conscientes de que UTF-8 a veces tendrá caracteres de varios bytes. No todos lidian con esto correctamente, pero generalmente son conscientes, lo cual es más de lo que se puede decir de UTF-16. Pero, por supuesto, debe elegir el más apropiado para su aplicación. Es por eso que hay más de uno en primer lugar.


3
UTF-16 es más simple para cualquier cosa dentro de BMP, es por eso que se usa tan ampliamente. Pero también soy un fanático de UTF-8, que tampoco tiene problemas con el orden de los bytes, lo que funciona a su favor.
Malcolm

2
Teóricamente sí. En la práctica, hay cosas como, por ejemplo, UTF-16BE, que significa UTF-16 en big endian sin BOM. Esto no es algo que inventé, esta es una codificación real permitida en las etiquetas ID3v2.4 (las etiquetas ID3v2 apestan, pero, desafortunadamente, son ampliamente utilizadas). Y en tales casos, debe definir endianness externamente, porque el texto en sí no contiene BOM. UTF-8 siempre se escribe de una manera y no tiene ese problema.
Malcolm

23
No, UTF-16 no es más simple. Es mas dificil. Te engaña y te engaña haciéndote creer que es de ancho fijo. Todo ese código está roto y más aún porque no lo notas hasta que es demasiado tarde. CASO EN PUNTO: Ayer encontré otro estúpido error UTF-16 en las bibliotecas principales de Java, esta vez en String.equalsIgnoreCase, que quedó en el buggery UCS-2, y falla en 16/17 puntos de código Unicode válidos. ¿Cuánto tiempo lleva ese código? No hay excusa para que tenga errores. UTF-16 conduce a la pura estupidez y un accidente a la espera de suceder. Corre gritando desde UTF-16.
tchrist

3
@tchrist One debe ser un desarrollador muy ignorante para no saber que UTF-16 no tiene una longitud fija. Si comienza con Wikipedia, leerá lo siguiente en la parte superior: "Produce un resultado de longitud variable de una o dos unidades de código de 16 bits por punto de código". Las preguntas frecuentes de Unicode dicen lo mismo: unicode.org/faq//utf_bom.html#utf16-1 . No sé, cómo UTF-16 puede engañar a alguien si está escrito en todas partes que es de longitud variable. En cuanto al método, nunca fue diseñado para UTF-16 y no debe considerarse Unicode, tan simple como eso.
Malcolm

2
@tchrist ¿Tiene una fuente para sus estadísticas? Aunque si los buenos programadores son escasos, creo que esto es bueno, porque nos hacemos más valiosos. :) En cuanto a las API de Java, las partes basadas en caracteres pueden quedar obsoletas, pero esto no garantiza que no se utilizarán. Y definitivamente no se eliminarán por razones de compatibilidad.
Malcolm

18

Bueno, hay una codificación que usa símbolos de tamaño fijo. Ciertamente me refiero a UTF-32. Pero 4 bytes para cada símbolo es demasiado espacio desperdiciado, ¿por qué lo usaríamos en situaciones cotidianas?

En mi opinión, la mayoría de los problemas aparecen por el hecho de que algunos softwares se quedaron atrás del estándar Unicode, pero no corrieron rápidamente la situación. Opera, Windows, Python, Qt: todos aparecieron antes de que UTF-16 fuera ampliamente conocido o incluso surgiera. Sin embargo, puedo confirmar que en Opera, Windows Explorer y Notepad ya no hay problemas con los caracteres fuera de BMP (al menos en mi PC). Pero de todos modos, si los programas no reconocen pares sustitutos, entonces no usan UTF-16. Cualesquiera que sean los problemas que surjan al tratar con tales programas, no tienen nada que ver con el UTF-16.

Sin embargo, creo que los problemas del software heredado con solo soporte BMP son algo exagerados. Los caracteres fuera de BMP se encuentran solo en casos y áreas muy específicos. Según las preguntas frecuentes oficiales de Unicode , "incluso en el texto de Asia oriental, la incidencia de pares sustitutos debería ser menos del 1% de todo el almacenamiento de texto en promedio". Por supuesto, los caracteres fuera de BMP no deben descuidarse porque un programa no es compatible con Unicode de lo contrario, pero la mayoría de los programas no están destinados a trabajar con textos que contienen dichos caracteres. Es por eso que si no lo apoyan, es desagradable, pero no una catástrofe.

Ahora consideremos la alternativa. Si UTF-16 no existiera, entonces no tendríamos una codificación adecuada para texto que no sea ASCII, y todo el software creado para UCS-2 tendría que ser completamente rediseñado para seguir siendo compatible con Unicode. Lo último probablemente solo retrasaría la adopción de Unicode. Tampoco hubiéramos podido mantener la compatibilidad con el texto en UCS-2 como lo hace UTF-8 en relación con ASCII.

Ahora, dejando de lado todos los problemas heredados, ¿cuáles son los argumentos en contra de la codificación en sí? Realmente dudo que los desarrolladores de hoy en día no sepan que UTF-16 es de longitud variable, está escrito en todas partes con Wikipedia. UTF-16 es mucho menos difícil de analizar que UTF-8, si alguien señala la complejidad como un posible problema. También es un error pensar que es fácil equivocarse al determinar la longitud de la cadena solo en UTF-16. Si usa UTF-8 o UTF-32, debe tener en cuenta que un punto de código Unicode no significa necesariamente un carácter. Aparte de eso, no creo que haya nada sustancial en contra de la codificación.

Por lo tanto, no creo que la codificación en sí misma deba considerarse dañina. UTF-16 es un compromiso entre simplicidad y compacidad, y no hay daño en usar lo que se necesita donde se necesita . En algunos casos, debe seguir siendo compatible con ASCII y necesita UTF-8, en algunos casos desea trabajar con ideogramas Han y ahorrar espacio con UTF-16, en algunos casos necesita representaciones universales de caracteres con un signo fijo codificación de longitud. Use lo que sea más apropiado, solo hágalo correctamente.


21
Esa es una visión anglocéntrica bastante maldecida, Malcolm. Casi a la par con "ASCII es lo suficientemente bueno para EE. UU., El resto del mundo debería encajar con nosotros".
Jonathan Leffler

28
En realidad soy de Rusia y encuentro cirilos todo el tiempo (incluidos mis propios programas), así que no creo que tenga una visión anglocéntrica. :) Mencionar ASCII no es del todo apropiado, porque no es Unicode y no admite caracteres específicos. UTF-8, UTF-16, UTF-32 son compatibles con los mismos conjuntos de caracteres internacionales, solo están destinados para su uso en sus áreas específicas. Y este es exactamente mi punto: si usa principalmente inglés, use UTF-8, si usa principalmente cirilos, use UTF-16, si usa idiomas antiguos, use UTF-32. Bastante sencillo.
Malcolm

16
"No es cierto, los scripts asiáticos como el japonés, el chino o el árabe también pertenecen a BMP. BMP en sí mismo es en realidad muy grande y ciertamente lo suficientemente grande como para incluir todos los scripts utilizados en la actualidad" Esto está muy mal. BMP contiene 0xFFFF caracteres (65536). Solo el chino tiene más que eso. Los estándares chinos (GB 18030) tienen más que eso. Unicode 5.1 ya asignó más de 100,000 caracteres.

12
@Marcolm: "BMP en sí mismo es realmente muy grande y ciertamente lo suficientemente grande como para incluir todos los scripts utilizados hoy en día" No es cierto. En este punto, Unicode ya asignó unos 100K caracteres, mucho más de lo que BMP puede acomodar. Hay grandes fragmentos de caracteres chinos fuera de BMP. Y algunos de ellos son requeridos por GB-18030 (estándar chino obligatorio). Otros son requeridos por los estándares japoneses y coreanos (no obligatorios). Entonces, si intenta vender algo en esos mercados, necesita más allá del soporte de BMP.

8
Cualquier cosa que use UTF-16 pero solo pueda manejar caracteres BMP estrechos no está usando UTF-16. Está defectuoso y roto. La premisa del OP es sólida: UTF-16 es dañino, porque lleva a personas ingenuas a escribir código roto. Puede manejar texto Unicode o no puede. Si no puede, está eligiendo un subconjunto, que es tan estúpido como el procesamiento de texto solo ASCII.
tchrist

16

Los años de trabajo de internacionalización de Windows, especialmente en los idiomas de Asia oriental, podrían haberme corrompido, pero me inclino hacia UTF-16 para las representaciones de cadenas internas al programa, y ​​UTF-8 para el almacenamiento en red o archivos de documentos de texto sin formato. Sin embargo, UTF-16 generalmente se puede procesar más rápido en Windows, por lo que ese es el beneficio principal de usar UTF-16 en Windows.

Dar el salto al UTF-16 mejoró dramáticamente la adecuación de los productos promedio que manejan textos internacionales. Solo hay unos pocos casos estrechos en los que se deben considerar los pares sustitutos (eliminaciones, inserciones y saltos de línea, básicamente) y el caso promedio es en su mayoría paso directo. Y a diferencia de las codificaciones anteriores como las variantes JIS, UTF-16 limita los pares sustitutos a un rango muy estrecho, por lo que la verificación es realmente rápida y funciona hacia adelante y hacia atrás.

De acuerdo, también es aproximadamente tan rápido en UTF-8 codificado correctamente. Pero también hay muchas aplicaciones UTF-8 rotas que codifican incorrectamente pares sustitutos como dos secuencias UTF-8. Entonces UTF-8 tampoco garantiza la salvación.

IE maneja pares sustitutos razonablemente bien desde el año 2000 más o menos, a pesar de que normalmente los convierte de páginas UTF-8 a una representación interna UTF-16; Estoy bastante seguro de que Firefox también lo hizo bien, así que no me importa lo que haga Opera.

UTF-32 (también conocido como UCS4) no tiene sentido para la mayoría de las aplicaciones, ya que requiere mucho espacio, por lo que prácticamente no es un iniciador.


66
No recibí tu comentario sobre UTF-8 y pares sustitutos. Los pares sustitutos son solo un concepto que es significativo en la codificación UTF-16, ¿verdad? Quizás el código que se convierte directamente de la codificación UTF-16 a la codificación UTF-8 podría ser incorrecto, y en ese caso, el problema es leer incorrectamente el UTF-16, no escribir el UTF-8. ¿Está bien?
Craig McQueen

11
De lo que Jason habla es de un software que implementa deliberadamente UTF-8 de esa manera: crea un par sustituto, luego UTF-8 codifica cada mitad por separado. El nombre correcto para esa codificación es CESU-8, pero Oracle (por ejemplo) lo tergiversa como UTF-8. Java emplea un esquema similar para la serialización de objetos, pero está claramente documentado como "UTF-8 modificado" y solo para uso interno. (Ahora, si pudiéramos hacer que las personas LEAN esa documentación y dejen de usar DataInputStream # readUTF () y DataOutputStream # writeUTF () de manera inapropiada ...)

AFAIK, UTF-32 sigue siendo una codificación de longitud variable, y no es igual a UCS4, que es un rango específico de punto de código.
Eonil

@Eonil, UTF-32 solo se podrá distinguir de UCS4 si tenemos un estándar Unicode que presenta algo como un UCS5 o más grande.
JasonTrue

@JasonTrue Aún así, solo los resultados son iguales de forma coincidente, no garantizados por el diseño. Lo mismo sucedió en el direccionamiento de memoria de 32 bits, Y2K, UTF16 / UCS2. ¿O tenemos alguna garantía de esa igualdad? Si es así, con mucho gusto lo usaría. Pero no quiero escribir un posible código rompible . Estoy escribiendo un código de nivel de caracteres, y la falta de una forma garantizada de transcodificar entre el punto de código UTF <-> me está molestando mucho.
Eonil

16

UTF-8 es definitivamente el camino a seguir, posiblemente acompañado por UTF-32 para uso interno en algoritmos que necesitan acceso aleatorio de alto rendimiento (pero que ignora la combinación de caracteres).

Tanto UTF-16 como UTF-32 (así como sus variantes LE / BE) sufren problemas de resistencia, por lo que nunca deben usarse externamente.


99
El acceso aleatorio en tiempo constante también es posible con UTF-8, solo use unidades de código en lugar de puntos de código. Tal vez necesite un acceso de punto de código aleatorio real, pero nunca he visto un caso de uso, y es probable que desee un acceso aleatorio de clúster de grafema en su lugar.

15

UTF-16? Definitivamente perjudicial. Solo mi grano de sal aquí, pero hay exactamente tres codificaciones aceptables para texto en un programa:

  • ASCII: cuando se trata de cosas de bajo nivel (por ejemplo, microcontroladores) que no pueden permitirse nada mejor
  • UTF8: almacenamiento en medios de ancho fijo como archivos
  • puntos de código enteros ("CP"?): una matriz de los enteros más grandes que son convenientes para su lenguaje de programación y plataforma (decae a ASCII en el límite de bajos recursos). Debe ser int32 en computadoras más antiguas e int64 en cualquier cosa con direccionamiento de 64 bits.

  • Obviamente, las interfaces con el código heredado utilizan la codificación necesaria para que el código anterior funcione correctamente.


44
@simon buchan, el U+10ffffmáximo saldrá por la ventana cuando (no si) se queden sin puntos de código. Dicho esto, usar int32 en un sistema p64 para la velocidad es probablemente seguro, ya que dudo que excedan U+ffffffffantes de que te veas obligado a reescribir tu código para sistemas de 128 bits alrededor de 2050. (Ese es el punto de "usar el int más grande que es conveniente "en lugar de" el más grande disponible "(que probablemente sería int256 o bignums o algo así)."
David X

1
@David: Unicode 5.2 codifica 107.361 puntos de código. Hay 867,169 puntos de código no utilizados. "cuando" es una tontería. Un punto de código Unicode se define como un número de 0 a 0x10FFFF, una propiedad de la que depende UTF-16. (También parece que 2050 baja una estimación para sistemas de 128 bits cuando un sistema de 64 bits puede contener la totalidad de Internet en su espacio de direcciones)

3
@David: Su "cuándo" se refería a quedarse sin puntos de código Unicode, no un conmutador de 128 bits que, sí, será en los próximos siglos. A diferencia de la memoria, no hay un crecimiento exponencial de caracteres, por lo que el Consorcio Unicode ha garantizado específicamente que nunca asignarán un punto de código arriba U+10FFFF. Esta es realmente una de esas situaciones en las que 21 bits son suficientes para cualquiera.

10
@Simon Buchan: Al menos hasta el primer contacto. :)

3
Unicode solía garantizar que no habría puntos de código por encima de U + FFFF también.
Shannon Severance

13

Unicode define puntos de código de hasta 0x10FFFF (1,114,112 códigos), todas las aplicaciones que se ejecutan en entornos multilingües que tratan con cadenas / nombres de archivos, etc., deben manejarlo correctamente.

Utf-16 : cubre solo 1,112,064 códigos. Aunque los que están al final de Unicode son de los planos 15-16 (Área de uso privado). No puede crecer más en el futuro, excepto romper el concepto Utf-16 .

Utf-8 : cubre teóricamente 2,216,757,376 códigos. El rango actual de códigos Unicode se puede representar mediante una secuencia máxima de 4 bytes. No sufre con el problema de orden de bytes , es "compatible" con ASCII.

Utf-32 : cubre teóricamente 2 ^ 32 = 4,294,967,296 códigos. Actualmente no está codificado en longitud variable y probablemente no lo estará en el futuro.

Esos hechos se explican por sí mismos. No entiendo abogar por el uso general de Utf-16 . Está codificado en longitud variable (no se puede acceder por índice), tiene problemas para cubrir todo el rango Unicode incluso en la actualidad, se debe manejar el orden de bytes, etc. No veo ninguna ventaja, excepto que se usa de forma nativa en Windows y algunos otros lugares. Aunque al escribir código multiplataforma probablemente sea mejor usar Utf-8 de forma nativa y hacer conversiones solo en los puntos finales de forma dependiente de la plataforma (como ya se sugirió). Cuando es necesario el acceso directo por índice y la memoria no es un problema, se debe usar Utf-32 .

El principal problema es que muchos programadores que trabajan con Windows Unicode = Utf-16 ni siquiera saben o ignoran el hecho de que está codificado en longitud variable.

La forma en que suele estar en la plataforma * nix es bastante buena, las cadenas c (char *) interpretadas como codificadas en Utf-8 , las cadenas c anchas (wchar_t *) interpretadas como Utf-32 .


77
Nota: UTF-16 cubre todo Unicode ya que el consorcio Unicode decidió que 10FFFF es el rango SUPERIOR de Unicode y definió UTF-8 con una longitud máxima de 4 bytes y excluyó explícitamente el rango 0xD800-0xDFFF del rango de puntos de código válido y este rango se usa para la creación de parejas sustitutas. Por lo tanto, cualquier texto Unicode válido se puede representar con cada una de estas codificaciones. También sobre crecer hacia el futuro. No parece que 1 millón de puntos de código no sea suficiente en un futuro lejano.

77
@Kerrek: Incorrecto: UCS-2 no es una codificación Unicode válida. Todas las codificaciones UTF- * por definición pueden representar cualquier punto de código Unicode que sea legal para el intercambio. UCS-2 puede representar mucho menos que eso, además de algunos más. Repita: UCS-2 no es una codificación Unicode válida, más que ASCII.
tchrist

1
"No entiendo abogar por el uso general de Utf-8 . Está codificado en longitud variable (no se puede acceder por índice)"
Ian Boyd

99
@Ian Boyd, la necesidad de acceder al carácter individual de una cadena en un patrón de acceso aleatorio es increíblemente exagerada. Es casi tan común como querer calcular la diagonal de una matriz de caracteres, lo cual es muy raro. Las cadenas casi siempre se procesan secuencialmente, y dado que acceder a UTF-8 char N + 1 dado que está en UTF-8 char N es O (1), no hay problema. Existe una enorme necesidad de hacer un acceso aleatorio a las cadenas. Si crees que vale la pena el espacio de almacenamiento para ir a UTF-32 en lugar de UTF-8 es tu propia opinión, pero para mí, no es un problema.
tchrist

2
@tchrist, le concederé que las cadenas se procesan prácticamente siempre de forma secuencial si incluye la iteración inversa como "secuencial" y estira esa comparación un poco más del final de una cadena a una cadena conocida. Dos escenarios muy comunes son truncar espacios en blanco desde el final de las cadenas y verificar la extensión del archivo al final de una ruta.
Andy Dent

11

Agregue esto a la lista:

El escenario presentado es simple (¡incluso más simple ya que lo presentaré aquí de lo que era originalmente!): 1. Un WinForms TextBox se encuentra en un formulario, vacío. Tiene un MaxLength establecido en 20 .

2.El usuario escribe en el cuadro de texto, o tal vez pega texto en él.

3. No importa lo que escriba o pegue en el TextBox, está limitado a 20, aunque simpatizará con un pitido en el texto más allá de 20 (YMMV aquí; ¡cambié mi esquema de sonido para darme ese efecto!).

4.El pequeño paquete de texto se envía a otro lugar, para comenzar una aventura emocionante.

Ahora este es un escenario fácil, y cualquiera puede escribir esto, en su tiempo libre. Lo escribí yo mismo en múltiples lenguajes de programación usando WinForms, porque estaba aburrido y nunca lo había probado antes. Y con texto en varios idiomas reales porque estoy conectado de esa manera y tengo más diseños de teclado que posiblemente cualquier persona en todo el universo.

Incluso llamé a la forma Magic Carpet Ride , para ayudar a mejorar el aburrimiento.

Esto no funcionó, por lo que vale.

Entonces, en su lugar, ingresé los siguientes 20 caracteres en mi formulario Magic Carpet Ride :

0123401234012340123 𠀀

UH oh.

Ese último personaje es U + 20000, el primer ideógrafo de Extensión B de Unicode (también conocido como U + d840 U + dc00, para sus amigos cercanos a los que no le da vergüenza ser desnudo, por así decirlo) ...

ingrese la descripción de la imagen aquí

Y ahora tenemos un juego de pelota.

Porque cuando TextBox.MaxLength habla sobre

Obtiene o establece el número máximo de caracteres que se pueden ingresar manualmente en el cuadro de texto.

lo que realmente significa es

Obtiene o establece el número máximo de unidades de código LE UTF-16 que se pueden ingresar manualmente en el cuadro de texto y truncará sin piedad la basura de cualquier cadena que intente jugar juegos cursis con la noción de carácter lingüístico de que solo alguien tan obsesionado como ese tipo de Kaplan lo encontrará ofensivo (¡caramba, necesita salir más!).

Trataré de ver si el documento se actualiza ...
Los lectores habituales que recuerden mi serie UCS-2 a UTF-16 notarán mi descontento con la noción simplista de TextBox.MaxLength y cómo debería manejar al menos este caso donde su comportamiento draconiano crea una secuencia ilegal, una que otras partes de .Net Framework pueden arrojar un

  • System.Text.EncoderFallbackException: no se puede traducir el carácter Unicode \ uD850 en el índice 0 a la página de códigos especificada. *

excepción si pasa esta cadena a otra parte en .Net Framework (como estaba haciendo mi colega Dan Thompson).

Ahora bien, tal vez la serie completa de UCS-2 a UTF-16 está fuera del alcance de muchos.
Pero no es razonable esperar que TextBox.Text no produzca un System.Stringque no hará que se lance otra pieza de .Net Framework? Quiero decir, no es que haya una posibilidad en la forma de algún evento en el control que le informe sobre el próximo truncamiento en el que puede agregar fácilmente la validación más inteligente, validación que al control en sí no le importa hacer. Llegaría al extremo de decir que este control punk está rompiendo un contrato de seguridad que incluso podría generar problemas de seguridad si se puede clasificar causando excepciones inesperadas para terminar una aplicación como una especie de denegación de servicio. ¿Por qué cualquier proceso, método, algoritmo o técnica de WinForms produce resultados no válidos?

Fuente: Michael S. Kaplan MSDN Blog


Gracias, muy buen enlace! Lo he agregado a la lista de problemas en la pregunta.

9

No diría necesariamente que UTF-16 es dañino. No es elegante, pero cumple su función de compatibilidad con UCS-2, al igual que GB18030 con GB2312 y UTF-8 con ASCII.

Pero hacer un cambio fundamental en la estructura de Unicode a mitad de camino, después de que Microsoft y Sun hubieran construido API enormes alrededor de caracteres de 16 bits, fue perjudicial. El hecho de no difundir el cambio fue más dañino.


8
UTF-8 es un superconjunto de ASCII, pero UTF-16 NO es un superconjunto de UCS-2. Aunque es casi un superconjunto, una codificación correcta de UCS-2 en UTF-8 da como resultado la abominación conocida como CESU-8; UCS-2 no tiene sustitutos, solo puntos de código ordinarios, por lo que deben traducirse como tales. La verdadera ventaja de UTF-16 es que es más fácil actualizar una base de código UCS-2 que una reescritura completa para UTF-8. Gracioso, ¿eh?

1
Claro, técnicamente UTF-16 no es un superconjunto de UCS-2, pero ¿cuándo se usaron U + D800 a U + DFFF para algo excepto los sustitutos de UTF-16?
dan04

2
No importa Cualquier procesamiento que no sea pasar ciegamente por bytestream requiere que decodifique los pares sustitutos, lo que no puede hacer si lo trata como UCS-2.

6

UTF-16 es el mejor compromiso entre manejo y espacio y es por eso que la mayoría de las plataformas principales (Win32, Java, .NET) lo usan para la representación interna de cadenas.


31
-1 porque UTF-8 es probable que sea más pequeño o no sea significativamente diferente. Para ciertas secuencias de comandos asiáticas, UTF-8 tiene tres bytes por glifo, mientras que UTF-16 es solo dos, pero esto se equilibra con UTF-8 que es solo un byte para ASCII (que a menudo aparece incluso dentro de idiomas asiáticos en nombres de productos, comandos y demás). cosas). Además, en dichos idiomas, un glifo transmite más información que un carácter latino, por lo que se justifica que ocupe más espacio.

32
No llamaría la combinación de los peores lados de ambas opciones un buen compromiso.

18
No es más fácil que UTF-8. También es de longitud variable.
luiscubal

36
Dejando a un lado los debates sobre los beneficios de UTF-16: lo que citó no es la razón por la que Windows, Java o .NET usan UTF-16. Windows y Java se remontan a una época en la que Unicode era una codificación de 16 bits. UCS-2 era una opción razonable en aquel entonces. Cuando Unicode se convirtió en una codificación de 21 bits, la migración a UTF-16 era la mejor opción que tenían las plataformas existentes. Eso no tiene nada que ver con la facilidad de manejo o los compromisos de espacio. Es solo una cuestión de legado.
Joey

10
.NET hereda el legado de Windows aquí.
Joey

6

Nunca he entendido el punto de UTF-16. Si desea la representación más eficiente en espacio, use UTF-8. Si desea poder tratar el texto como de longitud fija, use UTF-32. Si no quieres ninguno, usa UTF-16. Peor aún, dado que todos los caracteres comunes (plano multilingüe básico) en UTF-16 caben en un único punto de código, los errores que suponen que UTF-16 es de longitud fija serán sutiles y difíciles de encontrar, mientras que si intenta hacerlo esto con UTF-8, su código fallará rápida y ruidosamente tan pronto como intente internacionalizarse.


6

Como todavía no puedo comentar, publico esto como respuesta, ya que parece que no puedo contactar a los autores de utf8everywhere.org. Es una pena que no obtenga automáticamente el privilegio de comentario, ya que tengo suficiente reputación en otros intercambios de pila.

Esto se entiende como un comentario a la Opinión: Sí, UTF-16 debe considerarse una respuesta dañina .

Una pequeña corrección:

Para evitar que uno pase accidentalmente un UTF-8 char*a versiones ANSI-string de funciones de API de Windows, uno debe definir UNICODE, no _UNICODE. _UNICODEfunciones como mapas _tcslena wcslen, no MessageBoxa MessageBoxW. En cambio, la UNICODEdefinición se encarga de lo último. Como prueba, esto es del WinUser.hencabezado de MS Visual Studio 2005 :

#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

Como mínimo, este error debe corregirse en utf8everywhere.org.

Una sugerencia:

Quizás la guía debería contener un ejemplo de uso explícito de la versión de cadena ancha de una estructura de datos, para que sea menos fácil perderla / olvidarla. El uso de versiones de cadenas anchas de estructuras de datos además del uso de versiones de funciones de cadenas anchas hace que sea aún menos probable que se llame accidentalmente una versión de cadenas ANSI de dicha función.

Ejemplo del ejemplo:

WIN32_FIND_DATAW data; // Note the W at the end.
HANDLE hSearch = FindFirstFileW(widen("*.txt").c_str(), &data);
if (hSearch != INVALID_HANDLE_VALUE)
{
    FindClose(hSearch);
    MessageBoxW(nullptr, data.cFileName, nullptr, MB_OK);
}

Convenido; ¡Gracias! Actualizaremos el documento. El documento aún necesita más desarrollo y agregar información sobre bases de datos. Estamos felices de recibir contribuciones de redacción.
Pavel Radzivilovsky

@PavelRadzivilovsky _UNICODEtodavía está allí :(
cubuspl42

gracias por recordar. cubus, Jelle, ¿Quieres un usuario para nuestro SVN?
Pavel Radzivilovsky

@Pavel Claro, lo agradecería!
Jelle Geerts

@JelleGeerts: Pido disculpas por este retraso. Siempre puede contactarnos por nuestros correos electrónicos (vinculados desde el manifiesto) o Facebook. Somos faciles de encontrar. Aunque creo que solucionamos el problema que trajiste aquí (y te acredité allí), todos los debates UTF-8 vs UTF-16 siguen siendo relevantes. Si tiene más que aportar, no dude en contactarnos a través de esos canales privados.
ybungalobill

5

Alguien dijo que UCS4 y UTF-32 eran iguales. No, pero sé a qué te refieres. Sin embargo, uno de ellos es una codificación del otro. Desearía que hubieran pensado en especificar endianness desde el principio para que no tuviéramos la batalla de endianess aquí también. ¿No podrían haberlo visto venir? Al menos UTF-8 es igual en todas partes (a menos que alguien siga la especificación original con 6 bytes).

Si utiliza UTF-16 que tiene incluir el manejo de caracteres de varios bytes. No puede ir al enésimo carácter indexando 2N en una matriz de bytes. Tienes que caminar o tener índices de personajes. De lo contrario, ha escrito un error.

El borrador actual de la especificación de C ++ dice que UTF-32 y UTF-16 pueden tener variantes little-endian, big-endian y no especificadas. De Verdad? Si Unicode hubiera especificado que todo el mundo tenía que hacer little endian desde el principio, todo habría sido más simple. (Hubiera estado bien con big-endian también.) En cambio, algunas personas lo implementaron de una manera, otras de otra, y ahora estamos atrapados en la tontería por nada. A veces es vergonzoso ser ingeniero de software.


Se supone que la endianess no especificada incluye BOM como el primer carácter, utilizado para determinar de qué manera se debe leer la cadena. UCS-4 y UTF-32 de hecho son los mismos hoy en día, es decir, un valor numérico de UCS entre 0 y 0x10FFFF almacenado en un entero de 32 bits.

55
@Tronic: Técnicamente, esto no es cierto. Aunque UCS-4 puede almacenar cualquier número entero de 32 bits, UTF-32 tiene prohibido almacenar los puntos de código sin caracteres que son ilegales para el intercambio, como 0xFFFF, 0xFFFE y todos los sustitutos. UTF es una codificación de transporte, no interna.
tchrist

Los problemas de endianness son inevitables siempre que diferentes procesadores continúen usando diferentes órdenes de bytes. Sin embargo, podría haber sido bueno si hubiera un orden de bytes "preferido" para el almacenamiento de archivos de UTF-16.
Qwertie

Aunque UTF-32 es de ancho fijo para los puntos de código , no es de ancho fijo para los caracteres . (¿Has oído hablar de algo llamado "combinación de caracteres"?) Por lo tanto, no puedes ir al enésimo carácter simplemente indexando 4N en la matriz de bytes.
musiphil

2

No creo que sea dañino si el desarrollador es lo suficientemente cuidadoso.
Y deberían aceptar este intercambio si también lo saben bien.

Como desarrollador de software japonés, considero que UCS-2 es lo suficientemente grande y limitar el espacio aparentemente simplifica la lógica y reduce la memoria de tiempo de ejecución, por lo que usar utf-16 bajo la limitación UCS-2 es lo suficientemente bueno.

Hay un sistema de archivos u otra aplicación que supone que los puntos de código y los bytes son proporcionales, de modo que se puede garantizar que el número de punto de código sin formato se ajuste a algún almacenamiento de tamaño fijo.

Un ejemplo es NTFS y VFAT que especifican UCS-2 como codificación de almacenamiento de nombre de archivo.

Si ese ejemplo realmente quiere extenderse para admitir UCS-4, podría estar de acuerdo con usar utf-8 para todo de todos modos, pero la longitud fija tiene buenos puntos como:

  1. puede garantizar el tamaño por longitud (el tamaño de los datos y la longitud del punto de código es proporcional)
  2. puede usar el número de codificación para la búsqueda de hash
  3. los datos no comprimidos tienen un tamaño razonable (en comparación con utf-32 / UCS-4)

En el futuro, cuando la potencia de memoria / procesamiento sea barata, incluso en cualquier dispositivo incorporado, podemos aceptar que el dispositivo sea un poco lento para errores de caché adicionales o fallas de página y uso de memoria adicional, pero supongo que esto no sucederá en el futuro cercano ...


3
Para aquellos que leen este comentario, vale la pena señalar que UCS-2 no es lo mismo que UTF-16. Por favor, busque las diferencias para entender.
mikebabcock

1

"¿Debería una de las codificaciones más populares, UTF-16, ser considerada dañina?"

Muy posiblemente, pero las alternativas no necesariamente deben verse como mucho mejores.

La cuestión fundamental es que existen muchos conceptos diferentes sobre: ​​glifos, caracteres, puntos de código y secuencias de bytes. El mapeo entre cada uno de estos no es trivial, incluso con la ayuda de una biblioteca de normalización. (Por ejemplo, algunos caracteres en idiomas europeos que se escriben con un guión basado en el latín no se escriben con un único punto de código Unicode. ¡Y eso está en el extremo más simple de la complejidad!) Lo que esto significa es que hacer que todo sea correcto es asombrosamente difícil; se esperan errores extraños (y en lugar de quejarse de ellos aquí, dígales a los encargados del mantenimiento del software en cuestión).

La única forma en que UTF-16 puede considerarse dañino en lugar de, por ejemplo, UTF-8 es que tiene una forma diferente de codificar puntos de código fuera del BMP (como un par de sustitutos). Si el código desea acceder o iterar por punto de código, eso significa que debe ser consciente de la diferencia. OTOH, significa que un cuerpo sustancial de código existente que asume "caracteres" siempre puede encajar en una cantidad de dos bytes, una suposición bastante común, si es incorrecta, al menos puede continuar funcionando sin reconstruirlo todo. En otras palabras, ¡al menos puedes ver esos personajes que no se manejan correctamente!

Diría su pregunta y diría que todo el maldito shebang de Unicode debería considerarse dañino y todos deberían usar una codificación de 8 bits, excepto que he visto (en los últimos 20 años) a dónde lleva eso: horrible confusión sobre las diversas codificaciones ISO 8859, más el conjunto completo de las utilizadas para cirílico, y el conjunto EBCDIC, y ... bueno, Unicode por todas sus fallas supera eso. Si tan solo no fuera un compromiso tan desagradable entre los malentendidos de diferentes países.


Conociendo nuestra suerte, en unos años nos encontraremos quedando sin espacio en UTF-16. Meh
Donal Fellows

3
La cuestión fundamental es que el texto es engañosamente difícil. Ningún enfoque para representar esa información de forma digital puede ser sencillo. Es la misma razón por la que las fechas son difíciles, los calendarios son difíciles, el tiempo es difícil, los nombres personales son difíciles, las direcciones postales son difíciles: cada vez que las máquinas digitales se cruzan con construcciones culturales humanas, surge la complejidad. Es un hecho de la vida. Los humanos no funcionan con la lógica digital.
Aristóteles Pagaltzis
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.