Retirer le Markdown : ce que le texte brut perd, et ce qu'une regex se trompe à faire
Publié le 03/06/2025 · 14 min de lecture · Outils texte & langage
Daniel Okonkwo — Développeur front-end et rédacteur Tech chez OneKitly
Performance web · Formats de fichiers
Vérifié à partir de 5 sources
Le markdown garde du sens dans sa ponctuation : supprimer la syntaxe supprime de l'information. Passe un nettoyeur naïf sur un court rapport et les pertes sont concrètes : [détail complet](https://example.com/t3.pdf) devient « détail complet » et la destination a disparu sans trace qu'un lien ait existé ; une liste à deux niveaux de régions et de canaux ressort alignée à gauche, si bien que « Boutique : +4 % » et « Boutique : stable » n'appartiennent plus à aucune région ; et un tableau devient une file de mots dont la ligne de séparation --- survit en texte littéral. Dessous se cache un second problème : le markdown n'a pas de spécification unique. CommonMark et GitHub Flavored Markdown divergent — un tableau à barres verticales, le ~~barré~~, une URL nue et une liste de tâches sont des extensions GFM et restent du texte littéral sous CommonMark — et un nettoyeur à base de regex se trompe en plus sur des cas qu'un analyseur traite correctement. Vérifié dans Node : un nettoyeur naïf transforme rapport_final_v2.txt en rapportfinalv2.txt et 2 * 3 * 4 en 2 3 4, et il modifie l'intérieur d'un bloc de code clôturé, là où un analyseur CommonMark laisse les trois intacts. Garde le texte du lien et sa destination entre parenthèses, garde les puces et le contenu des blocs de code tel quel ; jette les marques d'emphase et les dièses de titre.
Un lien devient un texte dont la destination a disparu, une liste imbriquée perd sa hiérarchie, un tableau devient une file de mots. Puis la moitié technique : le markdown n'a pas de spécification unique, et un nettoyeur à base de regex abîme un nom de fichier, un signe de multiplication et l'intérieur d'un bloc de code — le tout confronté à un vrai analyseur.
Le markdown garde du sens dans sa ponctuation
Le markdown ressemble à du texte brut avec des décorations, ce qui donne l'impression que retirer les décorations est gratuit. Ça ne l'est pas. Trois de ses constructions portent une information qui n'existe nulle part ailleurs dans le document. Un lien contient deux choses, le texte et la destination, dont une seule est visible ; supprime la syntaxe et la destination part avec elle. Une liste imbriquée contient une relation — ce canal appartient à cette région — encodée entièrement dans l'indentation. Un tableau contient une grille, et une grille, ce sont deux dimensions écrites dans la seule dimension du texte.
Prends un court rapport : un titre de niveau deux, une phrase contenant un lien, et une liste à deux niveaux de régions et de leurs canaux. Passe-y un nettoyeur naïf — la liste habituelle de motifs pour les titres, l'emphase, le code en ligne, les liens et les puces — et il revient d'environ un tiers plus court, ce qui a l'air d'un travail bien fait jusqu'à ce qu'on le lise. « Voir le détail complet pour les chiffres bruts » ne pointe plus nulle part. « Boutique : +4 % », « En ligne : +11 % » et « Boutique : stable » sont collés à « Amérique du Nord » et « Europe », à la même indentation : plus rien ne dit à quelle région appartient chaque chiffre — et le document contient deux lignes « Boutique » différentes qui se contredisent désormais.
Le markdown n'a pas de spécification unique
La description d'origine de 2004 était un script Perl et une page de prose, pas une grammaire, et chaque implémentation ultérieure a tranché les ambiguïtés à sa manière. CommonMark existe pour corriger cela : c'est une spécification précise assortie d'une suite de tests. GitHub Flavored Markdown, c'est CommonMark plus quatre extensions, et ces extensions sont précisément les constructions que l'on suppose faire partie du markdown. Passe la même entrée dans un analyseur avec les extensions GFM activées puis désactivées, et la différence saute aux yeux.
Un tableau à barres verticales devient un vrai tableau sous GFM et reste un paragraphe de barres littérales sous CommonMark. Deux tildes autour d'un mot deviennent une suppression sous GFM et restent deux tildes sous CommonMark. Une adresse https:// nue devient un lien sous GFM et reste du texte sous CommonMark. Et un élément de liste commençant par une espace ou un x entre crochets devient une case à cocher sous GFM et conserve ses crochets littéraux sous CommonMark. Chacune de ces constructions sera reconnue ou non par ton nettoyeur, selon un réglage que personne n'a écrit.
Il existe aussi une construction commune aux deux spécifications que presque tous les nettoyeurs à regex oublient : le titre setext, une ligne de texte soulignée par une rangée de signes égal ou de tirets. CommonMark comme GFM en font un titre. Un nettoyeur dont la seule règle de titre reconnaît les dièses initiaux laisse la rangée de signes égal dans la sortie, sous forme de ligne de ponctuation.
Trois choses qu'une regex rate et qu'un analyseur réussit
D'abord, le tiret bas dans un nom de fichier. Passe un nettoyeur naïf sur « Ouvre rapport_final_v2.txt puis archive_2026_t1.csv » et le motif d'emphase par tiret bas mange les deux : rapportfinalv2.txt et archive2026t1.csv. Un analyseur CommonMark laisse la phrase exactement telle quelle, car la spécification dit qu'un tiret bas à l'intérieur d'un mot n'ouvre ni ne ferme d'emphase — cette règle existe précisément pour que le snake_case survive. L'astérisque se comporte ici différemment du tiret bas, ce qui est en soi une règle qu'une regex ne sait pas exprimer.
Deuxièmement, l'astérisque comme opérateur. « L'aire vaut 2 * largeur * hauteur, donc 2 * 3 * 4 = 24 » ressort du nettoyeur naïf en « L'aire vaut 2 largeur hauteur, donc 2 3 4 = 24 », tous les astérisques disparus et une double espace à leur place. L'analyseur n'y touche pas, car les règles de flanquement de CommonMark disent qu'un délimiteur entouré de blancs ne peut ni ouvrir ni fermer d'emphase. La règle est précise, bien documentée et longue de trois phrases — et c'est une règle de contexte, ce que les expressions régulières sont structurellement incapables de voir.
Troisièmement, le lien par référence, dont la destination n'est pas du tout à côté du texte. Écris « Voir [le rapport complet][rap] pour les détails » et place « [rap] : https://example.com/rapport-2026.pdf » en bas du document : un analyseur réunit les deux en un seul lien. Le motif de lien du nettoyeur naïf attend une parenthèse, ne trouve rien, et laisse à la fois les crochets dans la phrase et la ligne de définition au pied du document — la sortie est donc pire que l'entrée en deux endroits à la fois. Rien de tout cela ne se répare en ajoutant un motif de plus : résoudre un lien par référence suppose de tenir un état à l'échelle du document, ce qui est précisément la définition d'un analyseur.
Blocs de code : le contenu doit survivre intact
Un bloc de code est le seul endroit d'un document markdown où la syntaxe markdown n'est pas de la syntaxe markdown. Sa raison d'être est de contenir des caractères à reproduire exactement, et ces caractères comportent couramment des astérisques, des tirets bas et des dièses. Donne à un nettoyeur naïf un bloc Python clôturé contenant def f(*args), une ligne de commentaire commençant par un dièse et un identifiant écrit avec des tirets bas : il fait trois erreurs distinctes. Il mange l'un des trois accents graves de clôture avec son motif de code en ligne, il retire les tirets bas de l'identifiant, et il laisse le reste de la clôture dans la sortie. Un analyseur marque tout le bloc comme du code et ne regarde pas à l'intérieur.
Le bloc de code indenté est le même piège sans clôture visible. Quatre espaces en début de ligne créent un bloc de code, en CommonMark comme en GFM : un nettoyeur qui ne connaît que les accents graves réécrira donc joyeusement le contenu. Un identifiant écrit a_b_c ressort en abc, et un commentaire commençant par un dièse est à la merci de la règle des titres. Si tu écris le nettoyeur toi-même, détecte d'abord les zones de code et masque-les, applique ensuite toutes les autres règles à ce qui reste, puis remets les zones masquées telles quelles. C'est une passe de plus, et elle supprime une classe entière d'échecs.
Ce qu'il faut garder, ce qu'il faut jeter
La règle utile n'est pas « retirer la syntaxe » mais « retirer la syntaxe qui ne portait que de la mise en forme, et réécrire celle qui portait de l'information ». Jette les dièses de titre et les soulignements setext, les marques d'emphase, les clôtures de code, les chevrons de citation et les filets horizontaux : aucun ne dit quelque chose que les mots ne disent pas. Réécris le reste. Un lien devient son texte suivi de sa destination entre parenthèses. Une puce devient un caractère de puce, et l'indentation demeure, car c'est là que vit la hiérarchie. Un tableau devient une ligne par rangée avec un séparateur visible entre cellules, et la ligne d'alignement disparaît. Une image devient son texte alternatif, la seule partie d'elle qui ait jamais été des mots.
Le même document traité ainsi conserve tout ce que la passe naïve avait perdu. Le titre reste une ligne de mots, la phrase porte toujours sa destination entre parenthèses, et la liste à deux niveaux en a toujours deux : les quatre chiffres appartiennent encore aux deux régions. C'est plus long que la sortie naïve, et cette longueur supplémentaire est exactement l'information que la sortie naïve avait supprimée.
Un raccourci mérite d'être connu, avec l'avertissement qui l'accompagne. Si un vrai analyseur markdown est déjà disponible, la route correcte la plus courte vers le texte brut consiste à rendre le markdown en HTML puis à extraire le texte de ce HTML, car l'analyseur a déjà résolu pour toi les liens par référence, les blocs de code et les règles de flanquement. L'avertissement : la seconde moitié de cette route est un problème en soi — retirer du HTML obéit à son propre ordre des opérations, traité dans l'article compagnon de cette série, et le faire avec une seconde regex réintroduit exactement la classe de bug que tu venais d'éviter.
| Construction | Sortie du nettoyeur regex | Ce que fait un analyseur | Garder ou jeter |
|---|---|---|---|
| Lien en ligne [texte](url) | Texte gardé, destination supprimée | Texte et destination tous deux disponibles | Garder les deux : le texte, puis l'URL entre parenthèses |
| Tirets bas dans un nom de fichier : rapport_final_v2.txt | rapportfinalv2.txt | Inchangé : les tirets bas à l'intérieur d'un mot ne sont pas de l'emphase | Garder |
| Astérisque comme signe de multiplication : 2 * 3 * 4 | 2 3 4 | Inchangé : un délimiteur entouré d'espaces n'ouvre rien | Garder |
| Bloc de code clôturé | Mange un accent grave de clôture et modifie le code à l'intérieur | Contenu conservé tel quel | Jeter la clôture, garder chaque caractère intérieur |
| Lien par référence [texte][réf] | Les crochets survivent, la ligne de définition aussi | Destination résolue depuis la définition située ailleurs | Garder le texte et la destination, jeter la ligne de définition |
| Tableau à barres verticales | Une file de mots, la ligne --- survivant en texte | Lignes et cellules, et seulement sous GFM — CommonMark n'a pas de tableaux | Garder les cellules avec un séparateur, jeter la ligne d'alignement |
Questions fréquentes
- Où passe l'URL du lien quand on retire le markdown ?
- Nulle part, dans la plupart des nettoyeurs. Le motif courant remplace toute la construction [texte](url) par le texte capturé : « Voir le [détail complet](https://example.com/t3.pdf) » devient « Voir le détail complet », une phrase qui promet une destination qu'elle n'a plus. Si le texte brut s'adresse à un humain, garde la destination entre parenthèses après le texte ; s'il alimente un index de recherche, garde le texte et stocke l'URL dans un champ séparé. La supprimer en silence est la seule option sans cas d'usage.
- Pourquoi le retrait du markdown a-t-il abîmé mes noms de fichiers ?
- Parce que le nettoyeur a pris les tirets bas pour de l'emphase. Un motif fait d'un tiret bas, d'une capture paresseuse et d'un autre tiret bas reconnaît le milieu de rapport_final_v2.txt et supprime les deux tirets, donnant rapportfinalv2.txt. CommonMark s'en garde délibérément : un tiret bas à l'intérieur d'un mot n'ouvre ni ne ferme jamais d'emphase, règle qui préserve les identifiants snake_case et les noms de fichiers. Un nettoyeur qui se trompe là-dessus n'implémente pas markdown ; il implémente une supposition sur markdown.
- Retirer le markdown revient-il à convertir en HTML puis retirer les balises ?
- C'est une bonne route, mais pas une route identique. Rendre en HTML avec un vrai analyseur résout correctement les liens par référence, les blocs de code et les règles d'emphase, ce qui représente l'essentiel de la difficulté. Mais la seconde étape jette précisément ce que tu voulais garder, sauf à s'en occuper : le href d'une ancre, le alt d'une image, les frontières de cellules d'un tableau. Extrais ces attributs délibérément avant de prendre le contenu textuel, et rappelle-toi que retirer du HTML pose son propre problème d'ordre des opérations, traité à part dans cette série.
- Qu'advient-il d'un tableau ?
- Sous un nettoyeur naïf, il devient une file de mots : les barres verticales deviennent des espaces et la ligne d'alignement en tirets survit sous forme de ligne de ponctuation, si bien que le lecteur reçoit quatre colonnes de nombres sans rien qui dise laquelle est laquelle. Sous un analyseur, c'est une grille que tu peux redessiner : une ligne par rangée, cellules jointes par un séparateur visible, ligne d'alignement écartée. Note qu'un tableau n'est même pas du markdown au sens strict : c'est une extension GFM, et sous CommonMark seul, ces lignes de barres forment un paragraphe ordinaire.
- Le nettoyeur doit-il connaître les extensions GFM ?
- Si tes documents proviennent d'un hébergeur de code, d'un suivi de tickets ou d'un outil de discussion, oui. Les tableaux, le barré, les liens automatiques sur URL nue et les listes de tâches sont des extensions GFM, et un nettoyeur strictement CommonMark laisse leur syntaxe en caractères littéraux dans la sortie — deux tildes autour d'un mot, des crochets autour d'une espace, des rangées de barres. L'erreur inverse existe aussi : appliquer les règles GFM à un document écrit pour un rendu CommonMark strict transforme un paragraphe de barres en un tableau que son auteur n'a jamais écrit. Choisis la variante selon la source, et note-le à côté du code.
- Faut-il garder ou retirer les blocs de code ?
- Garde leur contenu et ne retire que la clôture. Ce qui ne doit jamais arriver, c'est l'option intermédiaire, où la clôture disparaît puis les règles d'emphase, de titres et de code en ligne passent sur le code intérieur : cela produit un texte qui ressemble à du code sans être le code écrit, ce qui est pire que les deux extrêmes. Si ton texte brut alimente un résumeur ou un index de recherche et que le code n'y ajoute que du bruit, supprime le bloc entier et laisse un repère à sa place. Modifier l'intérieur d'un bloc de code est la seule chose sans lecture défendable.
Articles qui pourraient t'intéresser
Tous les guides →Outils similaires
Sources
- 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
Tu as repéré une erreur dans cet article ?