Saltar al contenido

¿Qué cuenta como un carácter?

Cuatro sistemas distintos cuentan el mismo texto de cuatro formas distintas. Con prosa en inglés coinciden, y por eso el problema pasa desapercibido hasta el día en que deja de coincidir.

Última revisión:

Un emoji, cuatro respuestas

Toma un pulgar arriba con un tono de piel aplicado, o el emoji de una familia. Cuéntalo de cuatro formas y obtendrás cuatro números, y ninguno está mal.

Un lector ve un carácter. Una sola cosa, que se borra con una sola pulsación de la tecla de retroceso.

Una expresión regular puede ver siete. Está formado por varios puntos de código unidos por conectores invisibles: un emoji base, un modificador, un unificador, otro emoji, etc.

JavaScript dice once. Guarda el texto en unidades de 16 bits, y todo lo que queda fuera del rango básico ocupa dos, así que cada uno de esos puntos de código puede contar doble.

Una columna de base de datos ve veinticinco bytes. Codificado en UTF-8 para guardarse, cada punto de código ocupa hasta cuatro bytes.

Con prosa normal en inglés, los cuatro números son idénticos. Precisamente por eso el problema es invisible hasta que alguien escribe un nombre con acento, una oración en japonés o un solo emoji, y un campo que funcionó durante tres años empieza a recortar datos.

Qué número mide tu límite

La pregunta práctica nunca es “qué tan largo es este texto”. Es “qué es lo que impone el límite”, y hay cuatro respuestas habituales.

Una columna de base de datos declarada como VARCHAR cuenta bytes en la mayoría de los motores. Por eso un campo que acepta sin problema 255 caracteres en inglés rechaza muchos menos en japonés (tres bytes cada uno), y por eso un nombre con acento a veces no cabe cuando su versión sin acento sí cabía.

Un SMS tiene 160 caracteres en su propio alfabeto de siete bits, y baja a 70 por mensaje en cuanto un solo carácter queda fuera de él. Una sola comilla tipográfica pegada desde un procesador de textos, o un emoji, puede convertir un mensaje de una parte en tres y triplicar lo que te cobran.

Un validador de formularios en JavaScript casi siempre cuenta unidades UTF-16, y por eso un campo que dice 280 caracteres puede rechazar un texto que parece mucho más corto cuando está lleno de emojis.

Una persona cuenta lo que ve, que es la única definición que importa para un titular, una etiqueta title o cualquier cosa con un espacio visual limitado.

Dónde se separan los conteos

Saber qué entradas causan problemas es más útil que conocer la teoría, porque esas entradas son predecibles.

Los emojis, sobre todo los compuestos: tonos de piel, familias, banderas, profesiones. Una bandera son dos letras indicadoras regionales; una profesión suele ser una persona, un unificador y un objeto.

Los caracteres acentuados y combinables. Una é puede escribirse como un solo punto de código o como una e seguida de un acento agudo combinable. Se ven idénticas, se ordenan distinto, al compararlas no son iguales y cuentan distinto, y ambas formas aparecen en datos reales, a menudo en la misma columna.

Las escrituras no latinas. Los caracteres chinos, japoneses y coreanos ocupan tres bytes cada uno en UTF-8. El hindi, el tailandés, el tamil y el árabe forman grupos que un lector percibe como una sola unidad y que tienen varios puntos de código.

Los símbolos matemáticos y musicales, que están fuera del rango básico y por lo tanto cuentan doble en UTF-16.

Qué hacer al respecto

Cuenta lo que cuenta tu límite. Un contador que muestra los cuatro números responde la pregunta directamente, lo que es más rápido que razonarla. En el código, usa la función que corresponda: Intl.Segmenter en JavaScript cuenta lo que ve un lector, len(s.encode("utf-8")) en Python cuenta bytes, y en PHP, mb_strlen en lugar de strlen.

Arregla la columna, no el formulario. Si la restricción es un campo de base de datos, la solución duradera está antes: declárala como utf8mb4 en MySQL o text en PostgreSQL. El antiguo “utf8” de MySQL es famoso por admitir solo tres bytes por carácter y no poder guardar ni un emoji, una trampa de larga data que ha recortado en silencio muchísimos datos reales. Contar con cuidado en el formulario es un parche para una columna que debió declararse de otra forma.

Normaliza al entrar. Convertir el texto a una sola forma normal en el punto de entrada hace que las dos escrituras de é se vuelvan una, y las comparaciones, el orden y los conteos empiezan a coincidir. Todos los lenguajes tienen una función para ello, y hacerlo una vez en la frontera es mucho más fácil que manejar ambas formas en todas partes.

El caso relacionado: base64

Vale la pena mencionarlo porque produce el mismo tipo de sorpresa. Codificar datos en base64 toma tres bytes a la vez y los escribe como cuatro caracteres, así que el resultado es aproximadamente un tercio más grande: es aritmética, no ineficiencia. Un adjunto de 6 MB se convierte en unos 8 MB de texto, y por eso un archivo que está bastante por debajo de un límite de tamaño puede ser rechazado una vez codificado para enviarse.

La misma cuenta explica por qué los adjuntos de correo rebotan en límites que parecen cumplir. Si vas a comparar algo con un tope, mide la forma codificada.

Preguntas frecuentes

¿Por qué mi campo de 255 caracteres rechaza textos más cortos?

Casi seguro porque cuenta bytes, no caracteres. Las letras acentuadas ocupan dos bytes en UTF-8 y los caracteres CJK ocupan tres, así que 255 bytes pueden ser 85 caracteres japoneses. Declarar la columna como utf8mb4 o text lo soluciona como corresponde.

¿De verdad un emoji me cuesta tres SMS?

Puede ser. Un mensaje que solo contiene caracteres del alfabeto GSM tiene 160 por parte; un carácter fuera de él cambia todo el mensaje a una codificación de 70 caracteres. Así que un mensaje de 150 caracteres con un emoji pasa a tener tres partes en lugar de una.

¿Qué conteo debo usar para una etiqueta title?

Ninguno con exactitud: los buscadores recortan según el ancho en píxeles, no según el número de caracteres, así que un título con mayúsculas anchas se corta antes que uno con minúsculas estrechas. Unos 60 caracteres es la regla práctica habitual, y es una guía, no un límite.

¿Por qué dos textos que se ven idénticos no coinciden?

Normalmente porque un carácter acentuado está escrito de dos formas distintas: como un solo punto de código en uno y como una letra base más una marca combinable en el otro. Normalizar ambos a la misma forma antes de compararlos hace que coincidan, y vale la pena hacerlo en el punto donde los datos entran en tu sistema.

¿El conteo de palabras es más estable que el de caracteres?

En inglés, casi siempre. No en todos los idiomas: el chino y el japonés no separan las palabras con espacios, y el tailandés tampoco, así que contar palabras en esas escrituras requiere segmentación y cada herramienta da una respuesta distinta.