W3docs

Java StringTokenizer

Découpez des chaînes en tokens en Java avec la classe héritée StringTokenizer, et quand préférer String.split à la place.

java.util.StringTokenizer est la classe originale du JDK pour « découper une chaîne en morceaux » — un tokenizer caractère par caractère qui fait partie de la plateforme depuis la version 1.0. La Javadoc elle-même le déconseille pour le nouveau code : « StringTokenizer est une classe héritée maintenue pour des raisons de compatibilité, bien que son utilisation soit déconseillée dans le nouveau code. Il est recommandé que quiconque cherche cette fonctionnalité utilise la méthode split de String ou le package java.util.regex à la place. »

C'est le message principal, et il est exact — mais StringTokenizer apparaît encore dans des bases de code réelles et présente quelques niches légitimes. Ce chapitre l'enseigne brièvement, puis explique clairement quand utiliser String.split ou Scanner à la place.

La boucle de base

Un StringTokenizer est une Enumeration de sous-chaînes :

StringTokenizer st = new StringTokenizer("apple banana cherry");
while (st.hasMoreTokens()) {
  System.out.println(st.nextToken());
}
// apple
// banana
// cherry

Le jeu de délimiteurs par défaut est les espaces blancs — espace, tabulation, saut de ligne, retour chariot, saut de page. Les délimiteurs adjacents sont fusionnés : " a b " produit exactement deux tokens.

Délimiteurs personnalisés

Le deuxième constructeur prend une chaîne dont les caractères sont chacun traités comme un délimiteur — et non comme une chaîne délimiteur :

StringTokenizer st = new StringTokenizer("one,two;three|four", ",;|");
while (st.hasMoreTokens()) {
  System.out.println(st.nextToken());
}
// one
// two
// three
// four

C'est la particularité de conception qui piège les gens. new StringTokenizer(input, ", ") traite , et l'espace chacun comme un délimiteur, pas la séquence de deux caractères ", ". Si vous avez besoin de délimiteurs multi-caractères, vous avez dépassé les capacités de StringTokenizer.

Retourner les délimiteurs eux-mêmes

Le constructeur à trois arguments contrôle si les délimiteurs sont eux-mêmes émis comme tokens :

StringTokenizer st = new StringTokenizer("a+b*c", "+*", true);
while (st.hasMoreTokens()) {
  System.out.println(st.nextToken());
}
// a
// +
// b
// *
// c

C'est la seule fonctionnalité que String.split ne reproduit pas directement : vous le construiriez avec un Matcher et une regex. Pour l'analyse d'expressions très simples — le genre qui ne justifie pas l'utilisation d'un lexer — cette surcharge reste utile.

Compter les tokens à l'avance

countTokens() indique combien de tokens restent (c'est-à-dire ceux qui seraient produits par des appels répétés à nextToken()). Il ne les consomme pas.

StringTokenizer st = new StringTokenizer("a b c d");
int n = st.countTokens();        // 4
while (st.hasMoreTokens()) st.nextToken();
n = st.countTokens();            // 0

Cela est parfois utile pour allouer un tableau de sortie de la bonne taille — bien qu'avec String.split vous appelleriez simplement .split(...).length.

Changer le délimiteur en cours de traitement

Une fonctionnalité peu connue : nextToken(String newDelims) réinitialise le jeu de délimiteurs pour cet appel et les suivants. Une fois modifié, le nouveau jeu persiste pour les appels suivants à hasMoreTokens()/nextToken() jusqu'à ce que vous le changiez à nouveau.

StringTokenizer st = new StringTokenizer("key1=value1; key2=value2; key3=value3");
while (st.hasMoreTokens()) {
  String pair = st.nextToken("; ").trim();  // tokens separated by ';' or space
  System.out.println(pair);
}

Pour une analyse ponctuelle ad hoc, cela peut être élégant. Pour tout ce qui doit être maintenable, c'est source de confusion — les lecteurs ne s'attendent pas à ce qu'un jeu de délimiteurs change à l'intérieur d'une boucle.

Pourquoi String.split est généralement meilleur

Les raisons de préférer String.split (ou Pattern.compile(...).split) pour le nouveau code :

  • De vrais délimiteurs regex. Délimiteurs multi-caractères, classes de caractères, alternatives — tout est naturel. StringTokenizer ne gère que des délimiteurs à un seul caractère.
  • Les tokens vides sont visibles. "a,,b".split(",") retourne ["a", "", "b"]. StringTokenizer ignore silencieusement le token vide. Pour les entrées de type CSV, « le deuxième champ était vide » est une information dont vous avez généralement besoin.
  • Retourne un tableau. Facile à indexer, facile à convertir en List, facile à streamer.
  • Généralement plus rapide sous JIT grâce à la mise en cache de Pattern pour les patterns de découpage simples.
  • Plus facile à tester, plus facile à lire. Une boucle de tokenizer ressemble à du code de 1996 ; parts = csv.split(",") exprime clairement l'intention.

Pourquoi vous pourriez quand même le choisir

Une courte liste de cas où StringTokenizer reste défendable :

  • Traitement en flux d'une très longue chaîne où vous souhaitez consommer token par token sans conserver un tableau de tous les tokens. StringTokenizer n'alloue pas le résultat à l'avance ; split le fait.
  • Retourner les délimiteurs comme tokens pour le tokenizer le plus simple possible, sans recourir à Pattern/Matcher.
  • Maintien d'ancien code où le reste du fichier l'utilise et où la cohérence est ce qu'il y a de mieux pour le prochain lecteur.

Pour tout le reste : utilisez split.

Un exemple travaillé

Le programme ci-dessous tokenise trois entrées de trois façons différentes, côte à côte avec les appels split équivalents, afin que les différences comportementales soient visibles :

java— editable, runs on the server

Deux enseignements à tirer de la sortie. Dans le cas 1, split rapporte la cellule vide entre green et blue ([red, green, , blue]) ; le tokenizer la fusionne ([red, green, blue]). Dans le cas 2, les deux produisent les mêmes tokens, mais pour des raisons différentes : le tokenizer découpe mix à chaque ; et à chaque espace indépendamment ("; " signifie « l'un ou l'autre caractère est un délimiteur »), tandis que split("; ") correspond à la séquence littérale de deux caractères "; ". Ils s'accordent ici uniquement parce que les séparateurs dans mix sont exactement ; . Changez l'entrée en "k1=v1 ;k2=v2" et les deux divergent immédiatement. Quel que soit le comportement souhaité, le code doit être explicite à ce sujet — et c'est beaucoup plus facile avec split.

La suite

Jusqu'ici, nous avons découpé des chaînes en morceaux. Souvent, ce dont vous avez réellement besoin, c'est de transformer une chaîne en nombre, boolean ou autre primitif — et dans l'autre sens, des primitifs vers des chaînes. Ce va-et-vient possède son propre ensemble d'utilitaires et de pièges. Continuez vers Conversions de chaînes Java.

Exercices

Pratique
Laquelle des affirmations suivantes représente une **différence comportementale réelle** entre `StringTokenizer` et `String.split` ?
Laquelle des affirmations suivantes représente une **différence comportementale réelle** entre `StringTokenizer` et `String.split` ?
Was this page helpful?