Normalizador Unicode

Unicode tiene cuatro formas de normalización y no son intercambiables. NFC y NFD reorganizan el mismo carácter entre un punto de código y una base más una marca combinante; NFKC y NFKD van más allá y reescriben caracteres que solo se parecen entre sí — la ligadura fi, la A de ancho completo, el ① circulado, el numeral romano Ⅻ. Esta página pasa el mismo texto por las cuatro, muestra una fila por carácter indicando qué cambió y por qué, cuenta grafemas frente a puntos de código y bytes UTF-8, y compara dos cadenas con la forma que elija. La segunda mitad trata de los caracteres que nunca aparecen: espacios de ancho cero, uniones, controles bidireccionales, selectores de variación, caracteres de control C0 y C1, puntos de código de uso privado y mitades de sustitutos rotas, cada uno con su posición y su nombre, y la opción de eliminarlos de una copia del texto.
Todo el procesamiento se realiza localmente en su navegador — sus datos nunca se suben a ningún servidor.

¿No sabe qué es la normalización? La normalización Unicode explicada en lenguaje sencillo — por qué é puede ser uno o dos caracteres, y por qué eso rompe un inicio de sesión.
Forma para informar y para comparar:
Comparar los dos cuadros después de:
NFC (todavía nada)
NFD (todavía nada)
NFKC (todavía nada)
NFKD (todavía nada)
Todavía no se ha comparado nada. Pegue las dos cadenas y pulse Comparar las dos cadenas.
primera cadena, normalizada (todavía nada)
segunda cadena, normalizada (todavía nada)
Todavía no se ha analizado nada. Pulse Buscar caracteres invisibles y peligrosos y después Eliminar si quiere el texto sin esos caracteres.
texto sin las clases seleccionadas (todavía nada)
Ctrl+Intro en el primer cuadro ejecuta el modo activo.
Elija un modo y haga clic en su botón de acción. Normalizar escribe las cuatro formas de su texto y una fila por carácter; Comparar indica si dos cadenas son iguales tras la normalización; Analizar enumera los caracteres que no se ven, y puede eliminarlos. El botón Autocomprobación vuelve a ejecutar cada caso documentado en el archivo de datos de esta página, cada carácter que nombran las tablas de invisibles y de control, e informa del recuento aquí.
Cada carácter, una fila cada uno:qué es el carácter, qué hacen con él las cuatro formas y por qué. Una fila sombreada es un carácter que al menos una forma cambia; la columna NFD muestra en qué se descompone el carácter cuando se descompone.
¿Cuántos caracteres son?El mismo texto contado de cuatro formas: grupos de grafemas (lo que cuenta un lector), puntos de código (lo que asigna Unicode), unidades UTF-16 (lo que indexa JavaScript) y bytes UTF-8 (lo que viaja). La normalización mueve las cifras entre la segunda y la tercera columna sin cambiar la primera.
Lo que se encontró, en el orden en que aparece:la posición cuenta puntos de código, la clase es aquella a la que pertenece el carácter, y la nota indica qué hace ese carácter a lo que lea el texto después.
Las clases que esta página considera sospechosas:la tabla se imprime desde el propio analizador, así que las palabras de esta página y el comportamiento de los botones no pueden desincronizarse. La gravedad se refiere al efecto en una comparación, no a la probabilidad de que el carácter sea inofensivo.
Los caracteres de uso privado se enumeran como sospechosos por un motivo distinto al resto: sí se muestran, simplemente no tienen un significado acordado, así que el mismo punto de código es una imagen distinta en cada fuente que lo define.
Tamaño máximo de entrada que acepta esta página, y por qué:la normalización reconstruye la cadena entera, y esta página hace varias de ellas más un análisis y un recuento de grafemas, así que el coste crece en línea recta con la longitud de la entrada. Los límites de abajo existen porque la tabla por carácter se dibuja en el documento, y una tabla con un millón de filas es lo que de verdad detiene un navegador.
¿Qué es la normalización Unicode?

Unicode ofrece más de una forma de escribir algunos caracteres. La letra é puede ser un único punto de código, U+00E9, o una e normal seguida de un acento agudo combinante, U+0065 U+0301. Las dos se ven igual en pantalla, se imprimen igual, y como cadenas son distintas: tienen longitudes distintas, bytes UTF-8 distintos, y 'é' === 'e\u0301' es false.

No es una curiosidad, es una clase de error. Obtener una forma u otra depende de la distribución del teclado, del sistema operativo, del método de entrada y de si el texto se copió alguna vez a través de un procesador de textos. Una comprobación de contraseña, un índice único de base de datos, una lista de bloqueo o una clave de caché que compare cadenas byte a byte tratará las dos grafías como dos valores distintos — así un usuario puede fijar una contraseña en un dispositivo y no poder entrar desde otro, y la misma palabra puede aparecer dos veces en una lista de nombres «ya ocupados».

La versión de 30 segundos: NFC y NFD son el mismo texto escrito de dos formas — NFC compone (e + acento se convierte en un solo carácter), NFD descompone (un carácter se convierte en e + acento). Están pensadas para no perder información y son las dos formas que conviene usar para comparar y almacenar. NFKC y NFKD hacen todo lo que hacen las dos primeras y además sustituyen los caracteres de compatibilidad: la ligadura fi se convierte en dos letras, ½ pasa a 1⁄2, el numeral romano ⅲ pasa a XII, una A de ancho completo pasa a una A normal. Eso es exactamente lo que quiere un índice de búsqueda o un filtro de términos de búsqueda, y exactamente lo que nunca debe aplicarse a una contraseña.
Por qué cuatro formas y no una

La división existe porque se servían dos objetivos distintos. Componer y descomponer texto es un cambio canónico: el resultado significa lo mismo y, en las dos primeras formas, no se pierde nada. Plegar ligaduras, superíndices, letras circuladas y formas de ancho completo es un cambio de compatibilidad: se hace por comodidad para las coincidencias, y sí destruye la distinción entre dos cadenas que Unicode considera distintas. Las cuatro formas son todas las combinaciones de esas dos decisiones.

La tabla de arriba es la lista de casos del archivo de datos de esta página, comprobada contra el String.prototype.normalize de la propia plataforma, y la autocomprobación vuelve a verificar cada fila delante de usted. Lea juntas las dos primeras filas: son el mismo texto escrito de dos formas, y tras la normalización son la misma cadena.

Ejemplos resueltos

Estas son las cadenas que recorre el botón Ejemplo, normalizadas en vivo por la página en lugar de escritas como respuestas. Cada una se eligió porque alguna forma hace algo visible con ella.

Caracteres de compatibilidad: lo que cuesta NFKC

NFKC y NFKD son con pérdida, y la pérdida es deliberada. Responden a la pregunta «¿es esta la misma palabra?», no a «¿es esta la misma cadena?». De ahí se derivan tres consecuencias, y las tres son fáciles de equivocar en código de producción:

  • Nunca almacene una contraseña, un token o una firma en una forma de compatibilidad. Dos entradas distintas colapsarían en el mismo valor almacenado, lo que convierte una comprobación de longitud o de caracteres en una más débil. Normalice las contraseñas con NFC (o no las normalice) y compárelas como bytes tras hacer exactamente lo mismo en ambos lados.
  • Un plegado de compatibilidad puede convertir un carácter en varios. La ligadura fi es un punto de código que se convierte en dos letras, así que un campo validado por longitud antes de normalizar puede resultar más largo después. Normalice primero, luego valide, y después almacene.
  • NFKC no elimina todo lo invisible. Un selector de variación sobrevive a NFKC y a NFKD, y también un unificador de ancho cero. Por eso esta página tiene un modo de análisis: la normalización responde a una pregunta sobre la forma e identidad de un carácter, y el analizador responde a si algo está presente o no.
Grafemas, puntos de código y bytes

La normalización cambia el segundo de estos recuentos y normalmente deja el primero igual. Los tres números son todos «la longitud del texto» y todos distintos, por eso un campo de formulario con un límite de 20 caracteres puede corresponder a una cadena de 20 puntos de código, 30 unidades UTF-16 y 47 bytes.

El número de la columna de grafemas es lo que cuenta una persona. La diferencia respecto del recuento de puntos de código es exactamente de lo que trata el analizador: un nombre formado por 12 puntos de código puede mostrarse como 12 letras, como 3 letras con 9 marcas invisibles entre ellas, o como un solo emoji.

Los caracteres que no se pueden ver

Un espacio de ancho cero no ocupa ancho, no cambia ningún glifo, y es un carácter real con un punto de código real. Dos identificadores que se ven idénticos en pantalla pueden diferenciarse por uno de ellos, así es como un nombre de una lista puede ser un impostor que se ordena, busca y autentica de forma distinta al nombre de al lado. Lo mismo ocurre con los controles bidireccionales, que pueden hacer que una cadena se muestre en un orden distinto al orden en que se escribió — un truco que se ha usado para disfrazar nombres de archivo.

El analizador del tercer modo los enumera todos con su posición: caracteres de ancho cero y de unión, controles bidireccionales, selectores de variación, espacios inusuales, caracteres de control C0 y C1, puntos de código de uso privado, y un sustituto que no es mitad de un par válido, que ni siquiera es un carácter. La tabla de clases más arriba en esta página explica lo que hace cada clase, y el botón de eliminar quita una copia de esos caracteres sin tocar su entrada.

Los errores que esta página está diseñada para detectar
  • Comparar dos cadenas sin normalizarlas. 'é' === 'e\u0301' es false. Normalice ambos lados con la misma forma antes de comparar, y use la misma forma en el índice con el que compare.
  • Normalizar solo en un lado. Una búsqueda que normaliza la consulta pero no el valor almacenado no encuentra nada, y parece un problema de datos y no de normalización.
  • Usar una forma de compatibilidad en un formulario y llamarlo seguro para NFC. Aplicar NFKC a una contraseña reduce el espacio de contraseñas posibles y fusiona entradas que debían ser distintas.
  • Suponer que una forma de normalización corrige una suplantación. No elimina un unificador de ancho cero, un selector de variación ni un homoglifo — una а cirílica y una a latina son puntos de código distintos que NFC deja tal cual.
  • Confiar en un recuento de caracteres. El recuento que ve el usuario, el que impone una columna de base de datos y el que impone un límite de bytes son tres números distintos, y las filas de arriba muestran lo separados que pueden estar.
  • Suponer que va a recibir NFC. El texto que llega de un método de entrada, de un archivo o de otro sistema está en la forma que haya producido ese sistema. Esta página empieza en NFC porque es la forma que se espera que use la web, no porque sea la que tiene su entrada.
Respuestas rápidas

«¿Por qué estas dos cadenas no son iguales si se ven igual?»

Porque una de ellas está escrita con un carácter compuesto y la otra con una letra base más una marca combinante, o porque una contiene un carácter que no dibuja nada. Cambie al modo Comparar y ponga una cadena en cada cuadro: la página informa de la primera posición en la que difieren las cadenas normalizadas y de lo que hay en esa posición en cada una.

«¿Qué forma debo usar en una aplicación?»

NFC para todo lo que una persona vaya a leer, mostrar, almacenar y buscar, que es la forma que recomienda el W3C para la web. NFD es la respuesta correcta cuando el código que tiene detrás quiere letras base y marcas separadas, lo que es habitual en el procesamiento de texto y en algunos sistemas de archivos de macOS. Use NFKC o NFKD solo cuando esté haciendo coincidencias en lugar de almacenar: índices de búsqueda, filtros de palabras clave, detección de duplicados.

«¿Una cadena ya normalizada sigue normalizada después de concatenarle algo?»

No necesariamente. La normalización no se conserva al concatenar: unir dos cadenas NFC puede producir una secuencia que NFC habría escrito de otra forma, porque una marca combinante al inicio de la segunda cadena puede unirse al carácter final de la primera. Normalice después de ensamblar, no solo antes.

«¿La normalización elimina los caracteres invisibles?»

No, con una excepción parcial: NFKC y NFKD sí sustituyen el espacio sin ruptura por un espacio normal, que es un resultado visible de un plegado de compatibilidad. Todo lo demás — espacios de ancho cero, uniones, controles bidireccionales, selectores de variación — pasa por las cuatro formas sin tocar. Para eso existe el tercer modo.

«¿Por qué cambia el recuento de bytes cuando no cambia el de caracteres?»

Porque las dos formas se construyen con puntos de código distintos que ocupan números distintos de bytes en UTF-8. Una é precompuesta ocupa dos bytes; el par descompuesto ocupa tres. La tabla de recuentos de arriba imprime las cuatro cifras una al lado de otra para la entrada y para cada forma, así que la diferencia se ve en lugar de describirse.

«¿Cómo sé que los resultados de esta página son correctos?»

Pulse Autocomprobación. Normaliza cada caso del archivo de datos de esta página, vuelve a comprobar las cuatro formas entre sí según las relaciones documentadas (NFC es idempotente, NFD de una cadena NFC es igual al NFD del original, etcétera), pasa por el analizador cada carácter nombrado en las tablas de invisibles y de control, y compara los casos de grafemas con Intl.Segmenter.

Estándares y créditos

Las cuatro formas y sus definiciones proceden del Estándar Unicode, Anexo 15 (Unicode Normalization Forms). Los puntos de código usados como ejemplos de descomposición son los que asigna el estándar; esta página no implementa por sí misma las tablas de descomposición — llama al String.prototype.normalize del propio navegador, que es la misma implementación que usa la plataforma para la entrada de texto, la ordenación y la comparación, así que lo que se informa aquí es lo que hace el resto del sistema con la misma cadena. Los grupos de grafemas proceden de Intl.Segmenter cuando el navegador lo tiene, y de una aproximación documentada cuando no.

El texto de la página se escribió para este sitio. Cada ejemplo resuelto de esta página lo produjo el propio código de la página y después se comprobó contra la lista de casos registrada en el archivo de datos, que a su vez se comprobó contra los datos NormalizationTest de Unicode para los mismos caracteres. Las tablas de caracteres invisibles y peligrosos son un subconjunto seleccionado, escrito para advertencias legibles por personas, no el perfil de seguridad completo de Unicode (UTS #39), y la página lo indica en lugar de sugerir una completitud que no tiene.