Essayer:
public class Main {
public static void main(String[] args) {
String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String[] tokens = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
for(String t : tokens) {
System.out.println("> "+t);
}
}
}
Production:
> foo
> bar
> c;qual="baz,blurb"
> d;junk="quux,syzygy"
En d'autres termes: divisez la virgule uniquement si cette virgule a zéro, ou un nombre pair de guillemets devant elle .
Ou, un peu plus convivial pour les yeux:
public class Main {
public static void main(String[] args) {
String line = "foo,bar,c;qual=\"baz,blurb\",d;junk=\"quux,syzygy\"";
String otherThanQuote = " [^\"] ";
String quotedString = String.format(" \" %s* \" ", otherThanQuote);
String regex = String.format("(?x) "+ // enable comments, ignore white spaces
", "+ // match a comma
"(?= "+ // start positive look ahead
" (?: "+ // start non-capturing group 1
" %s* "+ // match 'otherThanQuote' zero or more times
" %s "+ // match 'quotedString'
" )* "+ // end group 1 and repeat it zero or more times
" %s* "+ // match 'otherThanQuote'
" $ "+ // match the end of the string
") ", // stop positive look ahead
otherThanQuote, quotedString, otherThanQuote);
String[] tokens = line.split(regex, -1);
for(String t : tokens) {
System.out.println("> "+t);
}
}
}
qui produit le même que le premier exemple.
ÉDITER
Comme mentionné par @MikeFHay dans les commentaires:
Je préfère utiliser le séparateur de Guava , car il a des valeurs par défaut plus saines (voir la discussion ci-dessus à propos des correspondances vides qui sont coupées par String#split()
, donc je l'ai fait:
Splitter.on(Pattern.compile(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"))
String line = "equals: =,\"quote: \"\"\",\"comma: ,\""
, tout ce que vous avez à faire est de supprimer le guillemet double superflu personnages.-1
à la méthode split param:line.split(regex, -1)
. Voir: docs.oracle.com/javase/6/docs/api/java/lang/…Splitter.on(Pattern.compile(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"))
.findAllIn("(?s)(?:\".*?\"|[^\",]*)*")
en combinaison avec une étape de post-traitement pour ignorer le premier champ (toujours vide) après chaque champ non vide.Bien que j'aime les expressions régulières en général, pour ce type de tokenisation dépendant de l'état, je crois qu'un simple analyseur (qui dans ce cas est beaucoup plus simple que ce mot pourrait le faire entendre) est probablement une solution plus propre, en particulier en ce qui concerne la maintenabilité , par exemple:
Si vous ne vous souciez pas de conserver les virgules à l'intérieur des guillemets, vous pouvez simplifier cette approche (pas de gestion de l'index de démarrage, pas de casse de dernier caractère ) en remplaçant vos virgules entre guillemets par autre chose, puis divisées en virgules:
la source
http://sourceforge.net/projects/javacsv/
https://github.com/pupi1985/JavaCSV-Reloaded (fork de la bibliothèque précédente qui permettra à la sortie générée d'avoir des terminateurs de ligne Windows
\r\n
lorsqu'ils n'exécutent pas Windows)http://opencsv.sourceforge.net/
API CSV pour Java
Pouvez-vous recommander une bibliothèque Java pour lire (et éventuellement écrire) des fichiers CSV?
Librairie Java ou application pour convertir CSV en fichier XML?
la source
Je ne conseillerais pas une réponse regex de Bart, je trouve la solution d'analyse meilleure dans ce cas particulier (comme Fabian l'a proposé). J'ai essayé une solution regex et une implémentation d'analyse propre, j'ai constaté que:
Ma solution et test ci-dessous.
Bien sûr, vous êtes libre de changer de commutateur pour les autres éléments de cet extrait si vous vous sentez mal à l'aise avec sa laideur. A noter alors absence de rupture après passage avec séparateur. StringBuilder a été choisi à la place de StringBuffer par conception pour augmenter la vitesse, là où la sécurité des threads n'est pas pertinente.
la source
-1
à la méthode de fractionnement dans la réponse de Bart, vous attraperez des chaînes vides (y compris des chaînes vides après la dernière virgule):line.split(regex, -1)
Essayez un lookaround comme
(?!\"),(?!\")
. Cela devrait correspondre à ceux,
qui ne sont pas entourés"
.la source
(?<!"),(?!")
, mais ça ne marchera toujours pas. Étant donné la chaîneone,two,"three,four"
, elle correspond correctement à la virguleone,two
, mais elle correspond également à la virgule"three,four"
et ne parvient pas à en faire correspondre unetwo,"three
.Vous êtes dans cette zone de frontière ennuyeuse où les expressions rationnelles ne feront presque pas l'affaire (comme l'a souligné Bart, échapper aux citations rendrait la vie difficile), et pourtant un analyseur à part entière semble exagéré.
Si vous êtes susceptible d'avoir besoin d'une plus grande complexité de sitôt, j'irais à la recherche d'une bibliothèque d'analyseur. Par exemple celui-ci
la source
J'étais impatient et j'ai choisi de ne pas attendre les réponses ... pour référence, il ne semble pas si difficile de faire quelque chose comme ça (qui fonctionne pour mon application, je n'ai pas besoin de m'inquiéter des citations échappées, comme les choses entre guillemets) est limité à quelques formes contraintes):
(exercice pour le lecteur: étendez la gestion des citations échappées en recherchant également des barres obliques inverses.)
la source
L'approche la plus simple n'est pas de faire correspondre les délimiteurs, c'est-à-dire les virgules, avec une logique supplémentaire complexe pour faire correspondre ce qui est réellement prévu (les données qui pourraient être des chaînes de caractères), juste pour exclure les faux délimiteurs, mais plutôt faire correspondre les données prévues en premier lieu.
Le modèle se compose de deux alternatives, une chaîne entre guillemets (
"[^"]*"
ou".*?"
) ou tout jusqu'à la prochaine virgule ([^,]+
). Pour prendre en charge les cellules vides, nous devons autoriser l'élément non cité à être vide et consommer la virgule suivante, le cas échéant, et utiliser l'\\G
ancre:Le modèle contient également deux groupes de capture pour obtenir soit le contenu de la chaîne citée, soit le contenu brut.
Ensuite, avec Java 9, nous pouvons obtenir un tableau comme
alors que les anciennes versions de Java ont besoin d'une boucle comme
L'ajout des éléments à un
List
ou à un tableau est laissé comme accise au lecteur.Pour Java 8, vous pouvez utiliser l'
results()
implémentation de cette réponse , pour le faire comme la solution Java 9.Pour un contenu mixte avec des chaînes intégrées, comme dans la question, vous pouvez simplement utiliser
Mais ensuite, les chaînes sont conservées dans leur forme entre guillemets.
la source
Plutôt que d'utiliser l'anticipation et d'autres expressions rationnelles folles, retirez d'abord les guillemets. Autrement dit, pour chaque groupe de devis, remplacez ce groupe par
__IDENTIFIER_1
ou un autre indicateur et mappez ce groupe à une carte de chaîne, chaîne.Une fois que vous avez fractionné sur une virgule, remplacez tous les identificateurs mappés par les valeurs de chaîne d'origine.
la source
que diriez-vous d'un one-liner en utilisant String.split ()?
la source
Je ferais quelque chose comme ça:
la source