Quitar el Markdown: lo que el texto plano pierde, y en qué se equivoca una regex
Publicado el 3/6/2025 · 13 min de lectura · Herramientas de texto e idioma
Daniel Okonkwo — Desarrollador front-end y redactor de Tecnología en OneKitly
Rendimiento web · Formatos de archivo
Verificado con 5 fuentes
El markdown guarda significado en su puntuación, así que borrar la sintaxis borra información. Pasa un limpiador ingenuo por un informe corto y las pérdidas son concretas: [desglose completo](https://example.com/t3.pdf) se vuelve «desglose completo» y el destino ha desaparecido sin rastro de que hubiera un enlace; una lista de dos niveles de regiones y canales sale alineada a la izquierda, de modo que «Tienda: +4 %» y «Tienda: estable» ya no pertenecen a ninguna región; y una tabla se vuelve una fila de palabras cuya línea separadora --- sobrevive como texto literal. Debajo hay un segundo problema: el markdown no tiene una especificación única. CommonMark y GitHub Flavored Markdown discrepan — una tabla de barras, el ~~tachado~~, una URL desnuda y una lista de tareas son extensiones GFM y siguen siendo texto literal bajo CommonMark — y un limpiador a base de regex además se equivoca en casos que un analizador resuelve bien. Verificado en Node: un limpiador ingenuo convierte informe_final_v2.txt en informefinalv2.txt y 2 * 3 * 4 en 2 3 4, y edita el interior de un bloque de código cercado, mientras que un analizador CommonMark deja los tres intactos. Conserva el texto del enlace y su destino entre paréntesis, conserva las viñetas y el contenido de los bloques de código tal cual; tira las marcas de énfasis y las almohadillas de título.
Un enlace se convierte en texto con su destino borrado, una lista anidada pierde su jerarquía, una tabla se vuelve una fila de palabras. Luego la mitad técnica: el markdown no tiene una especificación única, y un limpiador a base de regex destroza un nombre de archivo, un signo de multiplicación y el interior de un bloque de código — todo confrontado con un analizador de verdad.
El markdown guarda significado en su puntuación
El markdown parece texto plano con adornos, lo que hace que quitar los adornos parezca gratis. No lo es. Tres de sus construcciones llevan información que no existe en ningún otro sitio del documento. Un enlace contiene dos cosas, el texto y el destino, de las cuales solo una es visible; borra la sintaxis y el destino se va con ella. Una lista anidada contiene una relación — este canal pertenece a esa región — codificada enteramente en la sangría. Una tabla contiene una rejilla, y una rejilla son dos dimensiones escritas en la única dimensión del texto.
Toma un informe corto: un título de nivel dos, una frase con un enlace, y una lista de dos niveles de regiones y sus canales. Pásale un limpiador ingenuo — la lista habitual de patrones para títulos, énfasis, código en línea, enlaces y viñetas — y vuelve alrededor de un tercio más corto, lo que suena a trabajo bien hecho hasta que lo lees. «Ver el desglose completo para las cifras brutas» ya no apunta a ninguna parte. «Tienda: +4 %», «En línea: +11 %» y «Tienda: estable» quedan pegadas a «América del Norte» y «Europa», con la misma sangría, así que nada dice a qué región pertenece cada cifra — y el documento contiene dos líneas «Tienda» distintas que ahora se contradicen.
El markdown no tiene una especificación única
La descripción original de 2004 era un script en Perl y una página de prosa, no una gramática, y cada implementación posterior resolvió las ambigüedades a su manera. CommonMark existe para arreglar eso: es una especificación precisa con una batería de pruebas. GitHub Flavored Markdown es CommonMark más cuatro extensiones, y esas extensiones son justamente las construcciones que la gente supone parte del markdown. Pasa la misma entrada por un analizador con las extensiones GFM activadas y desactivadas y la diferencia se ve.
Una tabla de barras se vuelve una tabla real bajo GFM y sigue siendo un párrafo de barras literales bajo CommonMark. Dos virgulillas alrededor de una palabra se vuelven un borrado bajo GFM y siguen siendo dos virgulillas bajo CommonMark. Una dirección https:// desnuda se vuelve un enlace bajo GFM y sigue siendo texto bajo CommonMark. Y un elemento de lista que empieza con un espacio o una x entre corchetes se vuelve una casilla bajo GFM y conserva sus corchetes literales bajo CommonMark. Cada una de esas construcciones será reconocida o no por tu limpiador, según un ajuste que nadie escribió.
Hay además una construcción común a ambas especificaciones que casi todos los limpiadores a base de regex olvidan: el título setext, una línea de texto subrayada por una hilera de signos igual o de guiones. Tanto CommonMark como GFM la convierten en título. Un limpiador cuya única regla de título reconoce las almohadillas iniciales deja la hilera de signos igual en la salida, como una línea de puntuación.
Tres cosas que una regex falla y un analizador acierta
Primero, el guion bajo en un nombre de archivo. Pasa un limpiador ingenuo por «Abre informe_final_v2.txt y luego archivo_2026_t1.csv» y el patrón de énfasis por guion bajo se come los dos: informefinalv2.txt y archivo2026t1.csv. Un analizador CommonMark deja la frase exactamente igual, porque la especificación dice que un guion bajo dentro de una palabra no abre ni cierra énfasis — esa regla existe precisamente para que el snake_case sobreviva. El asterisco se comporta aquí de forma distinta al guion bajo, lo que ya de por sí es una regla que una regex no sabe expresar.
Segundo, el asterisco como operador. «El área es 2 * ancho * alto, así que 2 * 3 * 4 = 24» sale del limpiador ingenuo como «El área es 2 ancho alto, así que 2 3 4 = 24», con todos los asteriscos desaparecidos y un espacio doble en su sitio. El analizador no lo toca, porque las reglas de flanqueo de CommonMark dicen que un delimitador con espacios a ambos lados no puede abrir ni cerrar énfasis. La regla es precisa, está bien documentada y ocupa tres frases — y es una regla de contexto, que es justo lo que las expresiones regulares no pueden ver por construcción.
Tercero, el enlace por referencia, cuyo destino no está junto al texto en absoluto. Escribe «Ver [el informe completo][inf] para más detalles» y pon «[inf]: https://example.com/informe-2026.pdf» al final del documento: un analizador reúne los dos en un solo enlace. El patrón de enlace del limpiador ingenuo espera un paréntesis, no encuentra nada y deja tanto los corchetes en la frase como la línea de definición al pie del documento, de modo que la salida es peor que la entrada en dos sitios a la vez. Nada de eso se arregla añadiendo un patrón más: resolver un enlace por referencia exige mantener un estado a escala del documento, que es justamente lo que es un analizador.
Bloques de código: el contenido debe sobrevivir intacto
Un bloque de código es el único lugar de un documento markdown donde la sintaxis markdown no es sintaxis markdown. Su razón de ser es contener caracteres que deben reproducirse exactamente, y esos caracteres incluyen habitualmente asteriscos, guiones bajos y almohadillas. Dale a un limpiador ingenuo un bloque de Python cercado que contenga def f(*args), una línea de comentario que empiece por almohadilla y un identificador escrito con guiones bajos: comete tres errores distintos. Se come una de las tres comillas invertidas de la cerca con su patrón de código en línea, quita los guiones bajos del identificador, y deja el resto de la cerca en la salida. Un analizador marca el bloque entero como código y no mira dentro.
El bloque de código sangrado es la misma trampa sin cerca visible. Cuatro espacios al principio de línea crean un bloque de código, tanto en CommonMark como en GFM: un limpiador que solo conoce las comillas invertidas reescribirá alegremente el contenido. Un identificador escrito a_b_c sale como abc, y un comentario que empieza por almohadilla queda a merced de la regla de títulos. Si escribes tú el limpiador, detecta primero las zonas de código y enmascáralas, aplica luego todas las demás reglas a lo que quede, y después vuelve a poner las zonas enmascaradas tal cual. Es una pasada más, y elimina una clase entera de fallos.
Qué conservar, qué tirar
La regla útil no es «quitar la sintaxis» sino «quitar la sintaxis que solo llevaba formato, y reescribir la que llevaba información». Tira las almohadillas de título y los subrayados setext, las marcas de énfasis, las cercas de código, los signos de cita y las reglas horizontales: ninguno dice nada que no digan las palabras. Reescribe el resto. Un enlace se convierte en su texto seguido de su destino entre paréntesis. Una viñeta se convierte en un carácter de viñeta, y la sangría se queda, porque ahí es donde vive la jerarquía. Una tabla se convierte en una línea por fila con un separador visible entre celdas, y la fila de alineación desaparece. Una imagen se convierte en su texto alternativo, la única parte de ella que fueron palabras alguna vez.
El mismo documento tratado así conserva todo lo que la pasada ingenua había perdido. El título sigue siendo una línea de palabras, la frase sigue llevando su destino entre paréntesis, y la lista de dos niveles sigue teniendo dos, así que las cuatro cifras siguen perteneciendo a las dos regiones. Es más largo que la salida ingenua, y esa longitud extra es exactamente la información que la salida ingenua borró.
Vale la pena conocer un atajo, con la advertencia que lo acompaña. Si ya dispones de un analizador markdown de verdad, la ruta correcta más corta hacia el texto plano es renderizar el markdown a HTML y luego extraer el texto de ese HTML, porque el analizador ya ha resuelto por ti los enlaces por referencia, los bloques de código y las reglas de flanqueo. La advertencia: la segunda mitad de esa ruta es un problema propio — quitar HTML tiene su propio orden de operaciones, tratado en el artículo compañero de esta serie, y hacerlo con una segunda regex reintroduce justo la clase de error de la que acabas de escapar.
| Construcción | Salida del limpiador regex | Lo que hace un analizador | Conservar o tirar |
|---|---|---|---|
| Enlace en línea [texto](url) | Texto conservado, destino borrado | Texto y destino ambos disponibles | Conservar ambos: el texto y luego la URL entre paréntesis |
| Guiones bajos en un nombre de archivo: informe_final_v2.txt | informefinalv2.txt | Sin cambios: los guiones bajos dentro de una palabra no son énfasis | Conservar |
| Asterisco como signo de multiplicación: 2 * 3 * 4 | 2 3 4 | Sin cambios: un delimitador con espacio a ambos lados no abre nada | Conservar |
| Bloque de código cercado | Se come una comilla invertida de cerca y edita el código de dentro | Contenido conservado tal cual | Tirar la cerca, conservar cada carácter de dentro |
| Enlace por referencia [texto][ref] | Los corchetes sobreviven y la línea de definición también | Destino resuelto desde la definición situada en otro sitio | Conservar texto y destino, tirar la línea de definición |
| Tabla de barras verticales | Una fila de palabras, con la línea --- sobreviviendo como texto | Filas y celdas, y solo bajo GFM — CommonMark no tiene tablas | Conservar las celdas con un separador, tirar la fila de alineación |
Preguntas frecuentes
- ¿Adónde va la URL del enlace cuando se quita el markdown?
- A ninguna parte, en la mayoría de los limpiadores. El patrón habitual sustituye toda la construcción [texto](url) por el texto capturado: «Ver el [desglose completo](https://example.com/t3.pdf)» se vuelve «Ver el desglose completo», una frase que promete un destino que ya no tiene. Si el texto plano es para una persona, conserva el destino entre paréntesis tras el texto; si alimenta un índice de búsqueda, conserva el texto y guarda la URL en un campo aparte. Borrarla en silencio es la única opción sin caso de uso.
- ¿Por qué al quitar el markdown se destrozaron mis nombres de archivo?
- Porque el limpiador tomó los guiones bajos por énfasis. Un patrón formado por un guion bajo, una captura perezosa y otro guion bajo reconoce el centro de informe_final_v2.txt y borra ambos guiones, dando informefinalv2.txt. CommonMark evita eso a propósito: un guion bajo dentro de una palabra nunca abre ni cierra énfasis, regla que mantiene intactos los identificadores snake_case y los nombres de archivo. Un limpiador que se equivoca aquí no implementa markdown; implementa una suposición sobre markdown.
- ¿Quitar el markdown es lo mismo que convertir a HTML y quitar las etiquetas?
- Es una buena ruta, y no una ruta idéntica. Renderizar a HTML con un analizador de verdad resuelve correctamente los enlaces por referencia, los bloques de código y las reglas de énfasis, que es la mayor parte de la dificultad. Pero el segundo paso tira justo lo que querías conservar, salvo que te ocupes de ello: el href de un ancla, el alt de una imagen, las fronteras de celda de una tabla. Extrae esos atributos a propósito antes de tomar el contenido textual, y recuerda que quitar HTML tiene su propio problema de orden de operaciones, tratado aparte en esta serie.
- ¿Qué le pasa a una tabla?
- Bajo un limpiador ingenuo se vuelve una fila de palabras: las barras verticales se convierten en espacios y la fila de alineación de guiones sobrevive como una línea de puntuación, de modo que el lector recibe cuatro columnas de números sin nada que diga cuál es cuál. Bajo un analizador es una rejilla que puedes volver a dibujar: una línea por fila, celdas unidas por un separador visible, fila de alineación descartada. Fíjate en que una tabla ni siquiera es markdown en sentido estricto: es una extensión GFM, y bajo CommonMark a secas esas líneas de barras son un párrafo corriente.
- ¿Necesita el limpiador conocer las extensiones GFM?
- Si tus documentos vienen de un alojamiento de código, un gestor de incidencias o una herramienta de chat, sí. Las tablas, el tachado, los enlaces automáticos sobre URL desnuda y las listas de tareas son extensiones GFM, y un limpiador solo-CommonMark deja su sintaxis en la salida como caracteres literales: dos virgulillas alrededor de una palabra, corchetes alrededor de un espacio, filas de barras. El error inverso también existe: aplicar reglas GFM a un documento escrito para un renderizador CommonMark estricto convierte un párrafo de barras en una tabla que su autor nunca escribió. Elige el sabor según la fuente, y déjalo escrito junto al código.
- ¿Hay que conservar o quitar los bloques de código?
- Conserva su contenido y quita solo la cerca. Lo que nunca debe ocurrir es la opción intermedia, en la que la cerca desaparece y luego las reglas de énfasis, títulos y código en línea pasan sobre el código de dentro: eso produce un texto que parece código sin ser el código que se escribió, lo cual es peor que cualquiera de los dos extremos. Si tu texto plano alimenta un resumidor o un índice de búsqueda y el código solo añade ruido, elimina el bloque entero y deja una marca en su lugar. Editar el interior de un bloque de código es lo único sin lectura defendible.
Artículos que podrían interesarte
Todas las guías →Herramientas relacionadas
Fuentes
- CommonMark — CommonMark Specification — emphasis and strong emphasis, code spans, fenced and indented code blocks, link reference definitions
- GitHub — GitHub Flavored Markdown Spec — tables, task list items, strikethrough and autolink extensions
- IETF — RFC 7763 and RFC 7764 — the text/markdown media type and known variants
- WHATWG — HTML Standard — parsing and the text content of elements
- MDN Web Docs — Node.textContent and the difference between text content and rendered text
¿Has detectado un error en este artículo?