Regex - 15 - Parsing du monde réel

Exemples concrets de parsing avec regex : logs, CSV, attributs HTML et git log, avec construction étape par étape.

15 - Parsing du monde réel

Ce que tu vas apprendre

  • Comment parser des lignes de log avec des groupes nommes
  • Comment extraire des donnees d'un CSV (y compris les champs entre guillemets)
  • Comment trouver des attributs HTML (et pourquoi tu ne devrais pas aller plus loin)
  • Comment parser la sortie de git log

Prerequisites


La theorie c'est bien, mais ce qui compte c'est de résoudre de vrais problèmes. Dans cet article, je prends quatre cas concrets que j'ai rencontres en production et je construis chaque regex étape par étape. Tu vas voir le processus de reflexion, pas juste le résultat final.

Cas 1 : parser des lignes de log

Un fichier de log typique ressemble a ca :

2026-03-29T14:23:01.456Z [INFO]  server: Request received path=/api/users method=GET duration=45ms
2026-03-29T14:23:01.789Z [ERROR] database: Connection timeout host=db.prod.internal retries=3
2026-03-29T14:23:02.012Z [WARN]  auth: Token expires soon user=nicolas exp=1711700000

Je veux extraire : le timestamp, le niveau de log, le module, le message, et les paires clé=valeur.

Étape 1 : le timestamp

Le format est ISO 8601. Je commence simple :

\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z

Avec un groupe nomme :

(?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)

Étape 2 : le niveau de log

Entre crochets, un mot en majuscules :

\[(?<level>[A-Z]+)\]

Étape 3 : le module et le message

Le module est un mot suivi de deux-points. Le message est tout ce qui suit jusqu'aux paires clé=valeur.

(?<module>\w+):\s+(?<message>[^=]+?)(?=\s+\w+=|\s*$)

Le lookhead (?=\s+\w+=|\s*$) arrêté le message quand on arrive aux paires clé=valeur ou a la fin de ligne.

Étape 4 : les paires clé=valeur

(?<pairs>(?:\s+\w+=\S+)*)

Le pattern complet

javascriptconst logRegex = /^(?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\s+\[(?<level>[A-Z]+)\]\s+(?<module>\w+):\s+(?<message>.+?)(?=\s+\w+=)?\s*(?<pairs>(?:\w+=\S+\s*)*)$/;

const line = '2026-03-29T14:23:01.456Z [INFO]  server: Request received path=/api/users method=GET duration=45ms';
const match = line.match(logRegex);

console.log(match.groups);
// {
//   timestamp: "2026-03-29T14:23:01.456Z",
//   level: "INFO",
//   module: "server",
//   message: "Request received",
//   pairs: "path=/api/users method=GET duration=45ms"
// }

Pour extraire les paires individuellement :

javascriptfunction parsePairs(pairString) {
  const pairRegex = /(?<key>\w+)=(?<value>\S+)/g;
  const result = {};
  let m;
  while ((m = pairRegex.exec(pairString)) !== null) {
    result[m.groups.key] = m.groups.value;
  }
  return result;
}

parsePairs("path=/api/users method=GET duration=45ms");
// { path: "/api/users", method: "GET", duration: "45ms" }

La leçon ici : décomposé le problème. N'essaie pas d'écrire une mega-regex d'un coup. Construis morceau par morceau, teste chaque partie sur regex101, puis assemble.

Cas 2 : extraire des donnees CSV

Le CSV a l'air simple jusqu'a ce que tu rencontres des champs entre guillemets contenant des virgules et des guillemets echappes.

nom,ville,description
"Nguyen, Nicolas",Paris,"Dev ""fullstack"""
Martin,Lyon,Designer
"O'Brien",Dublin,"Pas de guillemets ici"

Les champs simples

Un champ sans guillemets, c'est juste "tout sauf une virgule" :

[^,]+

Les champs entre guillemets

Un champ entre guillemets peut contenir des virgules et des guillemets doubles echappes par dedoublement (""):

"(?:[^"]|"")*"

Ca se lit : un guillemet ouvrant, puis zero ou plus de (caractère non-guillemet OU deux guillemets), puis un guillemet fermant.

Le pattern combine

javascriptconst fieldRegex = /(?:^|,)("(?:[^"]|"")*"|[^,]*)/g;

function parseCSVLine(line) {
  const fields = [];
  let match;
  const adjusted = "," + line; // Astuce pour uniformiser le parsing
  const regex = /,("(?:[^"]|"")*"|[^,]*)/g;

  while ((match = regex.exec(adjusted)) !== null) {
    let field = match[1];
    // Retirer les guillemets englobants et desechapper
    if (field.startsWith('"') && field.endsWith('"')) {
      field = field.slice(1, -1).replace(/""/g, '"');
    }
    fields.push(field);
  }
  return fields;
}

parseCSVLine('"Nguyen, Nicolas",Paris,"Dev ""fullstack"""');
// ["Nguyen, Nicolas", "Paris", 'Dev "fullstack"']

Est-ce que cette regex gere tous les cas ? Non. Les CSV avec des sauts de ligne dans les champs entre guillemets, les BOM en début de fichier, les separateurs différents... Pour un vrai parser CSV robuste, utilise une librairie comme papaparse. Mais pour 90% des cas simples, cette regex fait le travail.

Cas 3 : extraire des attributs HTML

Trouver les attributs d'une balise HTML est faisable avec une regex. Parser du HTML complet ne l'est pas. La distinction est fondamentale.

Ce que la regex peut faire

Extraire les attributs d'une balise connue :

javascriptconst imgTag = '<img src="/photo.jpg" alt="Mon chat" class="rounded" loading="lazy">';

const attrRegex = /(\w+)="([^"]*)"/g;
let match;
const attrs = {};

while ((match = attrRegex.exec(imgTag)) !== null) {
  attrs[match[1]] = match[2];
}

console.log(attrs);
// { src: "/photo.jpg", alt: "Mon chat", class: "rounded", loading: "lazy" }

Trouver toutes les balises <a> avec leur href :

javascriptconst html = `
  <a href="/about">A propos</a>
  <a href="https://example.com" target="_blank">Externe</a>
`;

const linkRegex = /<a\s+[^>]*href="(?<url>[^"]*)"[^>]*>(?<text>[^<]*)<\/a>/g;
const links = [...html.matchAll(linkRegex)].map(m => ({
  url: m.groups.url,
  text: m.groups.text,
}));

// [{ url: "/about", text: "A propos" }, { url: "https://example.com", text: "Externe" }]

Ce que la regex ne peut PAS faire

Parser du HTML arbitraire. Le HTML est un langage imbrique (des balises dans des balises), et les regex ne peuvent pas gerer les structures recursives arbitraires.

html<!-- Bonne chance avec une regex -->
<div class="outer">
  <div class="inner">
    <p>Du texte avec un <a href="#">lien</a> et un <img src="x"> dedans</p>
  </div>
  <!-- Un commentaire avec des <balises> dedans -->
</div>

Balises auto-fermantes, commentaires, attributs sans guillemets, attributs avec des guillemets simples, contenu CDATA... Les cas limites sont infinis.

Utilise DOMParser en JavaScript :

javascriptconst parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const links = doc.querySelectorAll("a[href]");

C'est plus robuste, plus lisible, et ca gere tous les cas. Garde les regex pour l'extraction rapide d'un pattern precis dans du HTML bien connu, jamais pour le parsing général.

Cas 4 : parser la sortie de git log

git log avec un format personnalise produit une sortie tres parseable :

bashgit log --format="%H|%an|%ae|%aI|%s" --max-count=5

Sortie :

d8511f5abc...|Nicolas Nguyen|nicolas@example.com|2026-03-29T10:00:00+01:00|feat: add SQL series
9060b53def...|Nicolas Nguyen|nicolas@example.com|2026-03-28T15:30:00+01:00|fix: memory leak in decorator

Le parsing :

javascriptconst gitLogRegex = /^(?<hash>[a-f0-9]+)\|(?<author>[^|]+)\|(?<email>[^|]+)\|(?<date>[^|]+)\|(?<subject>.+)$/;

const lines = gitOutput.trim().split("\n");
const commits = lines.map(line => {
  const match = line.match(gitLogRegex);
  return match ? match.groups : null;
}).filter(Boolean);

Avec un format plus elabore, tu peux aussi extraire le corps du commit et les fichiers modifies :

javascript// Parser les conventional commits
const conventionalRegex = /^(?<type>feat|fix|docs|style|refactor|test|chore)(?:\((?<scope>[^)]+)\))?(?<breaking>!)?: (?<description>.+)$/;

const subject = "feat(auth): add OAuth2 support";
const match = subject.match(conventionalRegex);

console.log(match.groups);
// { type: "feat", scope: "auth", breaking: undefined, description: "add OAuth2 support" }

Ce pattern est utile pour générer des changelogs automatiques ou valider les messages de commit dans un hook pre-commit. Un exemple complet de hooks Git est disponible sur paltemps.fr.

Methodologie générale

A travers ces quatre exemples, un processus emerge :

  1. Regarde des exemples réels de l'input que tu dois parser
  2. Identifie les parties fixes (delimiteurs, mots-clés, format)
  3. Construis la regex par morceaux, en testant chaque partie séparément
  4. Utilise des groupes nommes pour rendre le résultat exploitable
  5. Teste sur des cas limites : chaînes vides, caractères speciaux, donnees malformees
  6. Demande-toi si une regex est le bon outil : si le format est trop complexe ou variable, passe a un vrai parser

Résumé

  • Parser des logs : décomposé en timestamp, level, module, message, paires clé=valeur
  • Parser du CSV : gere les champs entre guillemets avec "(?:[^"]|"")*"
  • Extraire des attributs HTML : faisable pour des cas simples, mais utilise DOMParser pour du HTML réel
  • Parser git log : combine un format personnalise avec une regex a groupes nommes
  • Methodologie : toujours construire par morceaux, toujours tester sur les cas limites

Article précédent : 14 - Performance et sécurité Article suivant : 16 - Quand ne pas utiliser les regex

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.