Aller au contenu
OneKitly

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

Daniel OkonkwoDéveloppeur front-end et rédacteur Tech chez OneKitly

Performance web · Formats de fichiers

Vérifié à partir de 5 sources

Voir le profil
En bref

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.

Six constructions markdown passées dans un nettoyeur à base de regex et dans un analyseur CommonMark, tous deux exécutés dans Node 26.3. La colonne analyseur est ce qu'un lecteur attend ; la colonne regex est ce que produit une liste de motifs.
ConstructionSortie du nettoyeur regexCe que fait un analyseurGarder ou jeter
Lien en ligne [texte](url)Texte gardé, destination suppriméeTexte et destination tous deux disponiblesGarder les deux : le texte, puis l'URL entre parenthèses
Tirets bas dans un nom de fichier : rapport_final_v2.txtrapportfinalv2.txtInchangé : les tirets bas à l'intérieur d'un mot ne sont pas de l'emphaseGarder
Astérisque comme signe de multiplication : 2 * 3 * 42 3 4Inchangé : un délimiteur entouré d'espaces n'ouvre rienGarder
Bloc de code clôturéMange un accent grave de clôture et modifie le code à l'intérieurContenu conservé tel quelJeter 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 aussiDestination résolue depuis la définition située ailleursGarder le texte et la destination, jeter la ligne de définition
Tableau à barres verticalesUne file de mots, la ligne --- survivant en texteLignes et cellules, et seulement sous GFM — CommonMark n'a pas de tableauxGarder les cellules avec un séparateur, jeter la ligne d'alignement
Supprimer le MarkdownSupprime la mise en forme Markdown pour obtenir du texte brut.Essayer l'outil

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
TutorielNettoyer un texte en désordre : l'ordre des opérations qui compte vraimentRetirer les balises avant de décoder les entités, couper les espaces avant de dédupliquer, écraser les blancs en dernier. Trois ordres exécutés dans Node, un pipeline de neuf étapes dans le bon sens, et les caractères invisibles — U+00A0, U+200B, U+FEFF — qui survivent à tout nettoyage naïf.GuideRetirer le HTML proprement : ce qu'un suppresseur de balises peut et ne peut pas faireRetirer des balises et assainir du HTML sont deux métiers différents. Un fragment réel passé dans une regex naïve puis dans un nettoyeur conscient de la mise en forme, avec le contenu des script et style, les coupures de blocs, les commentaires, les CDATA et l'ordre des entités montrés en sortie.GuideFormater les nombres pour six langues : séparateurs, monnaie et le retour à la valeur1 234,56 et 1,234.56 sont le même nombre, et les confondre change la valeur que lit un lecteur. Nous avons lancé Intl.NumberFormat pour les six locales du site et imprimé chaque séparateur — dont l'invisible qu'emploie le français — puis mesuré pourquoi parseFloat ne peut rien défaire.ExplicationLes emoji sont plus difficiles qu'ils n'en ont l'air : pourquoi « il suffit de les retirer » n'a pas de réponse en une ligneUn emoji visible peut valoir un point de code ou quatorze unités UTF-16. Nous avons lancé trois expressions régulières répandues sur une vraie phrase : chacune a cassé différemment, l'une a supprimé les chiffres. Voici pourquoi, quelle propriété Unicode répond à quelle question, et la règle de groupes de graphèmes qui marche vraiment.GuideLes listes de tâches Markdown et ce qui s'affiche vraiment oùLes listes de tâches ne sont pas dans CommonMark. C'est une extension de GitHub Flavored Markdown, d'où des cases à cocher ici et des crochets littéraux là. La règle exacte du marqueur, l'effet de l'imbrication, et un tableau de ce qui est CommonMark, GFM ou ni l'un ni l'autre — vérifié sur les deux spécifications et quatre moteurs de rendu.ExplicationCompter les mots est ambigu, et chaque outil répond autrementUn compte de mots est une définition, pas une mesure. Nous avons compté le même paragraphe de quatre façons et obtenu 25, 28, 33 et 38 — puis compté 50 000 caractères de prose ordinaire et obtenu un accord à 4,5 % près. L'écart tient entièrement aux composés, aux chiffres et aux URL.

Outils similaires

Sources

Tu as repéré une erreur dans cet article ?

Retirer le Markdown : ce que le texte brut perd, et ce qu'une regex se trompe à faire — OneKitly