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
- 14 - Performance et sécurité
- Groupes de capture et groupes nommes
- Lookahead et lookbehind
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 :
- Regarde des exemples réels de l'input que tu dois parser
- Identifie les parties fixes (delimiteurs, mots-clés, format)
- Construis la regex par morceaux, en testant chaque partie séparément
- Utilise des groupes nommes pour rendre le résultat exploitable
- Teste sur des cas limites : chaînes vides, caractères speciaux, donnees malformees
- 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