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.
Aes 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
maxlengthde HTML ylengthde JavaScript cuentan unidades de código UTF-16. En un campo conmaxlength10 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 en4oKs(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
- RFC 3629: UTF-8, a transformation format of ISO 10646 — (en inglés)
- Unicode Standard Annex #29: Unicode Text Segmentation — (en inglés)
- Unicode Technical Standard #51: Unicode Emoji — (en inglés)
- String: length – MDN Web Docs — (en inglés)
- Intl.Segmenter – MDN Web Docs — (en inglés)
- HTML attribute maxlength – MDN Web Docs — (en inglés)
- Encoding Standard – WHATWG — (en inglés)
- PostgreSQL Documentation: Character Types — (en inglés)