Los caracteres Unicode invisibles no ocupan espacio visible, pero siguen formando parte de la cadena. Los más habituales son los espacios de ancho cero, los guiones suaves, las marcas de orden de bytes, los espacios de no separación y las marcas direccionales. Cada uno tiene una función tipográfica legítima, y cada uno rompe búsquedas, validaciones, análisis y deduplicación cuando sobrevive a un copiar y pegar.
Los caracteres con los que te vas a encontrar
| Carácter | Punto de código | Función | Qué rompe |
|---|---|---|---|
| Espacio de ancho cero | U+200B | Sugiere un punto de corte de línea | Búsquedas, comparación de cadenas |
| No concatenador de ancho cero | U+200C | Impide que las letras se unan | Ligaduras, texto en árabe e indio |
| Concatenador de ancho cero | U+200D | Obliga a las letras a unirse | Secuencias de emojis, longitud del texto |
| Guion suave | U+00AD | Guion visible solo al cortar línea | Búsquedas, texto copiado |
| Espacio de no separación | U+00A0 | Espacio que nunca se corta | Recortes, análisis de CSV, código |
| Espacio fino de no separación | U+202F | Espacio estrecho antes de signos | Lo mismo que el anterior |
| Marca de orden de bytes | U+FEFF | Marca la codificación al inicio del archivo | Primera columna de un CSV, análisis de JSON |
| Concatenador de palabras | U+2060 | Impide un corte, sin ancho | Búsquedas |
| Marca de izquierda a derecha | U+200E | Fija la dirección del texto | Orden de visualización, recortes |
| Marca de derecha a izquierda | U+200F | Fija la dirección del texto | Orden de visualización |
| Anulación RTL | U+202E | Fuerza la visualización de derecha a izquierda | Falsificación de nombres de archivo |
| Separador de línea | U+2028 | Salto de línea Unicode | Cadenas literales de JavaScript |
| Separador de párrafo | U+2029 | Salto de párrafo Unicode | JSON en analizadores antiguos |
Todos estos caracteres tienen una función real. El problema nunca es su existencia, sino que viajan silenciosamente entre sistemas que los tratan de forma distinta.
De dónde vienen
- Copiar de páginas web. Los sitios insertan espacios de ancho cero para controlar dónde se cortan las cadenas largas, y guiones suaves para el texto justificado. Ambos acompañan al texto al copiarlo.
- Procesadores de texto. Los espacios de no separación se insertan automáticamente para mantener un número junto a su unidad, o un nombre en una sola línea.
- Extracción de PDF. Copiar desde un PDF suele producir guiones suaves donde el maquetador partió palabras entre líneas, además de espacios de no separación procedentes del espaciado justificado.
- Interfaces de chat con IA. El resultado se renderiza como HTML, así que al copiar se arrastra cualquier carácter de espaciado que haya usado ese formato, normalmente U+00A0 y U+202F.
- Conversiones de codificación. Una marca de orden de bytes aparece cuando un archivo se guarda como UTF-8 con BOM, algo habitual en Windows.
- Inserción deliberada. Los caracteres de ancho cero se han usado para marcar documentos y rastrear filtraciones, y para colar texto a través de filtros de palabras clave.
El patrón es constante: entran cuando el texto pasa de un contexto de presentación a un contexto de datos.
Cómo rompen las cosas
La búsqueda falla en silencio. Un espacio de ancho cero dentro de una palabra hace que buscarla no devuelva nada. Nada indica por qué: la palabra está ahí, a la vista.
La validación rechaza entradas correctas. Una dirección de correo con un espacio de no separación al final no pasa la comprobación de formato. El usuario ve una dirección correcta y un mensaje de error sin sentido.
Las comparaciones fallan. "Ada" === "Ada\u200B" es falso. La deduplicación pasa por alto duplicados evidentes; las búsquedas no devuelven nada; las uniones pierden filas.
El recorte no ayuda. La mayoría de las implementaciones de trim() solo eliminan los espacios en blanco ASCII. Un espacio de no separación sobrevive a .trim() en muchos lenguajes, y por eso «ya lo recorté» es un callejón sin salida tan común.
El código no compila. Un espacio de no separación donde debería haber un espacio normal produce un error de sintaxis en una línea que parece perfecta.
Las importaciones de CSV se corrompen. Una marca de orden de bytes se adhiere al primer encabezado, de modo que id se convierte en \uFEFFid y la primera columna deja de mapearse sin aviso.
La longitud es incorrecta. Los concatenadores de ancho cero en las secuencias de emojis hacen que un solo emoji visible sean varios puntos de código, así que los límites de caracteres rechazan entradas que parecen estar dentro del límite.
Los nombres de archivo pueden engañar. U+202E invierte el texto mostrado, así que report\u202Egnp.exe puede mostrarse como reportexe.png. Es un problema de seguridad real, no una curiosidad.
Cómo encontrarlos y eliminarlos
Escanea primero. Pega el texto en el Detector de caracteres invisibles. Indica qué tipos están presentes y cuántos hay de cada uno, y luego los elimina si lo pides. Ver los recuentos primero te dice si te enfrentas a un carácter suelto o a una contaminación sistemática.
En un editor. VS Code resalta la mayoría de los caracteres invisibles por defecto mediante su ajuste unicode-highlight. Para buscar y reemplazar con expresiones regulares, este patrón captura el conjunto habitual:
[\u200B-\u200F\u00AD\uFEFF\u2060\u202A-\u202E\u2028\u2029]
En el código, normaliza en la frontera: cuando llegan los datos, no cuando se usan:
const clean = input
.replace(/[\u200B-\u200F\u2060\uFEFF\u00AD\u202A-\u202E]/g, "")
.replace(/[\u00A0\u202F]/g, " ")
.normalize("NFC")
.trim();
Fíjate en el tratamiento en dos pasos: los caracteres de ancho cero se eliminan, pero los espacios de no separación se convierten en espacios normales; eliminarlos uniría las palabras.
No elimines a ciegas. Los concatenadores de ancho cero son estructurales en las secuencias de emojis y en árabe, persa y muchas escrituras indias, donde quitarlos cambia cómo se renderizan las palabras. Elimina con firmeza en identificadores, claves y código; con cautela en contenido destinado a los usuarios.
Sobre la cuestión concreta de si las herramientas de IA los insertan deliberadamente, consulta ¿oculta ChatGPT caracteres invisibles?. El equivalente visible pero igual de disruptivo se trata en por qué las comillas tipográficas rompen el código.
Preguntas frecuentes
¿Qué es un espacio de ancho cero?
U+200B, un carácter que no ocupa ancho visible pero sigue formando parte de la cadena. Los sitios web lo usan para sugerir dónde puede cortarse una palabra larga. Rompe las búsquedas y las comparaciones de cadenas porque el texto se ve idéntico mientras los bytes subyacentes difieren.
¿Por qué falla mi búsqueda en un texto que claramente está ahí?
Casi con seguridad hay un carácter invisible dentro de la palabra, normalmente un espacio de ancho cero o un guion suave recogido de una página web o un PDF. El texto renderizado coincide con lo que escribiste, pero la cadena subyacente no.
¿trim() elimina los espacios de no separación?
Normalmente no. La mayoría de las implementaciones de trim solo eliminan los espacios en blanco ASCII, así que un espacio de no separación (U+00A0) sobrevive. Por eso una entrada que parece no tener espacios al principio ni al final sigue fallando en la validación tras recortarla.
¿Cómo elimino los caracteres invisibles de un texto?
Pégalo en un detector que los enumere y los elimine, o usa una expresión regular que cubra U+200B–U+200F, U+00AD, U+FEFF, U+2060 y U+202A–U+202E. Elimina los caracteres de ancho cero, pero convierte los espacios de no separación en espacios normales en lugar de borrarlos, o las palabras se unirán.
¿Los caracteres invisibles sirven para algo?
Sí. Los guiones suaves controlan los cortes de palabra en el texto justificado, los espacios de no separación mantienen los valores junto a sus unidades, y los concatenadores de ancho cero son esenciales para las secuencias de emojis y para el árabe, el persa y las escrituras indias. Elimina con firmeza en código e identificadores, y con cautela en prosa.