Ir al contenido
OneKitly

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

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

Rendimiento web · Formatos de archivo

Verificado con 5 fuentes

Ver perfil
En resumen

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.

Seis construcciones markdown pasadas por un limpiador a base de regex y por un analizador CommonMark, ambos ejecutados en Node 26.3. La columna del analizador es lo que un lector esperaría; la columna de la regex es lo que produce una lista de patrones.
ConstrucciónSalida del limpiador regexLo que hace un analizadorConservar o tirar
Enlace en línea [texto](url)Texto conservado, destino borradoTexto y destino ambos disponiblesConservar ambos: el texto y luego la URL entre paréntesis
Guiones bajos en un nombre de archivo: informe_final_v2.txtinformefinalv2.txtSin cambios: los guiones bajos dentro de una palabra no son énfasisConservar
Asterisco como signo de multiplicación: 2 * 3 * 42 3 4Sin cambios: un delimitador con espacio a ambos lados no abre nadaConservar
Bloque de código cercadoSe come una comilla invertida de cerca y edita el código de dentroContenido conservado tal cualTirar la cerca, conservar cada carácter de dentro
Enlace por referencia [texto][ref]Los corchetes sobreviven y la línea de definición tambiénDestino resuelto desde la definición situada en otro sitioConservar texto y destino, tirar la línea de definición
Tabla de barras verticalesUna fila de palabras, con la línea --- sobreviviendo como textoFilas y celdas, y solo bajo GFM — CommonMark no tiene tablasConservar las celdas con un separador, tirar la fila de alineación
Eliminar MarkdownElimina el formato Markdown para obtener texto plano.Probar la herramienta

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
TutorialLimpiar texto desordenado: el orden de las operaciones que sí importaQuitar etiquetas antes de decodificar entidades, recortar antes de deduplicar, colapsar los espacios al final. Tres órdenes ejecutados en Node, un pipeline de nueve pasos en la secuencia correcta y los caracteres invisibles — U+00A0, U+200B, U+FEFF — que sobreviven a cualquier limpieza ingenua.GuíaQuitar HTML con criterio: lo que un eliminador de etiquetas puede y no puede hacerQuitar etiquetas y sanear HTML son dos trabajos distintos. Un fragmento real pasado por una regex ingenua y por un limpiador consciente del formato, con el contenido de script y style, los cortes de bloque, los comentarios, los CDATA y el orden de las entidades mostrados en la salida.GuíaFormatear números para seis idiomas: separadores, moneda y la vuelta al valor1.234,56 y 1,234.56 son el mismo número, y confundirlos cambia el valor que lee un lector. Ejecutamos Intl.NumberFormat para las seis locales del sitio e imprimimos cada separador —incluido el invisible que usa el francés— y luego medimos por qué parseFloat no puede deshacer nada de eso.ExplicaciónLos emoji son más difíciles de lo que parecen: por qué «basta con quitarlos» no tiene respuesta en una líneaUn emoji visible puede valer un punto de código o catorce unidades UTF-16. Lanzamos tres expresiones regulares populares sobre una frase real y cada una falló de otra forma; una borró los dígitos. Aquí está el porqué, qué propiedad Unicode responde a qué pregunta, y la regla de grupos de grafemas que sí funciona.GuíaLas listas de tareas en Markdown y qué se renderiza de verdad en cada sitioLas listas de tareas no están en CommonMark. Son una extensión de GitHub Flavored Markdown, y por eso el mismo fichero muestra casillas en un sitio y corchetes literales en otro. La regla exacta del marcador, qué hace el anidamiento y una tabla de qué es CommonMark, qué es GFM y qué no es ninguno — comprobado contra ambas especificaciones y cuatro renderizadores.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.

Herramientas relacionadas

Fuentes

¿Has detectado un error en este artículo?