ASCII, Unicode y UTF-8: Planos, Normalización y Orden Alfabético
Dev Tools

ASCII, Unicode y UTF-8: Planos, Normalización y Orden Alfabético

Dónde Acaba lo Básico y Empieza Esto

Si lo que buscas es la versión corta —qué es un código de carácter, por qué ASCII se quedó en 128 posiciones, qué rompieron las páginas de código y cómo UTF-8 convierte un punto de código en bytes—, eso ya está contado en la propia página del conversor ASCII y Unicode, en la guía que hay bajo la herramienta. Este artículo arranca justo donde termina aquel resumen. Sigue cuatro hilos que solo empiezan a importar cuando lo anterior ya está claro: cómo se publicó, se revisó y se rebautizó realmente el estándar ASCII a lo largo de veintitrés años; cómo está repartido el espacio de códigos de Unicode en planos y qué vive por encima del primero; por qué dos cadenas idénticas en pantalla pueden fallar una comparación; y qué provoca todo eso al ordenar, al buscar y al intentar medir cuánto ocupa un texto.

ASA X3.4-1963 y Todo lo que Vino Después

La versión de ASCII que casi todo el mundo tiene en la cabeza —128 posiciones, el alfabeto en sus dos cajas, la puntuación de siempre— no es la que se aprobó primero. La American Standards Association aprobó X3.4 el 17 de junio de 1963, y aquella primera edición no llevaba ni una sola minúscula. Las columnas que acabarían albergando de la a a la z quedaron en su mayor parte sin asignar, a la espera de una decisión que nadie había tomado todavía.

La revisión de 1967 es la que dio lugar a la tabla que seguimos usando. Rellenó las minúsculas, y colocó cada una a exactamente un bit de distancia de su mayúscula gemela: cambia el bit 6 de la A (0x41) y caes en la a (0x61). Por eso cambiar de caja dentro del rango ASCII se resuelve con una sola operación de bits en vez de con una tabla de consulta, un truco de eficiencia que ha sobrevivido al hardware para el que se pensó. Esa misma revisión añadió las llaves y la barra vertical, y reorganizó la columna de control: SOM pasó a llamarse SOH y RU desapareció sin más.

Aquella revisión salió como USAS X3.4-1967, y el cambio de prefijo no es una errata. El propio organismo de normalización se fue rebautizando por debajo del documento. Nació en 1918 como American Engineering Standards Committee, se convirtió en American Standards Association en 1928, se reorganizó como United States of America Standards Institute en 1966 —de ahí el "USAS" de la portada de 1967— y se quedó por fin en American National Standards Institute en 1969. Las revisiones siguieron llegando ya con el nombre nuevo: ANSI X3.4-1977 y después ANSI X3.4-1986. El comité que la patrocinaba cambió de nombre otras dos veces —el ASC X3 pasó a ser NCITS en 1996 y INCITS en 2001— y se llevó consigo aquella última edición, primero como ANSI NCITS 4-1986 y después como INCITS 4-1986, que sigue siendo la vigente, reafirmada como INCITS 4-1986 (R2022). Seis décadas, una tabla de 128 filas, cuatro nombres distintos de organismo en la cubierta.

El documento orientado a redes es otro, y posterior. El RFC 20, de octubre de 1969, es el que fijó el juego de caracteres para los protocolos de la época de ARPANET, y es la referencia que aparece en las especificaciones de internet en lugar de la edición de ANSI, que no se puede consultar gratis. Ahora bien, para lo que de verdad hace falta a diario —nombrar una codificación en una cabecera o en un archivo de configuración—, la cadena correcta no es ninguna de las dos: el nombre preferido por IANA es US-ASCII, y el montón de alias registrados a su lado, entre ellos ANSI_X3.4-1968, existe precisamente porque el software lleva cincuenta años escribiendo el nombre que estuviera vigente en cada momento.

Diecisiete Planos, y la Falla de los 65.536

El espacio de códigos de Unicode va de U+0000 a U+10FFFF: 1.114.112 posiciones, archivadas en 17 planos de 65.536 cada uno. Ese techo no se eligió por elegancia. Es exactamente lo que el mecanismo de subrogados de UTF-16 alcanza a direccionar, y el espacio se limitó para que encajara — el tope existe por culpa de una codificación, y no al revés.

El plano 0 es el Plano Multilingüe Básico, y contiene casi todo lo que aparece en un texto corriente: los caracteres latinos, griegos, cirílicos, árabes, hebreos, devanagari y han de uso diario, junto con la puntuación y los signos de moneda. Por encima, el archivo se vuelve más extraño:

  • Plano 1, el Plano Multilingüe Suplementario. Sistemas de escritura que nadie teclea en un móvil —el silabario Lineal B a partir de U+10000, el gótico en U+10330— comparten plano con la notación musical bizantina y la occidental (U+1D000 y U+1D100), con el bloque de Símbolos Alfanuméricos Matemáticos que da a los matemáticos sus letras caligráficas y de fraktur (U+1D400) y, de forma bastante incongruente, con los emojis: Emoticonos en U+1F600 y Símbolos y Pictogramas Diversos en U+1F300.
  • Plano 2, el Plano Ideográfico Suplementario, y plano 3, el Plano Ideográfico Terciario. Ideogramas CJK raros e históricos que no cabían en los bloques han del plano 0.
  • Plano 14, el Plano Suplementario de Uso Especial. Una zona de desbordamiento para caracteres de control de formato, casi vacía a propósito.
  • Planos 15 y 16, las Áreas Suplementarias de Uso Privado A y B: 131.068 posiciones que el estándar se compromete a no asignar nunca, de modo que un fabricante o un sistema cerrado pueda definir sus propios caracteres sin chocar jamás con una versión futura.

Con Unicode 17.0, publicado el 9 de septiembre de 2025, hay 159.801 caracteres realmente codificados — alrededor de una séptima parte del espacio disponible, después de 4.803 incorporaciones solo en esa versión. Todo lo demás está sin asignar, y ese es justo el diseño: la estructura de planos es un archivador construido a conciencia con sitio de sobra.

Lo que esa distribución produce en la práctica es una única falla dura, en U+FFFF, el techo del plano 0. Todo lo que queda en ese límite o por debajo cabe en una unidad de 16 bits; todo lo que queda por encima, no, y un sistema que guarde texto en unidades de 16 bits tiene que gastar dos. Los emojis están por encima de la falla. Casi todo lo que contiene el plano 1, también. Por eso una API de cadenas diseñada en los años noventa, cuando "con 16 bits sobra para cualquier carácter" todavía era una postura defendible, informa de una longitud de 2 para una sola cara sonriente — y por eso la tabla del conversor cuenta por punto de código y no por unidad de almacenamiento, y saca el par UTF-16 en su propia columna para que los dos números estén a la vista a la vez en lugar de tener que deducir uno de ellos.

Dos Secuencias, un Mismo Aspecto

Hay una propiedad de Unicode que no tiene nada que ver con las codificaciones y que suele sorprender a quien creía haber terminado de aprender: un mismo texto visible puede ser más de una secuencia legítima de puntos de código. Ninguna decisión sobre codificación afecta a esto, porque ocurre una capa más arriba.

La Unicode Character Database lo dice sin rodeos. El registro de U+00E9 en UnicodeData.txt reza, en parte, 00E9;LATIN SMALL LETTER E WITH ACUTE;...;0065 0301;, y ese campo final es una descomposición canónica: una declaración del estándar de que ese único carácter y la secuencia de dos puntos de código U+0065 U+0301 son la misma cosa. No parecidos, ni suficientemente próximos: canónicamente equivalentes. Cualquier proceso conforme puede sustituir uno por el otro sin pedir permiso.

El UAX #15 define cuatro formas de normalización para zanjar la cuestión. NFD aplica la descomposición canónica y expande cada carácter precompuesto en un carácter base más sus marcas combinantes en un orden fijo. NFC hace esa descomposición y después vuelve a componer, con lo que prefiere el punto de código precompuesto único siempre que el estándar defina uno. NFKD y NFKC ejecutan esas mismas dos operaciones pero con descomposición de compatibilidad, un instrumento mucho más romo: funde las variantes de estilo, de manera que esas letras matemáticas de fraktur se derrumban en ASCII normal y el ligado fi se separa en f e i. NFKC va de maravilla para construir un índice de búsqueda y es destructivo si se lo aplicas a un texto que piensas guardar y mostrar.

Todo esto se cuela en aplicaciones corrientes porque distintos sistemas emiten distintas formas sin que nadie se lo pida. La guía de internacionalización del W3C recomienda NFC para el contenido de la web, sobre todo porque es lo que producen la mayoría de los teclados. El HFS+ de Apple tiró por el camino contrario y guardaba los nombres de archivo descompuestos —la nota técnica TN1150 lo dice con todas las letras— con unas reglas congeladas en Unicode 3.2, lo que lo deja muy cerca de NFD sin llegar a serlo exactamente. Después APFS volvió a cambiar de política: conserva el nombre tal como se lo entregaron y normaliza únicamente para calcular el hash. Mueve un archivo llamado café.txt entre sistemas que no se ponen de acuerdo en esto y puedes acabar con un listado de directorio que muestra un nombre y una consulta posterior que se va de vacío, porque esas cuatro letras visibles son cinco puntos de código a un lado de la transferencia y cuatro al otro.

ASCII, Unicode y UTF-8: Planos, Normalización y Orden Alfabético

Ordenar: el Orden de la Tabla No Es el Orden Alfabético

El UTS #10, el algoritmo de ordenación de Unicode, empieza demoliendo la suposición sobre la que descansa la mayor parte del código que ordena cadenas: el sitio que ocupa un carácter en las tablas no determina el sitio que le corresponde al ordenar, y ninguna disposición de un juego universal de caracteres podría lograr que una comparación binaria diera el orden correcto en todos los idiomas a la vez.

El español lo demuestra de forma limpia. El abecedario que reconoce la Real Academia Española tiene 27 letras, con la ñ en un puesto propio justo detrás de la n — así que año va entre anular y apagar. Ordena por punto de código y no cae ni cerca: la ñ es U+00F1, 241 en decimal, mientras que la z es U+007A, 122, de modo que una ordenación cruda por valor de byte manda todas las eñes detrás de todas las zetas, al extremo equivocado de la lista. Una advertencia por si tiras del ejemplo de manual: ch y ll se retiraron como entradas alfabéticas propias y hoy se ordenan como los pares de letras corrientes que son, así que ya no ilustran nada.

Fuera del español la cosa empeora en lugar de mejorar. La ordenación alemana de guía telefónica trata la ä como si fuera ae. El sueco coloca la ä casi al final de su alfabeto, pasada la z, como letra de pleno derecho. El danés y el noruego archivan æ, ø y å detrás de la z, en ese orden. Dos de esos idiomas usan el mismo carácter y discrepan sobre dónde le toca ir, que es justo el motivo por el que una respuesta correcta necesita llevar una configuración regional pegada y no solo una cadena. La regla práctica que se deduce es breve: nunca ordenes texto visible para el usuario con un operador de comparación pelado. Pásaselo a un comparador que acepte una configuración regional —Intl.Collator en JavaScript, ICU por debajo de la mayoría de entornos de servidor, una cláusula COLLATE explícita en SQL— y asume que la respuesta correcta cambia legítimamente según quién esté leyendo.

Buscar: Tres Formas de que una Coincidencia Exacta Falle en Silencio

La búsqueda hereda todos los problemas anteriores y aporta uno propio. Tres fallos explican la mayoría de los avisos que empiezan por "el registro está ahí seguro".

  • Un desajuste de normalización. Una consulta tecleada en un teclado que emite NFC no será igual a un valor guardado que llegó en NFD, aunque los dos se dibujen idénticos a cualquier tamaño de letra. Normaliza los dos lados a una misma forma antes de comparar: la consulta que entra tanto como lo que hayas indexado, porque normalizar solo uno de los dos no arregla nada.
  • El plegado de mayúsculas fuera del rango ASCII. Cambiar de caja deja de ser una operación carácter a carácter en cuanto sales de lo básico latino. La ß alemana pasa a mayúsculas como SS, dos letras a partir de una, así que cualquier comparación que ignore mayúsculas y dé por hecho que la longitud sobrevive a la operación ya está rota. El turco es aún más duro: distingue una ı sin punto de una i con punto como letras separadas, de modo que poner en mayúscula una i con configuración regional turca da İ y no I, y una conversión a mayúsculas sensible a la región aplicada a un identificador o a una palabra clave de protocolo lo corrompe en silencio. Ese peligro concreto es la razón de que las comparaciones sin distinción de caja en los protocolos se especifiquen contra una configuración regional invariante y no contra la del usuario.
  • Tildes que el usuario no escribió. Quien busca "Munoz" en una lista de clientes espera que aparezca Muñoz. Eso no lo da nadie gratis: hay que construirlo, descomponiendo a NFD, descartando las marcas combinantes y comparando lo que queda. Aplica ese plegado al construir el índice y a la consulta, y nunca al valor que guardas y muestras.

Longitud: Decide Qué Estás Preguntando

"Cuánto ocupa este texto" tiene cuatro respuestas correctas distintas, y la mayor parte de los fallos de esta zona vienen de leer lo que devuelve una función como si hubiera devuelto otra de las cuatro.

  • Bytes. Depende por completo de la codificación en uso, y es lo que restringen de verdad el ancho de una columna de base de datos, una trama de red o un formato de archivo de ancho fijo.
  • Unidades de código. Lo que devuelve String.length en JavaScript, Java y C#, porque esos lenguajes guardan el texto en unidades de 16 bits. Fiable hasta el momento exacto en que algo cruza U+FFFF.
  • Puntos de código. Cuántas entradas del catálogo de Unicode hay presentes. Es la cifra que da la tabla del conversor, una fila por punto de código.
  • Grupos de grafemas. Cuántas cosas señalaría un lector llamándolas caracteres, según define el UAX #29. Es lo que una persona quiere decir cuando dice "veinte caracteres".

Esas cuatro cifras se separan mucho más a menudo que antes. Una mano saludando con un modificador de tono de piel son dos puntos de código y un grupo. Una e seguida de un acento agudo combinante son dos puntos de código y un grupo. Una bandera es una pareja de puntos de código indicadores de región y un grupo. Un emoji de familia montado con uniones de ancho cero puede llegar a siete puntos de código, veinticinco bytes UTF-8 y once unidades UTF-16, y aun así seguir siendo un solo objeto en pantalla que el lector borra con una sola pulsación de retroceso. Impón un límite de "20 caracteres" usando la cifra equivocada de las cuatro y acabarás partiendo un nombre por la mitad o rechazando uno que nunca fue demasiado largo.

Llevarlo a la Práctica

  • Normaliza una sola vez, a la entrada, en la frontera por la que el texto llega al sistema, y elige NFC salvo que tengas un motivo concreto para no hacerlo. Meter la normalización a posteriori en una tabla que ya guarda las dos formas es una migración de datos, no un cambio de configuración.
  • Nunca ordenes cadenas visibles para el usuario con un operador de comparación pelado. Dale una configuración regional a un comparador y deja que el resultado sea correcto para quien lo lee.
  • Averigua cuál de las cuatro medidas de longitud está protegiendo realmente cada límite, y aplica esa. Los límites en bytes protegen el almacenamiento; los recuentos de grupos protegen a las personas.
  • Trata U+FFFF como la frontera que es de verdad. El código que nunca se ha probado con un carácter por encima de ese punto tampoco se ha probado nunca con un emoji.
  • Cuando el texto no solo aparece mal ordenado sino visiblemente sustituido por otras letras, cambia de diagnóstico — eso es el fallo del mojibake, un mecanismo distinto con una solución distinta. Y cuando el problema es un carácter que no se ve en absoluto en vez de uno que sí, lee qué le hacen realmente LF, CR y NUL a un archivo.

Todo lo anterior se puede comprobar carácter a carácter. Pega una cadena en el conversor ASCII y Unicode y lee el punto de código, el bloque, los bytes UTF-8 y las unidades UTF-16 en una misma fila — la vía más rápida para saber si la é que tienes delante es uno o dos puntos de código, y si el emoji que estás a punto de guardar ocupa una unidad o un par. UTF-8 en sí, normalizada como RFC 3629 en noviembre de 2003 e ideada once años antes por Ken Thompson siguiendo unos criterios de diseño planteados por Rob Pike, es la única capa de esta pila que casi nunca se pone en medio. Todo lo que se apila por encima —los planos, las formas de normalización, las reglas de ordenación— es donde vive el trabajo que queda.

← Volver al Blog