Parser un fichier CSV

[Résolu/Fermé]
Signaler
-
 Utilisateur anonyme -
Bonjour,
Je dois parser un fichier CSV dont les lignes ont le format suivant :
"champ1,""champ2"",""champ3"",...,""dernierChamp"""
Problème : les champs peuvent contenir des virgules.
J'ai d'abord essayé de splitter autour des virgules, mais les champs qui contiennent des virgules sont coupés en deux.
Je ne sais pas trop comment faire... Des idées ?
Merci.
Marie

5 réponses

Messages postés
5591
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
23 septembre 2021
947
Salut plieuse,

On doit pouvoir faire cela avec les regexp Java.

Cependant, il faut que l'on soit sûr que tu décrives correctement le format pour t'aider.

Dans ton exemple :

-
"champ1,
: il n'y a pas de guillemet fermant (et un seul guillemet ouvrant, cf. ci-après) ? Est-ce bien le début de ligne ?
-
""champ2"",
: il y a deux guillemets ouvrants et deux guillemets fermants ?
-
""champ3"",
: même question
...,
-
""dernierChamp"""
: il y a 3 guillemets fermants. Cela signifie-t-il qu'en début de ligne il y en a aussi 3 et non un seul ?

Peux-tu clarifier tout cela, stp, éventuellement en donnant des exemples de lignes rectifiées.


Dal

Salut Dal,
Je n'ai pas d'exemple sous la main. Les guillemets sont bien comme je les ai décrits.
Marie
Messages postés
5591
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
23 septembre 2021
947
Salut plieuse,


Si ta ligne est exacte, voilà un exemple qui permet de matcher ce qui est entre les virgules:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HelloCSV {

public static void main(String[] args) {
String CVS_st = "\"champ1,\"\"champ2\"\",\"\"champ3\"\",\"\"champ4\"\",\"\"champ5\"\",\"\"champ6\"\",\"\"dernierChamp\"\"\"";
int n = 0;

// match the first field
// form: "champ1,
Pattern pattern = Pattern.compile("(\".+?),");
Matcher matcher = pattern.matcher(CVS_st);
if (matcher.find())
{
System.out.println(matcher.group(1));
n = matcher.end();
} else
{
// do something, since expected format is not found
}
// explore the rest of the string for
// form: ""champ2"",""champ3"",""champ4"",""champ5"", etc.
CVS_st = CVS_st.substring(n);
pattern = Pattern.compile("(\".+?\"),");
matcher = pattern.matcher(CVS_st);
while (matcher.find())
{
System.out.println(matcher.group(1));
n = matcher.end();
}
// reached the last comma, now we should get
// form: ""dernierChamp"""
CVS_st = CVS_st.substring(n);
pattern = Pattern.compile("(\".+?\"\"\")");
matcher = pattern.matcher(CVS_st);
if (matcher.find())
{
System.out.println(matcher.group(1));
} else
{
// do something, since expected format is not found
}
}
}

cela donne :


"champ1
""champ2""
""champ3""
""champ4""
""champ5""
""champ6""
""dernierChamp"""

Note que, à mon sens, champ1 ne peut pas contenir de virgule, compte tenu du format que tu indiques.

En revanche, s'il y a des virgules dans les autres champs, elles n'interfèrent pas avec la capture.


Dal

Bonjour

La ligne est bien exacte.

Elle est constitué d'un seul champ, entouré de double quotes
À l'intérieur de ces doubles quotes, les double quotes sont échappées en les doublant.

L'unique champ (sous réserve qu'il n'y ait pas de double quotes uniques ou triples dans les pointillés) est :
champ1,"champ2","champ3",...,"dernierChamp" 
À mon avis, ce texte a subi un double encodage en CSV

Les règles d'encodage classiques (je dis classiques car il n'y a pas de standard à ma connaissance ) sont , en utilisant la virgule comme délimiteur (on peut aussi choisir le point-virgule) :
1 - chaque champ est entouré de double quotes s'il contient un espace, un double quote ou un délimiteur (l'article de wikipedia ne semble pas prendre en compte les espaces)
2 - les double-quotes sont doublés à l'intérieur des champs
3 - on met les champs bout à bout, séparés par le délimiteur

On a beaucoup trop tendance à ignorer 1 et 2 , sans lesquels le codage CSV serait ambigu

Pour décoder une ligne CSV, il faut la balayer en détectant si un champ commence ou non par un double quote, afin d'interpreter correctement les autres double quotes rencontrés (fin de champ ou faisant partie du champ) et les virgules (séparateur ou faisant partie du champ).
Messages postés
5591
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
23 septembre 2021
947
Salut le père,

Merci de ces précisions.

J'ai des doutes sur le fait que le champ1 dans l'exemple donné respecte ces règles (c'est
"champ1,
et pas
champ1,
), à moins que le premier guillemet écrit par plieuse ne soit en trop (ainsi que le dernier de la ligne, probablement, s'agissant du dernier champ). Mais plieuse a l'air de dire que c'est comme cela.

Il suffit de peu :-)

Les regexp utilisées dans le code proposé pourraient être adaptées pour fonctionner selon les règles que tu décris, si ce format était respecté, sous la forme d'une seule regexp (basée sur la 2ème, mais en plus compliquée).

Là j'ai proposé une solution adaptée à ce cas particulier, et non pour tout fichier CSV.


Dal

Edit : Je viens de relire ton message plus attentivement. Cependant, comme plieuse parle de champs et veux récupérer ces champs, je pense qu'elle n'est pas satisfaite de savoir que la ligne complète est un seul champ selon le format que tu décris.

Bonjour,
Je vous remercie pour vos réponses. Mon chef m'a demandé de me concentrer sur autre chose de sorte que je ne pourrai pas tester vos solutions tout de suite. En attendant, je clos le sujet.
Merci.
Marie