Ir al contenido
OneKitly

Dónde puede cortarse una línea: el algoritmo Unicode detrás de cada párrafo ajustado

Publicado el 1/7/2025 · 13 min de lectura · Herramientas de texto e idioma

Daniel Okonkwo

Daniel OkonkwoDesarrollador front-end y redactor de Tecnología en OneKitly

Rendimiento web · Formatos de archivo

Verificado con 5 fuentes

Ver perfil
En resumen

Cortar texto partiendo por los espacios es incorrecto en la mayoría de los sistemas de escritura, y Unicode especifica el procedimiento correcto: UAX #14, el algoritmo de corte de línea. Asigna a cada punto de código una clase de corte y luego aplica reglas numeradas a cada par de clases. Un espacio duro U+00A0 y un espacio fino duro U+202F son de clase GL, pegamento, y el corte después de ellos está prohibido: una implementación conforme no encontró ninguna oportunidad de corte dentro de cost·100·USD escrito con espacios duros, donde la versión ordinaria tiene dos. Un guion blando U+00AD es de clase BA, así que el corte ahí está permitido y el guion solo se hace visible si se toma. Un espacio de ancho cero U+200B es de clase ZW: permite el corte y no muestra nada, que es como se ajusta un texto sin espacio alguno. Un guion duro U+2011 es pegamento, mientras que el guion-menos ordinario U+002D es de clase HY y sí permite cortar tras él. El japonés no necesita espacios: cada ideograma es de clase ID y el corte se permite entre casi cualquier par. El tailandés es de clase SA y no tiene ninguna sin análisis por diccionario.

«Cortar en los espacios» falla en la mayoría de los sistemas de escritura. UAX #14 da a cada carácter una clase de corte de línea; buscamos las nuestras en Unicode 17.0.0 y ejecutamos una implementación conforme sobre espacios duros, guiones blandos, espacios de ancho cero, URL, japonés y tailandés.

«Cortar en los espacios» describe una sola familia de escrituras

Corta por el espacio y una frase japonesa vuelve como un solo token: 日本語のテキストは、空白で区切らない no tiene ningún espacio, ni tampoco su equivalente tailandés. Ambas se ajustan perfectamente en pantalla, porque la regla no habla de espacios. Pasa una implementación conforme sobre esa frase japonesa: informa de dieciséis oportunidades de corte en dieciocho caracteres — una entre casi cada par — negándose a cortar antes de la coma ideográfica o del punto. La misma implementación sobre la frase tailandesa informa de cero, por una razón a la que volvemos más abajo.

El caso de la escritura latina tampoco es seguro. Escrito con espacios ordinarios, cost 100 USD ofrece dos oportunidades de corte; escrito con espacios duros U+00A0 no ofrece ninguna, y un ajustador que corta por espacios lo devuelve como un único token largo que no puede partir. Los caracteres se ven idénticos. La diferencia vive en los puntos de código, y cualquier ajustador que lea solo las formas producirá una línea demasiado larga o un corte tipográficamente incorrecto.

UAX #14: una clase por carácter y luego reglas sobre pares

La base de datos de caracteres Unicode incluye un fichero LineBreak.txt que asigna una clase de dos letras a cada punto de código. Analizamos la versión actual — LineBreak-17.0.0.txt, de 29 de julio de 2025 — y define 49 clases distintas en 3 654 rangos. La mayor con diferencia es ID, ideográfica, con 172 561 puntos de código; AL, alfabética ordinaria, cubre 26 954; las interesantes son diminutas, GL con 41 puntos de código, QU con 39, WJ con 2 y ZW con exactamente 1. El algoritmo recorre luego la cadena y consulta una regla para cada par de clases adyacentes.

Las reglas están numeradas y divididas en dos grupos. Unas pocas no son modificables: una implementación no puede cambiarlas y seguir reclamando conformidad. Las demás, desde LB12a en adelante, son un valor por defecto razonable que una implementación puede mejorar. LB12 prohíbe cortar tras el pegamento. LB13 lo prohíbe antes del corchete de cierre, el signo de exclamación, la barra y la clase de puntuación de cierre, incluso tras espacios. LB18 permite cortar tras un espacio. LB21 lo prohíbe antes de un guion o de un carácter de corte posterior. Cuando los pares chocan, gana la regla de número más bajo, y por eso la especificación es una lista y no una tabla.

Cinco caracteres invisibles que deciden dónde caen las líneas

El espacio duro y el espacio fino duro son ambos de clase GL. Ocupan ancho y prohíben el corte, que es lo que mantiene un número con su unidad y un tratamiento con su apellido. El unificador de palabras U+2060 es de clase WJ y va más lejos: ni corte antes ni después, y ningún ancho. El espacio de ancho cero U+200B es lo contrario: clase ZW, sin ancho, corte permitido. Y el guion blando U+00AD es de clase BA, un carácter de corte posterior que no representa nada hasta que se usa el corte, momento en que el motor de renderizado aporta un guion.

La prueba del guion blando hace concreto el comportamiento. Tomamos un compuesto alemán de 63 caracteres e insertamos cinco guiones blandos: longitud almacenada 68, longitud visible 63. Con ancho 99 se muestra como una sola palabra sin cortar de 63 caracteres. Con ancho 30 cabe en tres líneas, con ancho 20 en cuatro, y el guion aparece solo al final de cada línea cortada — nunca en medio. Quita los guiones blandos y la misma palabra con ancho 20 vuelve como una sola línea de 63 caracteres que desborda, porque no hay ningún sitio donde el algoritmo tenga permitido cortar.

El mismo truco rescata escrituras que el algoritmo no sabe segmentar por sí solo. El tailandés es de clase SA, dependiente de contexto complejo, y UAX #14 dice claramente que las series de estos caracteres exigen análisis morfológico, que sin él no se hallará ninguna oportunidad de corte y que una implementación que carezca de él debería tratarlos como letras ordinarias. Nuestra implementación informó de cero oportunidades en una frase tailandesa de 23 caracteres. Insertar tres espacios de ancho cero en las fronteras de palabra produjo exactamente tres — invisibles en el texto representado, decisivas para el ajuste.

Las URL se cortan en sitios que no elegiste

Dale una URL al algoritmo y la respuesta es más interesante que «en ningún sitio» o «en cualquiera». Nuestra dirección de prueba produjo siete oportunidades de corte: tras la doble barra, tras cada barra simple, tras cada guion del camino y tras el signo de interrogación. Nunca antes de una barra — LB13 lo prohíbe de plano, incluso tras un espacio — y nunca en los puntos del nombre de host, porque el punto es de clase IS y LB15d rechaza cortar antes de él. El resultado es que una URL ajustada conserva sus separadores al final de la línea, donde el lector ve que la línea continúa.

El peligro es lo que hagas con ese corte. Un ajuste blando es una decisión de visualización: la cadena en memoria no cambia, y recomponer el párrafo a otro ancho solo mueve el corte. Un ajuste duro inserta un carácter de salto de línea real en los datos. Cortamos duramente la URL de prueba a 24 columnas; volviendo a unir las líneas sin nada, la URL vuelve byte a byte, pero uniéndolas con un espacio — que es lo que hará un cliente de correo, una aplicación de chat o un copiado descuidado — se obtienen cuatro fragmentos separados por espacios, y el enlace está muerto. Nunca cortes duramente algo que deba seguir siendo un solo token.

El ajuste duro destruye la recomposición — medido

Ajustamos un párrafo a 32 columnas, guardamos el resultado con saltos de línea reales y luego lo reajustamos a 20 sin deshacerlo antes. La salida fueron siete líneas, varias mucho más cortas de 20 caracteres, porque cada línea dura se reajustó aislada y los finales irregulares no pudieron rellenarse. Deshaciéndolo primero — uniendo las líneas con un espacio — y ajustando después a 20 salieron cinco líneas llenas. Esa diferencia es el coste del ajuste duro: el texto ya no sabe qué finales de línea eran del autor y cuáles del renderizador anterior.

De ahí viene también el famoso ancho fijo del correo electrónico, y el número que se cita no es el de la especificación. La RFC 5322 dice que cada línea NO DEBE superar los 998 caracteres y NO DEBERÍA superar los 78, sin contar el CRLF; los 998 existen porque implementaciones de transporte rechazan más de 1 000 caracteres por línea, y los 78 porque el software de visualización destroza cualquier cosa más larga. La RFC 3676, que define format=flowed, repite los 78 y explica que no son 79 ni 80 porque la última columna suele reservarse a un indicador de corte. Los 72 que usaban de verdad los clientes de correo son una convención encima, que deja sitio para varios niveles de marcas de cita en las respuestas.

Format=flowed es el compromiso que hizo soportable el correo de ancho fijo: una línea terminada en un espacio antes de su CRLF es un corte blando que el lector puede recomponer, mientras que una línea sin ese espacio es la del autor. Es texto ordinario para un cliente que jamás ha oído hablar de ello y texto recomponible para uno que sí. La lección general va más allá del correo: guarda el párrafo, no las líneas, y deja que el último renderizador decida dónde caen los cortes.

Lo que CSS te deja cambiar y lo que no

CSS Text Level 3 expone el algoritmo mediante cinco propiedades, y conviene conocer sus conjuntos de valores exactos. white-space admite normal, pre, nowrap, pre-wrap, break-spaces o pre-line, y por defecto vale normal. word-break admite normal, keep-all, break-all o break-word. line-break admite auto, loose, normal, strict o anywhere, y controla con cuánta severidad se aplica el algoritmo a la puntuación de Asia Oriental. hyphens admite none, manual o auto, con manual por defecto. overflow-wrap admite normal, break-word o anywhere. La especificación cita UAX #14 por su nombre para las clases subyacentes.

Dos detalles de esa lista son fáciles de equivocar. Como hyphens vale manual por defecto, los guiones blandos ya presentes en tu texto se respetan sin ningún CSS — y hyphens: none los suprime, pero la especificación dice explícitamente que eso no suprime las oportunidades de corte que ofrecen caracteres visibles como U+002D y U+2010. Y overflow-wrap: anywhere es un último recurso, no un arreglo: permite cortar en cualquier carácter cuando una palabra desbordaría, lo que para el compuesto de 63 caracteres de arriba significa un corte en medio de un morfema donde un guion blando habría dado uno correcto.

Los espacios tipográficos no son decoración

La puntuación francesa es la demostración más clara de que estos puntos de código hacen trabajo real. Ajustamos dos veces la misma frase francesa: una con espacios ordinarios en todas partes, otra con los espacios finos duros correctos ante la puntuación alta y un espacio duro ante la unidad. La primera versión ofrecía catorce oportunidades de corte, incluida una entre un número y su grupo de millares y otra antes de una comilla angular de cierre. La segunda ofrecía nueve, y ninguna separaba un signo de la palabra a la que pertenece. Mismo texto visible, conjunto distinto de cortes legales.

El mismo razonamiento va mucho más allá del francés. Un número y su unidad, una abreviatura y el nombre que la sigue, una cifra y un signo de porcentaje, un ordinal y su sustantivo: todos se leen mal si un corte cae entre ellos, y todos se arreglan con un carácter invisible en vez de con una regla en el ajustador. Nuestra herramienta de ajuste deja intactos esos caracteres, que es el único comportamiento correcto: limpiarlos, como hace un normalizador de espacios ingenuo, da en silencio al renderizador permiso para cortar donde el autor lo prohibía.

Clases de corte consultadas en LineBreak-17.0.0.txt (Unicode 17.0.0, con fecha 29/07/2025) y oportunidades de corte producidas por una implementación UAX #14 en Node 26.3.0. «¿Corte después?» describe el comportamiento en texto corrido; el algoritmo completo tiene unas cuarenta reglas numeradas y algunas dependen de la clase vecina.
CarácterPunto de códigoClase¿Corte después?¿Visible?
EspacioU+0020SPSí, como ancho en blanco
Espacio duroU+00A0GLNoSí, como ancho en blanco
Espacio fino duroU+202FGLNoSí, más estrecho
Guion-menosU+002DHYSiempre
Guion duroU+2011GLNoSiempre
Guion blandoU+00ADBASolo si se toma el corte
Espacio de ancho ceroU+200BZWNunca
Unificador de palabrasU+2060WJNo, ni antes de élNunca
Barra en una URLU+002FSYSí después, nunca antes (LB13)Siempre
Ideograma CJKU+4E00 y 172 560 másIDSí, entre casi cualquier parSiempre
Letra tailandesaU+0E01SASolo con análisis por diccionarioSiempre
Ajustar texto (word wrap)Ajusta las líneas largas a un ancho máximo sin cortar palabras.Probar la herramienta

Preguntas frecuentes

¿Por qué mi texto se corta en medio de un número?
Porque el separador de grupos es un espacio ordinario, clase SP, y la regla LB18 permite cortar tras cualquier espacio. Sustitúyelo por un espacio duro U+00A0 o un espacio fino duro U+202F, ambos de clase GL, y la oportunidad desaparece: nuestra frase francesa pasó de catorce oportunidades de corte a nueve con los espacios tipográficos, y ninguna de las nueve restantes caía dentro de un número.
¿Qué diferencia hay entre un guion blando y un espacio de ancho cero?
Ambos permiten cortar y ninguno es visible hasta que se usa, pero el guion blando U+00AD deja un guion cuando el corte se toma y el espacio de ancho cero U+200B no deja nada. Usa el guion blando dentro de una palabra, donde el guion es la tipografía correcta; usa el espacio de ancho cero entre unidades que no deben ganar guion, como partes de un identificador largo o de una URL, y en escrituras que nunca usan guion.
¿Debo cortar duramente el texto a 72 u 80 caracteres?
Solo cuando lo exija un protocolo. La RFC 5322 fija un límite duro de 998 caracteres por línea y una recomendación de 78; los 72 que usaban los clientes de correo dejan sitio para las marcas de cita en las respuestas y son una convención, no una especificación. El ajuste duro cuesta la recomposición: reajustar a 20 un párrafo ya cortado duramente a 32 columnas dio siete líneas irregulares, frente a cinco líneas llenas si lo deshacíamos primero.
¿Cómo evito que una URL larga desborde su contenedor?
No insertando saltos de línea. El algoritmo ya ofrece cortes tras la doble barra, tras cada barra y tras cada guion — siete oportunidades en nuestra dirección de prueba — así que un ajuste blando suele bastar. Si el contenedor es más estrecho que la serie irrompible más larga, añade overflow-wrap: anywhere para ese elemento o inserta espacios de ancho cero, que no cuestan nada al copiar. Una URL cortada duramente y reunida con un espacio ya no es una URL.
¿El algoritmo sabe dónde acaban las palabras tailandesas?
No, y UAX #14 lo dice. Los caracteres tailandeses, lao y jemer son de clase SA, dependiente de contexto complejo, y el anexo indica que sus series exigen análisis morfológico, que sin él no se hallará ninguna oportunidad de corte y que una implementación que carezca de él debería tratarlos como letras ordinarias. Nuestra implementación conforme halló cero oportunidades en una frase tailandesa de 23 caracteres, y exactamente tres tras insertar tres espacios de ancho cero.

Artículos que podrían interesarte

Todas las guías
GuíaConvertir entre formatos de lista sin perder datos: las reglas de comillas que nadie leePasar de una lista con saltos de línea a una lista con comas es trivial hasta que un elemento contiene una coma. Las reglas de comillas de la RFC 4180, por qué un campo CSV puede contener un salto de línea, por qué las hojas de cálculo europeas usan el punto y coma, y qué le hace un elemento vacío a la ida y vuelta — cada caso ejecutado e impreso.ExplicaciónFrecuencia de palabras y ley de Zipf: contamos seis libros en seis lenguas y ajustamos la pendienteLa palabra de rango n aparece unas 1/n veces respecto a la primera. Contamos seis libros de dominio público, imprimimos rango × frecuencia, ajustamos log frecuencia contra log rango y obtuvimos pendientes entre -1,02 y -1,08 en las seis lenguas — y los dos sitios donde la ley falla.ExplicaciónContar palabras es ambiguo, y cada herramienta responde distintoUn recuento de palabras es una definición, no una medida. Contamos el mismo párrafo de cuatro formas y obtuvimos 25, 28, 33 y 38; luego contamos 50 000 caracteres de prosa corriente y obtuvimos acuerdo dentro del 4,5 %. La diferencia se debe por completo a compuestos, cifras y URL.ExplicaciónQué miden realmente los índices de legibilidad (y las tres cosas que no ven)El Flesch Reading Ease y el nivel Flesch-Kincaid cuentan sílabas y longitud de frase. Nada más. Las dos fórmulas completas, un pasaje puntuado de principio a fin y el truco de la coma que regala 3,9 cursos sin cambiar una palabra.ExplicaciónLa densidad de palabras clave es una métrica muerta, y esto es lo que la sustituyóLa densidad contaba ocurrencias porque la recuperación contaba ocurrencias. TF-IDF, luego BM25 con su curva de saturación, luego los embeddings la sustituyeron. Aquí va la misma página de 800 palabras puntuada de tres formas, y por qué las tres discrepan.TutorialFiltrar líneas por un patrón sin línea de comandosEsto es grep para quien no usa grep, con una diferencia importante: la búsqueda es una subcadena literal, así que una expresión regular de verdad devuelve un cuadro vacío y ningún error. Cada afirmación se comprobó ejecutando la herramienta.

Herramientas relacionadas

Fuentes

¿Has detectado un error en este artículo?