Aller au contenu
OneKitly

Coller un tableau dans une pull request : ce qui casse, et les deux caractères qui cassent tout

Publié le 29/07/2026 · 15 min de lecture · Outils pour développeurs

Daniel Okonkwo

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

Performance web · Formats de fichiers

Vérifié à partir de 4 sources

Voir le profil
En bref

Une cellule de tableau Markdown accepte tout sauf deux caractères. La barre verticale ferme la cellule : il faut donc l'écrire \| — le convertisseur le fait pour toi, colle USB-C | 2 m et il produit USB-C \| 2 m, que GitHub affiche comme une seule cellule. Le saut de ligne ferme la ligne, et il n'existe aucun échappement pour lui : une cellule contenant un vrai retour à la ligne est impossible dans la syntaxe des tableaux GFM, alors l'outil le remplace par la balise HTML <br>, la seule chose qui fonctionne. Sous l'en-tête vient la ligne de séparation, et elle est obligatoire — une ligne d'en-tête sans rangée de tirets en dessous n'est pas un tableau, et GitHub l'affiche comme un paragraphe contenant des barres. Cette rangée porte aussi l'alignement : --- laisse la valeur par défaut du moteur, :-- aligne à gauche, --: à droite, :-: centre, et un deux-points ailleurs ne fait rien. La ligne de séparation doit compter exactement autant de cellules que l'en-tête, sinon le tableau n'est pas reconnu du tout. Les espaces qui font joliment tomber les colonnes sont purement cosmétiques : | a | b | et |a|b| produisent un HTML identique octet pour octet, d'où l'interrupteur « Aligner les colonnes » et le fait qu'on peut le couper sans risque. Un détail sur l'échappement, parce qu'ici la sortie correcte a l'air fausse : l'antislash est échappé avant la barre. Une cellule qui contient déjà x \| y ressort en x \\\| y, où \\ est un antislash littéral et \| une barre littérale, si bien que GitHub affiche l'unique cellule x \| y. N'échapper que la barre donnerait x \\| y — un antislash littéral suivi d'une vraie coupure de cellule — et la colonne se scinderait. Tout est dans l'ordre, et c'est pourquoi un tableau Markdown existant peut repasser dans le convertisseur CSV et en ressortir intact.

Un tableau Markdown n'interdit que deux caractères dans une cellule : la barre verticale et le saut de ligne. Voici ce que fait chacun, comment un convertisseur les traite, pourquoi l'échappement doit être appliqué dans le bon ordre, et pourquoi l'alignement du source ne compte jamais.

Deux caractères, et deux seulement

Presque tout traverse une cellule de tableau Markdown sans dommage. Accents, idéogrammes, emoji, accents graves, astérisques, crochets, symboles monétaires, guillemets : rien de tout cela ne signifie quoi que ce soit pour l'analyseur de tableau, qui ne cherche jamais que deux choses. La barre verticale termine une cellule. Le saut de ligne termine une rangée. Voilà toute la grammaire, et chaque ennui que tu auras en collant un tableau dans une pull request sera l'un de ces deux caractères arrivé là où l'analyseur ne l'attendait pas.

La barre verticale a un échappement. Écris \| et l'analyseur lit une barre littérale au lieu d'une frontière de colonne. Le convertisseur l'applique pour toi : ses propres données d'exemple contiennent la cellule USB-C | 2 m, et faire passer l'exemple donne la ligne | Câble | USB-C \| 2 m | 9.90 |, soit une rangée de trois cellules et non de quatre. Donne-lui une cellule qui n'est qu'une barre et tu obtiens \| tout seul, toujours une cellule. Cette partie est solide, et c'est celle que les tableaux écrits à la main ratent le plus souvent : une commande shell dans un tableau de documentation — ps | grep node — gagne silencieusement une colonne et décale toutes les valeurs d'un cran vers la droite, un défaut qu'un relecteur survole parce que le tableau ressemble toujours à un tableau.

Le saut de ligne n'a aucun échappement, et c'est la partie qu'il faut intérioriser : une cellule contenant un retour à la ligne n'est pas seulement malcommode dans la syntaxe des tableaux GFM, elle est impossible. La rangée se termine au retour à la ligne, point. Il n'existe exactement que deux issues honnêtes. Remplacer la coupure par la balise HTML <br>, que GitHub autorise dans une cellule et que le convertisseur applique — un champ CSV entre guillemets valant « ligne un\nligne deux » ressort en ligne un<br>ligne deux dans une seule cellule. Ou accepter que le contenu n'a rien à faire dans un tableau et le mettre dans une liste ou un paragraphe en dessous. Tout le reste est un vœu pieux : il n'y a ni astuce d'antislash, ni barre doublée, ni marqueur de continuation.

La ligne de séparation n'est pas un ornement

GitHub Flavored Markdown appelle la rangée de tirets sous l'en-tête la ligne de séparation, et la spécification est sans ambiguïté : elle est faite de cellules dont le seul contenu est des tirets, avec éventuellement un deux-points au début, à la fin, ou aux deux. Sans elle, pas de tableau. Une ligne d'en-tête et trois lignes de données sans tirets entre elles s'affichent comme quatre paragraphes de texte contenant des barres — c'est pourquoi un tableau qui semblait bon dans un éditeur et qui a cassé dans la pull request a presque toujours perdu cette ligne-là dans un copier-coller.

Quatre formes de cellule de séparation signifient quatre choses différentes, et le convertisseur propose les quatre comme réglage d'alignement. Un --- nu laisse l'alignement au moteur, ce qui en pratique veut dire à gauche dans tous les moteurs que quiconque utilise. Un deux-points au début, :---, force à gauche. Un deux-points à la fin, ---:, force à droite, et c'est celui qu'il te faut sur toute colonne de nombres. Un deux-points aux deux bouts, :---:, centre. Un deux-points au milieu des tirets n'est pas un alignement et n'est pas valide — la cellule doit être deux-points, tirets, deux-points, dans cet ordre, et rien d'autre. Règle l'outil sur droite et un tableau à deux colonnes ressort avec | -----: | ---: | en dessous, et chaque nombre de la colonne s'aligne sur son dernier chiffre dans le rendu.

Une règle attrape l'essentiel des échecs silencieux : la ligne de séparation doit compter exactement autant de cellules que la ligne d'en-tête, sinon le tableau n'est pas reconnu du tout. La spécification le dit noir sur blanc, et le mode de défaillance est le pire qui soit — tu n'obtiens pas un tableau cassé, tu n'obtiens pas de tableau. Un en-tête de quatre colonnes au-dessus d'une séparation de trois s'affiche en texte littéral, barres comprises. C'est précisément ce qui arrive quand on ajoute une colonne à la main en oubliant les tirets. Le convertisseur ne peut pas commettre cette erreur, puisqu'il construit les deux rangées à partir du même nombre de colonnes : un argument correct en faveur de la génération plutôt que de l'édition.

L'alignement du source est pour toi, pas pour GitHub

Le convertisseur a un interrupteur « Aligner les colonnes », activé par défaut, et il ne change rien au tableau rendu. Activé, un tableau à deux colonnes ressort en | Item | Qty |, | ------ | --- |, | Widget | 3 |, cellules bien carrées. Désactivé, tu obtiens | Item | Qty |, | --- | --- |, | Widget | 3 |, en dents de scie. GitHub produit un HTML identique dans les deux cas. Les espaces existent pour qu'un humain lisant le diff voie les colonnes, et pour aucune autre raison. Coupe l'alignement quand le tableau est large et que c'est le diff qu'on lira ; laisse-le quand le fichier est édité à la main.

Il y a un piège à connaître si tes données ne sont pas latines. L'alignement est calculé en comptant les caractères, et un caractère n'est pas une colonne. Donne au convertisseur une cellule contenant deux emoji : il en compte deux, aligne sur deux, et le source ne tombe plus juste dans un éditeur où chaque emoji occupe deux colonnes de chasse fixe. La même chose se produit avec du texte chinois, japonais ou coréen, et en sens inverse avec un é écrit e plus accent combinant, soit deux caractères dans une seule colonne. Rien de tout cela n'affecte le tableau rendu : c'est un problème de lisibilité, pas de correction — mais si tu produises un large tableau de noms de lieux japonais, n'attends pas un source bien peigné.

Échapper l'échappement, et deviner le délimiteur

Un convertisseur qui remplace chaque barre par \| et s'arrête là se trompe sur exactement une entrée, et c'est celle qu'on rencontre dès qu'on réimporte un tableau qu'on a écrit soi-même : une cellule contenant déjà \|. N'échappe que la barre et x \| y devient x \\| y, où l'antislash doublé est un antislash littéral et où la barre derrière lui redevient vivante — la colonne se scinde et rien ne prévient. L'ordre doit être inverse. Ce convertisseur échappe d'abord l'antislash, puis la barre, si bien que x \| y ressort en x \\\| y, ce qui a l'air d'un antislash de trop et n'en est pas un : GFM lit \\ comme un antislash littéral et \| comme une barre littérale, et affiche l'unique cellule x \| y.

La conséquence, c'est que l'aller-retour est sûr. Convertis des données en tableau Markdown, ressors le tableau, enregistre-le en CSV, repasse-le dans l'outil : les colonnes qui contenaient des barres reviennent telles qu'elles sont parties, et tout le reste avec. Ça vaut plus que ça n'en a l'air, parce qu'exporter un tableau rendu vers un tableur puis le réimporter est une chose banale dès qu'une colonne change de nom. L'outil frère de ce site, le générateur de tableaux Markdown, referme la même boucle par l'autre bout : son analyseur lit \| comme une barre littérale quand le délimiteur est la barre et le déséchappe à l'entrée, si bien qu'un tableau collé là revient identique lui aussi.

Le délimiteur est deviné plutôt que déclaré, et la devinette se fait sur plusieurs enregistrements, pas sur le seul en-tête. L'outil compte les virgules, points-virgules, tabulations et barres hors guillemets sur cinq enregistrements au plus, et un délimiteur qui donne le même compte sur chaque enregistrement lu l'emporte sur celui qui est simplement le plus nombreux sur le premier. C'est ce qui tranche un en-tête comme A|B|C,D : seul, il ressemble à trois colonnes séparées par des barres, mais mets sous lui des lignes en virgules et la virgule gagne, parce que c'est son compte qui se répète. Un export français dont l'en-tête est Nom;Prénom est détecté en point-virgule dans les deux cas. La seule chose que le premier enregistrement décide encore seul, c'est quels délimiteurs sont candidats — un en-tête qui n'en contient aucun des quatre ne laisse rien à départager, et la devinette retombe sur la virgule. Si ta première ligne est inhabituelle, règle le délimiteur à la main.

Un enchaînement qui survit à la relecture

Exporte les données en CSV plutôt que de copier des cellules depuis un tableur : les règles de guillemets d'un fichier CSV sont la seule chose qui indique au convertisseur où s'arrête un champ contenant une virgule ou un retour à la ligne. Colle, vérifie le séparateur détecté, choisis l'alignement — à droite pour les nombres, par défaut pour le reste — et lis les deux premières lignes de sortie avant de copier. Ces deux lignes sont l'en-tête et la séparation, et si elles n'ont pas le même nombre de barres, rien ne s'affichera en aval.

Cherche ensuite les trois cellules qui posent problème. Tout ce qui contient une barre : vérifie qu'elle est sortie en \| et non en frontière de colonne vivante, et souviens-toi qu'une cellule contenant déjà un antislash le voit doubler — \\\| est la bonne sortie, pas un échappement en trop. Tout ce qui était multiligne : vérifie que c'est devenu <br> et demande-toi si c'est vraiment ce que tu veux dans un tableau. Tout ce qui est vide : une cellule vide est parfaitement légale et s'affiche vide, donc une rangée de blancs au milieu de ton tableau est une donnée, pas un dégât. Si le tableau part dans un dépôt plutôt que dans un commentaire, commite-le une fois avec l'alignement activé pour que le premier relecteur lise le diff, puis ne le reformate plus jamais — un changement d'espaces sur un tableau, c'est trente lignes de bruit dans une pull request qui ne dit rien.

Ce que le convertisseur fait de chaque cellule problématique, et ce que GitHub affiche
Contenu de la celluleCe que l'outil produitRésultat
USB-C | 2 mUSB-C \| 2 mCorrect — une cellule contenant une barre visible
Un champ entre guillemets contenant un vrai retour à la ligneligne un<br>ligne deuxCorrect — la seule chose que permettent les tableaux GFM ; un vrai saut de ligne est impossible
x \| y (déjà échappé)x \\\| yCorrect — l'antislash est échappé d'abord, GitHub affiche l'unique cellule x \| y
Un champ videRien entre les barresCorrect — une cellule vide est légale et s'affiche vide
Une rangée avec moins de champs que l'en-têteComplétée par des cellules vides jusqu'à la rangée la plus largeCorrect — un tableau irrégulier ne s'afficherait pas, l'outil le rend rectangulaire
Deux emojiAlignée comme si elle faisait deux colonnes de largeCosmétique seulement — ils en occupent quatre ; le tableau rendu n'en souffre pas
CSV vers tableau MarkdownTransforme un CSV en tableau Markdown, guillemets compris : un champ peut contenir le délimiteur, un saut de ligne ou une barre verticale sans casser le tableau. Virgule, point-virgule (Excel français), tabulation ou barre — détecté ou imposé.Essayer l'outil

Questions fréquentes

Peut-on mettre un saut de ligne dans une cellule de tableau Markdown ?
Pas un vrai. Le saut de ligne est ce qui termine une rangée : la syntaxe des tableaux n'a donc aucun moyen d'exprimer une cellule qui en contient un — il n'existe pas de séquence d'échappement comme \| pour la barre. Le contournement accepté par GitHub est la balise HTML <br>, ce que ce convertisseur substitue : un champ CSV entre guillemets contenant une coupure devient une cellule valant ligne un<br>ligne deux. Deux limites honnêtes. Les moteurs qui filtrent le HTML afficheront la balise en texte littéral, et une cellule contenant trois ou quatre <br> signale en général que le contenu veut être une liste sous le tableau plutôt qu'une cellule dedans.
Mon tableau s'affiche en texte brut avec des barres. Qu'ai-je cassé ?
Presque toujours la ligne de séparation. Soit elle manque, soit elle n'a pas le même nombre de cellules que l'en-tête — la spécification exige que l'en-tête et la séparation aient le même nombre de cellules, faute de quoi le tableau n'est pas reconnu et retombe en paragraphe. Compte les barres sur la ligne un et la ligne deux de ton tableau ; elles doivent être égales. L'autre cause fréquente est une ligne vide entre l'en-tête et la séparation, qui termine le bloc avant qu'il commence. Une troisième, plus rare : les cellules de séparation ne doivent contenir que des tirets et éventuellement des deux-points aux bords, donc un espace-tiret-espace égaré ou un tiret cadratin collé par la correction automatique d'un éditeur invalide la rangée.
Les espaces qui alignent les colonnes comptent-ils ?
Non. | a | b | et |a|b| produisent le même HTML sur GitHub, et l'interrupteur « Aligner les colonnes » n'existe que pour rendre le source lisible dans un éditeur. L'alignement a néanmoins un coût réel dans un dépôt : la largeur de chaque colonne étant celle de sa plus longue valeur, modifier une cellule peut changer l'alignement de toute la colonne, et un changement d'un mot devient un diff qui touche chaque rangée. Si le tableau vit dans un fichier versionné et change souvent, le générer sans alignement donne des diffs plus propres. S'il est écrit une fois et lu par des humains dans le fichier brut, garde l'alignement.
Peut-on mettre du gras, des liens ou du code dans une cellule ?
Oui — la mise en forme en ligne fonctionne normalement dans les cellules : **gras**, [lien](https://example.com) et `code` s'affichent tous. Les constructions de bloc, non : ni titres, ni listes, ni blocs de code délimités, ni tableaux imbriqués, car tous exigent des sauts de ligne que la rangée ne peut pas contenir. Le piège est un fragment de code contenant une barre, comme `ps | grep node`. Les accents graves ne protègent pas une barre de l'analyseur de tableau — la cellule est découpée d'abord, le code interprété ensuite — il faut donc quand même écrire `ps \| grep node`. C'est l'un des rares cas où l'échappement doit être fait à la main, le convertisseur n'échappant que les barres présentes dans les données sources.
Pourquoi mon CSV français est-il ressorti en une seule colonne ?
Parce que la ligne d'en-tête n'a rien donné à compter au détecteur, et c'est l'en-tête qui décide quels délimiteurs sont seulement en lice. L'outil compte les virgules, points-virgules, tabulations et barres hors guillemets sur les premiers enregistrements, et un compte qui se répète l'emporte sur celui qui est simplement le plus grand ligne 1 — mais un délimiteur absent du premier enregistrement n'est pas candidat du tout. Un tableur français ou allemand exporte en points-virgules, puisque la virgule est le séparateur décimal, et un en-tête Nom;Prénom est détecté correctement. Un en-tête réduit à un seul mot sans séparateur, non : il n'y a rien à compter, la devinette retombe sur la virgule, et tout le fichier arrive en une colonne. Règle l'option Délimiteur sur Point-virgule à la main. Le même remède vaut pour un export tabulé collé depuis un terminal, où les tabulations ont pu devenir des espaces en route et où il ne reste vraiment plus rien à trouver.

Articles qui pourraient t'intéresser

Tous les guides
TutorielConstruire un tableau Markdown de zéro, sans compter les tirets à la mainLa plus petite chose qui soit encore un tableau tient en deux lignes : une ligne d'en-tête et une ligne de séparation. Voici pourquoi la seconde est obligatoire en GitHub Flavored Markdown, où les tableaux à barres n'existent pas du tout, et ce qu'un générateur fait que la saisie manuelle ne peut pas faire.GuideTransposer un tableau dont les lignes auraient dû être des colonnesCe qu'il advient de la ligne d'en-tête, des lignes de longueurs inégales, des types — et la seule chose pour laquelle on confond régulièrement la transposition et qu'elle ne sait pas faire.ExplicationCSV vers JSON : les cinq cas qui cassent tous les convertisseursDélimiteurs entre guillemets, sauts de ligne intégrés, types ambigus, en-têtes en double et encodage. Chaque cas a été passé dans le convertisseur et la sortie exacte est reproduite ici — y compris les deux qu'il ne rattrape pas.TutorielMarkdown : guide du débutantMets en forme du texte brut avec quelques symboles : # pour les titres, ** pour le gras, - pour les listes. Voici ce qu'est le markdown, la syntaxe de base, pourquoi il est partout, et les pièges.ExplicationPoint-virgule, tabulation, barre verticale : choisir un délimiteur qui survit au trajetPourquoi la langue du lecteur décide du délimiteur, ce que le convertisseur fait aux guillemets quand tu changes, ce qu'est réellement la première ligne sep=, et le nombre de cellules citées sur le même export écrit de cinq façons.ExplicationPourquoi ton CSV casse les accents et les dates dans ExcelTrois pannes totalement différentes se cachent derrière la même phrase. L'une est l'encodage, l'autre le séparateur, la troisième Excel qui devine des types en ouvrant le fichier — et le remède diffère pour chacune. Voici comment les distinguer en cinq secondes.

Outils similaires

Ceci décrit le comportement d'un format de fichier et d'un moteur de rendu, vérifié sur la spécification citée et sur le code de l'outil tel qu'il existe aujourd'hui. Les moteurs de rendu divergent : GitHub, GitLab, un générateur de site statique et l'aperçu de ton éditeur sont quatre implémentations différentes, et ce qui passe dans l'une peut échouer dans l'autre. Rien ici n'est une garantie sur ta chaîne de publication — teste le résultat là où il sera réellement publié, et considère tout outil, celui-ci compris, comme quelque chose à vérifier et non à croire.

Sources

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