Une chaîne comme "Paris;Lyon;Nice" paraît simple, mais son découpage peut vite produire des résultats inattendus dès qu’il y a des espaces, des champs vides ou un séparateur spécial. La méthode split de Java permet de transformer une chaîne en tableau de sous-chaînes, à condition de bien comprendre son fonctionnement avec les expressions régulières et le paramètre limit. Je présente ici les usages essentiels, les pièges courants et les alternatives utiles dans un projet réel.
La méthode split devient prévisible avec trois règles simples
- split(String regex) découpe une chaîne autour d’une expression régulière.
- Le séparateur disparaît du résultat, tandis que les éléments deviennent des chaînes dans un tableau.
- Le paramètre limit contrôle le nombre de découpages et la conservation des éléments vides.
- Les caractères spéciaux comme le point ou la barre verticale doivent être échappés.
- Les données CSV complexes nécessitent souvent une véritable bibliothèque de parsing.
Comprendre ce que renvoie split en Java
La forme la plus courante est split(String regex). Elle cherche les correspondances du motif fourni, coupe la chaîne à ces endroits et renvoie un tableau de type String[]. Le séparateur lui-même n’apparaît pas dans le résultat.
String villes = "Paris;Lyon;Nice";
String[] resultat = villes.split(";");
System.out.println(Arrays.toString(resultat));
// [Paris, Lyon, Nice]
Dans cet exemple, le point-virgule est un séparateur littéral qui ne pose aucune difficulté. Pour lire les valeurs, on peut parcourir le tableau avec une boucle classique ou utiliser un flux. Personnellement, je commence presque toujours par afficher le tableau avec Arrays.toString, car cela révèle immédiatement les éléments vides que la console masque parfois dans un traitement plus long.
for (String ville : resultat) {
System.out.println(ville);
}
Si le motif ne correspond jamais à la chaîne, Java renvoie un tableau contenant la chaîne d’origine. Le résultat n’est donc pas null. En revanche, appeler split sur une référence String égale à null provoque une NullPointerException, ce qui mérite une vérification lorsque la valeur vient d’un formulaire, d’un fichier ou d’une API.
Choisir le séparateur sans se faire piéger par les regex
Le premier argument de split n’est pas toujours une simple chaîne de caractères. C’est une expression régulière, aussi appelée regex, c’est-à-dire un motif capable de décrire un ou plusieurs séparateurs. Cette particularité est pratique pour gérer plusieurs espaces, mais elle surprend dès qu’on utilise un caractère spécial.
String nombres = "10,20,30";
String[] valeurs = nombres.split(",");
String phrase = "Java est portable";
String[] mots = phrase.trim().split("\\s+");
Le motif \\s+ signifie « une ou plusieurs espaces blanches ». Il absorbe donc plusieurs espaces consécutifs, les tabulations et certains retours à la ligne. C’est plus robuste qu’un simple " " lorsque le texte provient d’une source irrégulière.
Les caractères suivants ont un sens particulier dans une regex. Pour les traiter comme des séparateurs ordinaires, il faut les protéger avec deux antislashs dans une chaîne Java.
| Caractère voulu | Motif Java | Exemple |
|---|---|---|
| Point | "\\." |
"a.b.c".split("\\.") |
| Barre verticale | "\\|" |
"a|b|c".split("\\|") |
| Plus | "\\+" |
"a+b+c".split("\\+") |
| Espace variable | "\\s+" |
"Java 21".split("\\s+") |
Le piège le plus connu concerne le point. Écrire "." ne signifie pas « point littéral », mais « n’importe quel caractère ». Pour un séparateur reçu dynamiquement, je préfère utiliser Pattern.quote, qui évite de mémoriser toutes les règles d’échappement.
String separateur = ".";
String chemin = "docs.java.api";
String[] morceaux = chemin.split(Pattern.quote(separateur));
Cette approche est particulièrement utile pour une configuration ou une donnée importée. Elle évite qu’un séparateur choisi par l’utilisateur soit interprété comme une regex inattendue.
Maîtriser limit et les champs vides
La version split(regex, limit) est celle que j’utilise dès que les champs vides ont une signification. Le paramètre limit modifie à la fois le nombre de découpages et le traitement des éléments vides situés à la fin.
| Valeur de limit | Comportement | Usage typique |
|---|---|---|
0 |
Découpe au maximum et supprime les chaînes vides finales. | Découpage simple de texte. |
> 0 |
Effectue au plus limit - 1 découpages. |
Conserver le reste dans le dernier élément. |
< 0 |
Découpe au maximum et conserve les chaînes vides finales. | Import de colonnes ou de valeurs positionnelles. |
Cette différence devient visible avec une chaîne qui se termine par un séparateur. Avec "A,,B,", la version sans limite ignore le dernier champ vide, alors que -1 le conserve.
String ligne = "A,,B,";
System.out.println(Arrays.toString(ligne.split(",")));
// [A, , B]
System.out.println(Arrays.toString(ligne.split(",", -1)));
// [A, , B, ]
Le deuxième élément vide entre les deux virgules est conservé dans les deux cas. C’est uniquement l’élément vide final qui disparaît avec la limite par défaut. Pour un fichier où chaque position correspond à une colonne précise, je recommande donc split(",", -1), puis une validation du nombre de colonnes.
Une limite positive sert à garder une partie non découpée. Par exemple, avec une adresse ou une ligne contenant une zone libre, on peut séparer les deux premiers champs et laisser le reste intact.
String valeur = "id|nom|commentaire avec | symbole";
String[] parties = valeur.split("\\|", 3);
System.out.println(Arrays.toString(parties));
// [id, nom, commentaire avec | symbole]
Le dernier élément récupère tout ce qui reste après les deux premiers découpages. C’est un comportement simple, mais il évite parfois de reconstruire manuellement une sous-chaîne.
Adapter le découpage aux données réelles
Un séparateur correct ne suffit pas toujours. Les données copiées depuis un formulaire ou un fichier contiennent souvent des espaces autour des virgules. Dans ce cas, un motif comme "\\s*,\\s*" retire les espaces avant et après le séparateur.
String saisie = "Paris, Lyon , Nice";
String[] villes = saisie.split("\\s*,\\s*");
System.out.println(Arrays.toString(villes));
// [Paris, Lyon, Nice]
Cette solution est très pratique pour une liste simple. Elle ne remplace toutefois pas un parseur CSV. Une ligne comme "Paris,\"Lyon, centre\",Nice" contient une virgule protégée par des guillemets, et split la traitera quand même comme un séparateur. Pour des fichiers CSV réels, avec guillemets, retours à la ligne ou caractères échappés, une bibliothèque spécialisée est plus sûre.
Je vérifie aussi systématiquement le contenu avant de convertir les éléments. split renvoie des chaînes, pas des nombres ni des dates. Une conversion directe avec Integer.parseInt échouera si un champ est vide ou contient un espace non prévu.
String donnees = "12, 45, 78";
String[] morceaux = donnees.split("\\s*,\\s*");
for (String morceau : morceaux) {
int nombre = Integer.parseInt(morceau);
System.out.println(nombre);
}
Dans une application exposée à des données externes, je préfère entourer la conversion d’une validation claire. Il faut contrôler les champs obligatoires, le nombre d’éléments et le format attendu avant de considérer le tableau comme fiable.
Quand split n’est pas le meilleur outil
Pour une chaîne courte et un séparateur simple, String.split est généralement le choix le plus lisible. Il ne faut pas remplacer cette méthode par une solution plus complexe uniquement pour gagner quelques microsecondes dans un code exécuté une seule fois.
En revanche, si le même motif est utilisé des milliers de fois, compiler la regex une seule fois peut être pertinent. La classe Pattern permet de réutiliser le motif et propose aussi splitAsStream, utile lorsque le traitement peut rester dans un flux.
Pattern separateur = Pattern.compile("\\s*,\\s*");
String[] premiereLigne = separateur.split("Paris, Lyon, Nice");
String[] secondeLigne = separateur.split("Bordeaux, Lille, Brest");
Le flux est intéressant si l’on veut filtrer ou transformer les éléments sans conserver immédiatement tout le tableau en mémoire.
Pattern separateur = Pattern.compile("\\s*,\\s*");
long total = separateur.splitAsStream("12, 45, 78")
.mapToInt(Integer::parseInt)
.sum();
StringTokenizer peut encore apparaître dans du code ancien, mais je ne le choisis pas pour un nouveau développement. Il est moins expressif, ne fonctionne pas comme une regex et gère différemment les éléments vides. Pour du texte structuré, split ou un parseur dédié rendent l’intention beaucoup plus claire.
Enfin, si l’objectif est de découper une phrase en unités linguistiques, un simple séparateur ne suffit pas toujours. Les emojis, les caractères Unicode combinés et les règles propres aux langues demandent une logique différente de celle qui consiste à couper une chaîne autour d’un motif.
Le bon réflexe avant de valider son découpage
Pour un usage courant, je retiens une règle simple. Utilisez split(regex) pour un découpage basique, split(regex, -1) lorsque les champs vides comptent, et Pattern.quote lorsque le séparateur vient d’une source variable.
Testez toujours les cas limites avec une chaîne vide, deux séparateurs consécutifs, un séparateur final, des espaces et une valeur sans séparateur. Ces cinq tests révèlent la majorité des erreurs de production bien plus vite qu’un exemple parfaitement propre.
Le découpage n’est que la première étape. Une fois le tableau obtenu, validez sa longueur, nettoyez les valeurs si nécessaire et convertissez-les explicitement. C’est cette discipline qui transforme une méthode très courte en traitement réellement fiable.