3 votes

Expression régulière pour sélectionner les cinq premiers CSV d'une chaîne de caractères

J'ai une chaîne CSV comme apple404, orange pie, wind\,cool, sun\\mooon, earth, en Java. Pour être précis, chaque valeur de la chaîne csv peut être n'importe quoi, à condition que les virgules et les barres obliques inverses soient échappées à l'aide d'une barre oblique inversée.

J'ai besoin d'une expression régulière pour trouver les cinq premières valeurs. Après quelques recherches, j'ai trouvé ce qui suit. Mais elle n'autorise pas les virgules échappées dans les valeurs.

Pattern pattern = Pattern.compile("([^,]+,){0,5}");
Matcher matcher = pattern.matcher("apple404, orange pie, wind\\,cool, sun\\\\mooon, earth,");
    if (matcher.find()) {
        System.out.println(matcher.group());
    } else {
        System.out.println("No match found.");
    }

Quelqu'un sait-il comment faire fonctionner le système pour les virgules échappées dans les valeurs ?

2voto

anubhava Points 172509

Les expressions rationnelles négatives suivantes, basées sur le look-behind, fonctionneront :

 Pattern pattern = Pattern.compile("(?:.*?(?<!(?:(?<!\\\\)\\\\)),){0,5}");

Cependant, pour une analyse CSV complète, il est préférable d'utiliser un analyseur CSV dédié tel que JavaCSV .

1voto

Joseph Myers Points 3267

Cette expression régulière fonctionne bien. Elle reconnaît correctement non seulement les virgules encapsulées par le backslash, mais aussi les backslashs encapsulés par le backslash. De plus, les correspondances qu'elle produit ne contiennent pas les virgules.

/(?:\\\\|\\,|[^,])*/g

(J'utilise la notation standard des expressions régulières, étant entendu que vous remplacerez les délimiteurs par des guillemets et doublerez toutes les barres obliques inverses lorsque vous représenterez cette expression régulière dans un littéral de chaîne Java).

exemple de saisie

"Apple404, tarte à l'orange, vent, fraîcheur, soleil \\ mooon, earth" (lune, terre)

produit le résultat suivant

  1. "apple404"

  2. "tarte à l'orange"

  3. Wind\N- "cool" - "cool" - "cool"

  4. " soleil \\ "

  5. "mooon"

Notez que la double barre oblique inverse après "sun" est échappée et n'échappe donc pas à la virgule suivante.

Cette expression régulière fonctionne en atomisant l'entrée en séquences les plus longues, en commençant par les doubles barres obliques inversées (en les traitant comme une alternative possible de valeur de caractère sur plusieurs octets), suivies par les virgules échappées (une deuxième alternative possible de caractère sur plusieurs octets), suivies par n'importe quelle valeur non-comma. N'importe quel nombre de ces atomes est pris en compte, suivi d'une virgule littérale.

Pour obtenir les N premiers champs, on peut simplement scinder le tableau de correspondances de la réponse précédente ou entourer l'expression principale de parenthèses supplémentaires, inclure une virgule facultative afin de faire correspondre le contenu entre les champs, l'ancrer au début de la chaîne pour éviter que le moteur ne renvoie d'autres groupes de N champs, et la quantifier (avec N = 5 ici) :

/^((?:\\\\|\\,|[^,])*,?){0,5}/g

Une fois de plus, j'utilise la notation standard des expressions régulières, mais je vais également faire l'exercice trivial de citer cette expression comme une chaîne Java :

"^((?:\\\\\\\\|\\\\,|[^,])*,?){0,5}"

Il s'agit de la seule solution présentée sur cette page jusqu'à présent qui réponde réellement aux deux parties des exigences précises spécifiées par le PO, "...les virgules et les barres obliques inverses sont échappées à l'aide d'une barre oblique inversée. ." Pour l'entrée fi\,eld1\\,field2\\,field3\\,field4\\,field5\\,field6\\, il ne correspond correctement qu'aux cinq premiers champs fi\,eld1\\,field2\\,field3\\,field4\\,field5\\, .

Note : ma première réponse reposait sur la même hypothèse que celle qui fait implicitement partie du code original et de l'exemple de données de l'OP, à savoir qu'une virgule doit suivre chaque champ. Le problème était que si l'entrée comporte exactement 5 champs ou moins, et que le dernier champ n'est pas suivi d'une virgule (ou d'un champ vide), le dernier champ n'est pas pris en compte. Je n'ai pas apprécié cette situation et j'ai donc mis à jour mes deux réponses afin qu'elles ne nécessitent pas de virgules.

Le défaut de cette réponse est qu'elle suit l'hypothèse du PO selon laquelle les valeurs entre virgules contiennent "n'importe quoi" plus les virgules échappées ou les barres obliques inverses échappées (c'est-à-dire qu'il n'y a pas de distinction entre les chaînes de caractères entre guillemets doubles, etc. mais seulement la reconnaissance des virgules échappées et des barres obliques inverses). Ma réponse répond aux critères de ce scénario imaginaire. Mais dans le monde réel, quelqu'un s'attendrait à pouvoir utiliser des guillemets doubles autour d'un champ CSV afin d'inclure des virgules dans un champ sans utiliser de barres obliques inverses.

Je me fais donc l'écho de @anubhava et suggère qu'un "vrai" parseur CSV soit toujours utilisé lors de la manipulation de données CSV. Dans le cas contraire, il s'agit simplement d'un script enfantin et non d'un véritable "traitement" des données CSV.

Java evaluation results

1voto

Ravi Thapliyal Points 19305

Vous pouvez utiliser String.split() ici. En spécifiant le limit como 6 les cinq premiers éléments (index 0 a 4 ) seront toujours les cinq premières valeurs de colonne de votre chaîne CSV. Si dans le cas où des valeurs de colonnes supplémentaires seraient présentes, elles déborderaient toutes sur l'index 5 .

L'expression rationnelle (?<!\\\\), s'assure que la chaîne CSV n'est scindée qu'au niveau d'un , virgule non précédée d'un \ .

String[] cols = "apple404, orange pie, wind\\,cool, sun\\\\mooon, earth, " +
                "mars, venus, pluto".split("(?<!\\\\),", 6);

System.out.println(cols.length); // 6
System.out.println(Arrays.toString(cols));
// [apple404,  orange pie,  wind\,cool,  sun\\mooon,  earth,  mars, venus, pluto]

System.out.println(cols[4]); // 5th = earth 
System.out.println(cols[5]); // 6th discarded = mars, venus, pluto

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X