Manipulation de fichiers CSV en JavaScript Vanilla

Il m'arrive régulièrement de manipuler des fichiers CSV en JavaScript. Pour mes besoins assez limité, j'aime bien réutiliser des petits bouts de code que je vais partager ici.

Pour manipuler du JavaScript dans un projet important, il peut être plus prudent d'utiliser une librairie du type Papa Parse.

J'utilise donc un fichier avec des fonctions relativement simples :

Fichier : csvUtilities.js (js)
1function splitByObject(csv) {
2    const lines = [];
3    let current = '';
4    let inQuotes = false;
5
6    for (let i = 0; i < csv.length; i++) {
7        const char = csv[i];
8
9        if (char === '"') {
10            // gérer """"
11            const next = csv[i + 1];
12            if (inQuotes && next === '"') {
13                current += '"';
14                i++;
15            } else {
16                inQuotes = !inQuotes;
17            }
18            current += char;
19        }
20        else if ((char === '\n' || char === '\r') && !inQuotes) {
21            // FIN DE LIGNE — si on n’est pas dans les guillemets
22            if (current.trim() !== '') {
23                lines.push(current);
24            }
25            current = '';
26        } 
27        else {
28            current += char;
29        }
30    }
31
32    if (current.trim() !== '') {
33        lines.push(current);
34    }
35
36    return lines;
37}
38
39function trimAndCleanArray(arr) {
40    return arr
41        .map(val => val.trim()) // Remove blank space
42        .filter(Boolean); // Remove empty values
43}
44        
45function parseCSVLine(line, separator = ';') { // Transforme une ligne CSV brut en tableau
46    const results = [];
47    let current = '';
48    let inQuotes = false;
49
50    for (let i = 0; i < line.length; i++) {
51        const char = line[i];
52        const nextChar = line[i + 1];
53
54        if (char === '"') {
55            if (inQuotes && nextChar === '"') {
56                current += '"'; // guillemet échappé
57                i++;
58            } else {
59                inQuotes = !inQuotes;
60            }
61        }
62        else if (char === separator && !inQuotes) {
63            results.push(current);
64            current = '';
65        }
66        else {
67            current += char;
68        }
69    }
70
71    results.push(current);
72    return results;
73}
74
75function decodeCSVContent(content, separator = ';', hasHeader = true) { // Transforme un CSV brut en objet CSV
76    let rawLines = trimAndCleanArray(splitByObject(content)); // Sépare et nettoie les lignes
77    const lines = rawLines.map(line => parseCSVLine(line, separator)); // Sépare les lignes en colonnes
78                
79    let header;
80    if(hasHeader) { // Si la première ligne est un header
81        header = lines.shift(); // Deplace la première ligne dans le header
82    }
83    else {
84        header = Array.from({length: lines[0].length}, (_, index) => 'Colonne ' + (index + 1)) // Sinon, genère un header (Colonne 1, Colonne 2...)
85    }
86        
87    return { header, lines }
88}
89
90async function getFileInputContent(input) { // Récupère le contenu brut d'un input (à utiliser avec <input type="file" accept=".csv" />)
91    return await input.files[0].text();
92}
93        
94async function decodeInputCsvContent(input, separator = ';', hasHeader = true) { // Retourne le header et le contenu du fichier CSV dans un tableau
95    let csvContent = await getFileInputContent(input);
96    let { header, lines } = decodeCSVContent(csvContent, separator, hasHeader);
97            
98    return { header, lines };
99}
100
101function csvToObjects(mapping, lines, header) {
102    let columnIndexes = {};
103
104    if(header) {
105        for (let key in mapping) {
106            columnIndexes[key] = header.indexOf(mapping[key]);
107        }
108    }
109    else {
110        columnIndexes = mapping;
111    }
112
113    const objects = [];
114    lines.forEach((line) => {
115        const lineObj = {};
116        for (let key in columnIndexes) {
117            lineObj[key] = line[columnIndexes[key]];
118        }
119
120        objects.push(lineObj);
121    });
122
123    return objects;
124}

L'utilisation se fait de la manière suivante :

(js)
1const csvContent = `Title;Date;Content;Le nom de l'auteur
2Mon super titre;2025-06-19;"Mon contenu ; avec des guillemets";Valentin
3Mon grand titre;2025-08-07;Mon projet;Benjamin`;
4
5// On peut utiliser decodeCSVContent ou decodeInputCsvContent (pour un input).
6// Le premier paramètre est le contenu brut ou l'input
7// Le deuxième est le séparateur (; par défaut)
8// Le troisième est s'il y a ou non un header (true par défaut)
9
10const { header, lines } = decodeCSVContent(csvContent);
11// header = [ "Title", "Date", "Content", "Le nom de l'auteur" ]
12// lines = [
13//     [ "Mon super titre", "2025-06-19", "Mon contenu ; avec des guillemets", "Valentin" ],
14//     [ "Mon grand titre", "2025-08-07", "Mon projet", "Benjamin" ]
15// ]
16
17// Pour simplifier la manipulation des lignes, il est possible d'utiliser le fonction csvToObjects
18const mapping = {
19    'projectDate': 'Date',
20    'author': 'Le nom de l\'auteur'
21};
22const objects = csvToObjects(mapping, lines, header);
23// objects = [
24//     { projectDate: "2025-06-19", author: "Valentin" },
25//     { projectDate: "2025-08-07", author: "Benjamin" }
26// ]

Valentin LORTET

Cet article vous a plu ? N'hésitez pas à le partager:

Découvrir d’autres articles