Saltar al contenido

Por qué el número de caracteres y el de bytes no coinciden

Publicado: · Última actualización:

Pegas el mismo texto en dos programas: uno dice 5 y el otro dice 15. Ninguno se equivoca; cuentan unidades distintas. Esta guía repasa las cuatro unidades con las que se mide la longitud de un texto y te enseña a averiguar a cuál corresponde el número que tienes delante.

Hay cuatro unidades para contar

  • Carácter: lo que en pantalla se ve como un solo carácter. El estándar Unicode lo llama grupo de grafemas (grapheme cluster). Es la unidad a la que se refiere la gente cuando pregunta «cuántos caracteres».
  • Punto de código: el número que Unicode asigna a cada carácter. A es U+0041 y € es U+20AC. Un carácter en pantalla puede estar formado por varios puntos de código.
  • Unidad de código UTF-16: la unidad de 16 bits con la que JavaScript y muchos otros entornos guardan las cadenas internamente. La mayoría de los puntos de código ocupan 1 unidad; los que están en U+10000 o por encima ocupan 2.
  • Byte: el espacio que ocupa realmente el texto al guardarlo en un archivo o enviarlo por una red. Depende de la codificación.
Carácter Caracteres Puntos de código Unidades de código UTF-16 Bytes en UTF-8
A 1 1 1 1
é 1 1 1 2
€ 1 1 1 3
😀 1 1 2 4

Bytes en UTF-8 según el tipo de carácter

UTF-8 usa 1–4 bytes según el rango en el que cae el punto de código.

Rango de puntos de código Bytes Qué incluye Ejemplos
U+0000–U+007F 1 Letras sin acento, cifras y signos básicos (ASCII) A 7 !
U+0080–U+07FF 2 Letras latinas con acento, griego, cirílico y otros é ß α Ж
U+0800–U+FFFF 3 Hangul, caracteres chinos, kana y muchos símbolos 한 字 あ €
U+10000–U+10FFFF 4 La mayoría de los emojis, caracteres chinos poco frecuentes y otros 😀

Por eso dos cadenas con el mismo número de caracteres pueden ocupar un número distinto de bytes.

Cadena Caracteres Bytes en UTF-8 Cálculo
Hello 5 5 5 × 1
café 4 5 3 × 1 + 2
Größe 5 7 3 × 1 + 2 × 2
Привет 6 12 6 × 2
안녕하세요 5 15 5 × 3
こんにちは 5 15 5 × 3
Hi 😀 4 7 3 × 1 + 4

Por qué un emoji que parece un solo carácter ocupa muchos bytes

Algunos emojis son varios puntos de código unidos que se dibujan como una sola imagen.

Emoji Caracteres Puntos de código Unidades de código UTF-16 Bytes en UTF-8 Composición
👍🏽 1 2 4 8 Pulgar U+1F44D + tono de piel U+1F3FD
🇺🇳 1 2 4 8 Indicadores regionales U+1F1FA + U+1F1F3
👨‍👩‍👧 1 5 8 18 3 personas + los 2 ZWJ que las unen

El emoji de la familia son tres emojis, 👨 👩 👧, con un conector de ancho cero (ZWJ, U+200D) entre ellos. Cada emoji ocupa 4 bytes y cada ZWJ ocupa 3, así que el total es 3 × 4 + 2 × 3 = 18 bytes. Un programa que cuenta caracteres muestra 1; uno que cuenta puntos de código, 5; y uno que cuenta unidades de código UTF-16, 8.

No pasa solo con los emojis. é puede escribirse como un único punto de código (U+00E9, 2 bytes) o como una e seguida de un acento agudo combinable (U+0301), que son 2 puntos de código (3 bytes). En pantalla se ven igual, pero el número de bytes es distinto.

Por qué un mismo carácter ocupa distintos bytes según la codificación

El número de bytes no lo fija el carácter, sino la codificación. Antes de que UTF-8 se generalizara, cada ámbito lingüístico usaba sus propias codificaciones, y estas guardaban los caracteres de su idioma en 2 bytes como máximo.

Carácter UTF-8 UTF-16 Codificación antigua
A 1 2 1
é 2 2 1 (windows-1252)
한 3 2 2 (EUC-KR)
あ 3 2 2 (Shift_JIS)
字 3 2 2 (EUC-KR, Shift_JIS)
😀 4 4 No se puede representar

De ahí que, para un mismo carácter, encuentres tanto «2 bytes» como «3 bytes». Lo primero se refiere a una codificación antigua como EUC-KR o Shift_JIS y lo segundo a UTF-8. 한글 abc ocupa 10 bytes en UTF-8 y 8 bytes en EUC-KR; Tokyo 東京 ocupa 12 bytes en UTF-8 y 10 bytes en Shift_JIS. Casi todos los sistemas que se crean hoy usan UTF-8, pero los sistemas antiguos, y los formularios que heredaron sus reglas, siguen aplicando la regla de 2 bytes.

Qué hay que contar en la práctica

Cada sitio que impone un límite cuenta en su propia unidad. Antes de ajustar un texto a un número, averigua qué se está contando.

  • Columnas de bases de datos: en algunos productos la longitud se mide en caracteres y en otros en bytes. Para varchar(n) de PostgreSQL, la documentación indica que n es un número de caracteres, no de bytes. Consulta la documentación del producto y de la configuración que uses.
  • Campos de formularios web: el atributo maxlength de HTML y length de JavaScript cuentan unidades de código UTF-16. En un campo con maxlength 10 solo caben 5 😀.
  • URL y Base64: ambos convierten bytes de UTF-8, así que la longitud del resultado depende del número de bytes y no del de caracteres. El carácter € se convierte en %E2%82%AC (9 caracteres) al codificarlo para una URL y en 4oKs (4 caracteres) en Base64.
  • Mensajes de texto y formularios con límite en bytes: cuánto cabe depende de la codificación que se use internamente. Comprueba si la unidad que indican las instrucciones son caracteres o bytes.

Si no se indica la unidad, escribe caracteres de uno en uno en un campo que muestre un contador y fíjate en cuánto sube el número.

Aumento al añadir A / 字 / 😀 Unidad que se cuenta
1 / 1 / 1 Caracteres o puntos de código
1 / 1 / 2 Unidades de código UTF-16
1 / 3 / 4 Bytes en UTF-8
1 / 2 / otro valor Un sistema antiguo de 2 bytes

Para distinguir caracteres de puntos de código, usa 👍🏽: si el número sube 1, son caracteres; si sube 2, son puntos de código.

Cómo contarlo tú mismo

Pega el código siguiente en la consola de las herramientas para desarrolladores del navegador y verás los cuatro valores. Cambia solo la cadena de la primera línea.

const s = '👨‍👩‍👧';
[...new Intl.Segmenter().segment(s)].length; // 1: caracteres
[...s].length;                               // 5: puntos de código
s.length;                                    // 8: unidades de código UTF-16
new TextEncoder().encode(s).length;          // 18: bytes en UTF-8

Para comprobarlo sin código, pega tu texto en las herramientas relacionadas de abajo. Muestran a la vez el número de caracteres, el de bytes en UTF-8 y el de bytes con la regla de 2 bytes.

Fuentes