Journal Unicode, NDJSON et bug subtil

Posté par  .
Étiquettes : aucune
6
19
oct.
2026

Bonjour Nal,

Pendant des années, j'ai utilisé le code suivant pour parser des fichiers NDJSON (un format de fichier qui contient un document JSON par ligne) en node.js :

import { stdin } from "node:process";
import readline from "node:readline/promises";

const rl = readline.createInterface(stdin);

for await (const l of rl) {
  const data = JSON.parse(l);
}

Cependant il a un bug, saurez-vous le trouver ?

La reproduction

Le bug est exhibé lorsqu'on passe le texte généré ainsi au programme ci-dessus :

console.log(JSON.stringify("a\u2028b")); // Affiche le texte "a b" dans le terminal

Le bug

U+2028 est un caractère nommé "séparateur de lignes". C'est un caractère qui est autorisé dans les chaînes de caractères JSON, et qui est considéré comme un séparateur de lignes par readline.

Par conséquence le JSON "a\u2028b" (où \u2028 désigne le caractère dont on parle, pas la séquence d'échappement qui le produit) est découpé par readline en "a et b" qui sont du JSON invalide.

Les caractères U+2028 et U+2029

Unicode a introduit deux caractères, U+2028 (Séparateur de lignes) et U+2029 (Séparateur de paragraphes), que le consortium a recommandé d'utiliser à la place des caractères issus de ascii (\n et \r\n selon la plateforme) qui ne distingue pas s'il s'agit d'un retour à la ligne au sein d'un paragraphe ou un nouveau paragraphe.

Aujourd'hui ces caractères ne semblent pas gérés : dans vim et dans le terminal ils sont affichés comme des espaces, et Visual Studio Code produit l'avertissement suivant :

The file 'u2028.txt' contains one or more unusual line terminator characters, like Line Separator (LS) or Paragraph Separator (PS).

It is recommended to remove them from the file.

Les specs

Concernant les caractères autorisés dans une chaîne de caractères JSON, tout est autorisé sauf les caractères de contrôle ascii (dont notamment \n), le caractère \ et les guillemets fermants, qui doivent être échappés.

Concernant le readline de Node.js, le comportement n'est pas documenté (la doc ne mentionne que \n et \r\n), mais le problème a été introduit dans Node 24.0.0, malheureusement sans explication, et alors que la tendance semblait davantage être à ne plus utiliser ces caractères.

  • # ASCII’s not dead ?

    Posté par  . Évalué à 3 (+0/-0).

    Les caractères de contrôle ASCII sont interdit dans JSON ? Je pense aux caractères \x1c \x1d \x1e et \x1f

    Le programme unicode me sort un truc assez surprenant, il me les désigne tous les quatre comme des “paragraph separators” ou “segment separator” pour \x1f ou encore “information separators” four three two one…

    $ unicode  -v -x 1c 1d 1e 1f | grep -E '^(U\+|Bidi)'
    U+001C INFORMATION SEPARATOR FOUR
    Bidi: B (Paragraph Separator)
    U+001D INFORMATION SEPARATOR THREE
    Bidi: B (Paragraph Separator)
    U+001E INFORMATION SEPARATOR TWO
    Bidi: B (Paragraph Separator)
    U+001F INFORMATION SEPARATOR ONE
    Bidi: S (Segment Separator)
    

    À ma connaissance il s’agit de, respectivement, Field Separator, Group Separator, Record Separator et Unit Separator. Ces caractères pourraient-ils résoudre ton problème ?

    • [^] # Re: ASCII’s not dead ?

      Posté par  (site web personnel, Mastodon) . Évalué à 4 (+1/-0).

      La réponse est dans la question:

      Concernant les caractères autorisés dans une chaîne de caractères JSON, tout est autorisé sauf les caractères de contrôle ascii (dont notamment \n), le caractère \ et les guillemets fermants, qui doivent être échappés.

      Ces caractères de contrôle sont donc interdits dans les chaînes de caractères en JSON.

      Pour citer précisément la spécification:

      All code points may be placed within the quotation marks except for the code points that must be escaped: quotation mark (U+0022), reverse solidus (U+005C), and the control characters U+0000 to U+001F.

      De façon surprenante, le codepoint U+007F, lui, est autorisé sans échappement. En ASCII et en Unicode il s'agit de DELETE qui est un caractère de contrôle. Les tabulations et retours à la ligne (dans leur forme ASCII) sont, eux, interdits.

      Le programme unicode me sort un truc assez surprenant, il me les désigne tous les quatre comme des “paragraph separators” ou “segment separator” pour \x1f ou encore “information separators” four three two one…

      Ce sont bien les noms choisis par Unicode. Ils sont différents des noms qui étaient utilisés dans la spécification ASCII, même si ce sont les mêmes caractères.

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.