Tous les traitements possibles avec les chaînes

Chapitres traités   

Après avoir vu comment formater des nombres et des dates afin de produire des messages adaptés à pas mal de situations, nous allons passer cette fois-ci un peu plus de temps sur le texte et sur tous les autres types de traitements qui s'y rattachent. Effectivement, la plupart des programmes manipulent du texte d'une manière ou d'une autre, aussi la plate-forme Java définit-elle un certain nombre de classes importantes pour représenter, formater, et parcourir du texte.

La représentation du texte se fait principalement au travers de la classe String que nous connaissons déjà. Nous allons toutefois y revenir pour décrire bien plus précisément l'ensemble des méthodes qui la constitue.

Toujours au travers de cette classe String, le formatage va nous permettre d'associer des variables d'une autre nature (des entiers, en hexadécimal ou pas, des réels, en représentation flottante ou pas, etc. ) avec une chaîne de caractères qui sert de canevas. A ce sujet, nous travaillerons également avec la classe Scanner qui réalise du formatage automatique avec des chaînes de caractères. Nous profiterons également de revoir tous les problèmes de conversion entre les chaînes de caractères avec les types primitifs, avec donc un retour sur les classes enveloppes, comme Integer, Double, etc.

Pour terminer sur cette introduction, nous avons souvent besoin d'analyser des textes afin de contrôler si un certain motif est respecter dans la chaîne de caractères. Nous allons donc travailler sur ce que nous appelons les expressions régulières qui gèrent des motifs correspondant à des critères d'évaluation.

Choix du chapitre Les actions principales de la classe String

Les chaînes de caractères correspondent à un type de données fondamental et couramment utilisé. En java, cependant, les chaînes de caractères ne correspondent pas à un type primitif, à la différence de char, int ou double. En lieu et place, les chaînes de caractères sont représentées par la classe java.lang.String qui définit de nombreuses méthodes pour la manipulation des chaînes de caractères. Les objets String sont immuables : une fois qu'un objet String a été créé, il n'existe aucune possibilité de modifier la chaîne de caractères qu'il représente. Ainsi, chaque méthode qui agit sur une chaîne de caractères retourne généralement un nouvel objet String qui contient la chaîne de caractères modifiée.

Un certain nombre de méthodes manipulent des objets String et renvoient un nouvel objet String comme résultat. Même si cela est utile, vous devez savoir qu'en créant beaucoup de chaînes de caractères de cette manière, les performances de l'application peuvent être affectées. Si vous désirez que votre application soit plus performante en proposant beaucoup de manipulation sur la même chaîne de caractères, préférez plutôt la classe StringBuilder que nous traiterons dans le prochain chapitre.

java.lang.String
Méthodes Retour Explication
charAt(int index) char Récupère un caractère particulier dans la chaîne dont la position est précisée en argument.
codePointAt(int index) int Renvoie le point de code qui démarre ou se termine à l'emplacement spécifié.
codePointCount(int début, int fin) int Renvoie le nombre de points de code entre début et fin-1. Les substitutions sans paires sont considérées comme des points de code.
compareTo(String autreChaîne)
compareToIgnoreCase(String autreChaîne)
int Compare la chaîne avec une autre chaîne passée en argument. Possibilité de ne pas tenir compte de la casse des caractères avec la deuxième méthode.
concat(String autreChaîne) String Concatène la chaine avec une autre.
endWith(String suffixe) boolean Vérifie si la chaîne de caractère se termine par le suffixe précisé en argument.
equals(String autreChaîne)
equalsIgnoreCase(String autreChaîne)
Object Compare la chaîne à une autre en ne tenant pas compte de la casse des caractères éventuellement.
format(String format, Object ... args) String Méthode statique qui permet de formater un texte, en relations avec les arguments proposés, dans un motif défini à la manière de la fonction printf() du C++.
indexOf(String sousChaîne)
indexOf(int car)
indexOf(String sousChaîne, int décalage)
indexOf(int car, int décalage)
int Cherche la première occurence d'un caractère ou d'une partie de la chaîne.
intern() String Cherche et renvoie une instance unique de la chaîne dans l'ensemble des chaînes partagées.
lastIndexOf(String sousChaîne)
lastIndexOf(int car)
lastIndexOf(String sousChaîne, int index)
lastIndexOf(int car, int index)
int Cherche la dernière occurence d'un caractère ou d'une sous-chaîne.
length() int Renvoie la longueur de la chaîne.
match(String motif) boolean Evalue que la chaîne de caractères correspond au critère spécifié par le motif proposé exprimé sous la forme d'expression régulière.
offsetByteCodePoints(int début, int pointCode) int Renvoie l'indice du point de code d'où pointe pointCode, depuis le point de code jusqu'à début.
replace(char ancien, char nouveau) String Remplace toutes les occurences d'un caractère dans une chaîne par un autre caractère.
replaceAll(String motif, String remplacement)
replaceFirst(String motif, String remplacement)
String Remplace toutes les occurences ou la première partie de la chaîne de caractère avec la chaîne de remplacement spécifiée en argument à partir d'un motif défini par une expression régulière.
split(String motif) String[] Décompose la chaîne en un tableau de chaînes en utilisant comme séparateur un élément d'expression régulière.
startWith(String prefixe) boolean Vérifie si la chaîne débute par un prefixe.
subString(int début)
subString(int début, int fin)
String Retourne une partie de la chaîne. Attention, le caractère indicé par début est pris en compte, alors que le caractère correspondant à fin ne l'est pas ; fin représente la limite supérieure exclusive.
toLowerCase() String Convertit la chaîne en minuscule.
toUpperCase() String Convertit la chaîne en majuscule.
trim() String Supprime les espaces blancs de début et de fin de chaîne.
valueOf(Type valeur) String Renvoie une chaîne de caractères correspondant à la valeur numérique passée en paramètre de la méthode. Type = int, long, double, float, Object, char[], char, boolean.

Dans ce chapitre, nous allons nous consacrer uniquement sur les méthodes de bases. Nous verrons par la suite comment travailler avec les valeurs numériques, comment formater de telles valeurs, comment récupérer des valeurs quelconques à partir de la classe Scanner, et surtout comment travailler avec les expressions régulières, puisqu'il s'agit d'un sujet important. Tous ces autres points seront, bien entendus traités, mais dans les prochains chapitres.

Création d'un objet chaîne de caractère

Si vous désirez créer un objet String à partir d'un message spécifique, il suffit de placer ce dernier entre guillemets. Lorsque vous déclarez un objet de type String sans préciser de valeur, votre objet n'est pas initialisé. Il faudra le faire par la suite. Rappelez-vous d'ailleurs que comme tout autre objet, le nom de l'objet correspond en réalité à un pointeur vers l'objet réel où sont placées toutes ses valeurs internes. Pour terminer, un texte exprimé entre guillemets dans votre code est finalement considéré comme un objet de type String.

String message = "Mon texte personnel"; // déclaration et initialisation de l'objet message
String texte;  // déclaration de l'objet texte sans être initialisé
System.out.println(texte);  // erreur de compilation
texte = "Autre message";   // initialisation de l'objet texte
System.out.println(message);  // affichage de message
System.out.println(texte); // affichage de texte

Détermination de la longueur d'une chaîne

Comme toujours, les tableaux Java étant de vrais objets qui connaissent leur propre taille, les objets String n'ont pas besoin de caractère de terminaison. Pour connaître la taille d'un objet String, utilisez pour cela sa méthode length().

String message = "Mon texte personnel"; 
int longueur = message.length(); // longueur = 19
longueur = "Autre message".length(); // longueur = 13

La chaîne de caractères représentées par le texte entre guillemets, comme nous l'avons dit plus haut, est considéré comme un objet de type String. A ce titre, vous avez donc le droit de solliciter toutes les méthodes que vous désirez à partir de ce texte, comme ici avec la méthode length().

Concaténation de chaînes de caractères

Il est possible de juxtaposer plusieurs chaînes, les unes à la suite des autres, ce que nous appelons une concaténation de chaînes. Il existe la méthode concat() qui réalise ce type d'opération. Egalement, l'opérateur + a été redéfini pour concaténer deux objets String ou un objet String avec une valeur d'un autre type, produisant en sortie un nouvel objet String. Pour finir, nous pouvons également travailler avec l'opérateur += pour concaténer deux chaînes. Dans ce cas là, le contenu de la première chaîne est modifié.

public class Principal {
   public static void main(String[] args) {
      String message = " le moment";
      String texte = "C'est".concat(message);
      String s = texte +" d'intégrer le nombre réel "+12.89;
      s += " dans ce message";
      String autre = "C'est"+message.concat(" d'intégrer le nombre réel ")+12.89;
      System.out.println(s);
      System.out.println(autre);
   }
}
Résultats

C'est le moment d'intégrer le nombre réel 12.89 dans ce message
C'est le moment d'intégrer le nombre réel 12.89

 

Soyez conscient que chaque fois qu'une concaténation de chaînes de caractères est réalisée et que le résultat est enregistré dans une variable ou passé à une méthode, un nouvel objet String doit être créé. Dans certaines circonstances, cela peut s'avérer très peu efficace et dégrader les performances.

Les chaînes littérales (entre guillemets) ne peuvent occuper plusieurs lignes sur le code source, mais il est possible de les concaténer pour obtenir le même résultat :

String grandMessage = "Voici un grand message qui doit " +
         "s'écrire sur plusieurs lignes. Je suis obligé d'utiliser " +
         "l'opérateur + pour cela";

Récupérer un seul caractère de la chaîne

La méthode charAt() de la classe String permet de récupérer un seul caractère de la chaîne. Vous devez spécifier sa position dans la chaîne au travers du paramètre de la méthode :

Résultats

S
a
l
u
t

public class Principal {
   public static void main(String[] args) {
      String bonjour = "Salut";
      for (int i=0; i<bonjour.length(); i++)
         System.out.println(bonjour.charAt(i));
   }
}

Travailler sur une partie de la chaîne de caractères

La classe String propose plusieurs méthodes simples permettant de retrouver des parties de la chaîne. Les méthodes startsWith() et endsWith() comparent un argument avec le début et la fin de la chaîne. Il est possible de récupérer une partie de la chaîne à l'aide de la méthode substring(). Par contre, cette méthode réclame, d'une part où débute la sous-chaîne et d'autre part où elle se termine, ceci au travers d'indices numériques que vous spécifiez dans les arguments. A propos d'indice, il est possible également de délivrer l'emplacement d'un caractère ou d'une partie de la chaîne à partir de la méthode indexOf(). Cette méthode recherche la première occurence. Par contre, la méthode lastIndexOf() fait la même chose, mais à partir de la dernière occurence.

Résultat

Nom du site : unsite

 
public class Principal {
   public static void main(String[] args) {
      String url = "http://www.unsite.fr/";
      if (url.startsWith("http://") && url.endsWith("fr/")) {
         int début = url.indexOf('.')+1;
         int fin = url.lastIndexOf(".");
         String site = "Nom du site : "+ url.substring(début, fin);
         System.out.println(site);
      }
   }
}

Modifications sur la chaîne de caractères

La méthode trim() est très utile pour supprimer tous les espaces blancs aux deux extrémités d'une chaîne de caractères (c'est-à-dire les caractères retour chariot, nouvelle ligne, tabulation et tout simplement les espaces). Par ailleurs, les méthodes toUpperCase() et toLowerCase() renvoient un nouvel objet String qui est, soit tout en majuscules, soit tout en minuscules.

Résultats

En majuscules : BONJOUR À TOUT LE MONDE
En minuscules : bonjour à tout le monde

 
public class Principal {
   public static void main(String[] args) {
      String bienvenue = "    Bonjour à tout le monde   ";
      bienvenue = bienvenue.trim();
      System.out.println("En majuscules : "+bienvenue.toUpperCase());
      System.out.println("En minuscules : "+bienvenue.toLowerCase());
   }
}

Comparaison de chaîne de caractères

Puisque les chaînes de caractères sont des objets plutôt que des valeurs primitives, elles ne peuvent généralement pas être comparées directement avec l'opérateur d'égalité ==. Si nous le faisons, == compare des références et peut déterminer si deux expressions pointent vers une même référence de chaîne de caractères. Par contre, cet opérateur d'égalité ne peut pas déterminer si deux chaînes de caractères distinctes contiennent le même texte.

Résultats

Bonjour
false

 
import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);
      String message = "Bonjour";
      String saisie = clavier.next();
      System.out.println(message==saisie);
   }
}

Pour tester l'égalité de deux chaînes de caractères, utilisez plutôt la méthode equals(). Il est possible également d'utiliser la méthode equalsIgnoreCase() en lieu et place de la méthode equals() si vous désirez ne pas tenir compte de la casse des caractères.

Résultats

Bonjour
true
true

 
import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);
      String message = "Bonjour";
      String saisie = clavier.next();
      System.out.println(message.equals(saisie));
      System.out.println("BONJOUR".equalsIgnoreCase(saisie));
   }
}

Bien que méconnue, intern() est une méthode importante de la classe String. Cette méthode retourne une nouvelle chaîne de caractères avec la garantie que la nouvelle chaîne possède le même contenu. En réalité, intern() retourne toujours une référence vers le même objet String. Autrement dit, si s et t sont deux objets String tels que s.equals(t), alors :

s.intern() == t.intern()

Cela signifie que la méthode intern() permet de réaliser des comparaisons de chaînes rapides en utilisant ==. Plus important encore, les littéraux chaînes sont toujours internés par la JVM Java de façon implicites, de sorte que si vous prévoyez de comparer une chaîne s avec un certain nombre de littéraux de chaînes, vous devriez interner s puis effectuer la comparaison avec ==.

Résultats

Bonjour
true
true
true
false

 
import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);
      String message = "Bonjour";
      String bienvenue = "Bonjour";
      String saisie = clavier.next();
      System.out.println(message==bienvenue);
      System.out.println("Bonjour"==bienvenue);
      System.out.println(message.equals(saisie));
      System.out.println(saisie=="Bonjour");
   }
}

La méthode compareTo() permet de comparer la valeur lexicale d'un String à un autre String. Elle renvoie un entier strictement inférieur, égal ou strictement supérieur à 0. compareToIgnoreCase() fait la même chose, mais sans tenir compte de la casse des caractères.

Résultats

true
true
true

 
public class Principal {
   public static void main(String[] args) {
      String abc = "abc";
      String def = "def";
      String num = "123";
      System.out.println(abc.compareTo(def)<0);
      System.out.println(abc.compareTo("abc")==0);
      System.out.println(abc.compareTo(num)>0);
   }
}

Points de code et unités de code

Les chaînes Java sont implémentées sous forme de suites de valeurs char. Comme nous l'avons vu, le type char est une unité de code permettant de représenter des points de code Unicode en codage UTF-16. Les caractères Unicode les plus souvent utilisés peuvent être représentés par une seule unité de code. Les caractères complémentaires exigent, quant à eux, une paire d'unité de code.

 

Choix du chapitre Beaucoup de manipulations sur la même chaîne - la classe StringBuilder

Etant donné que les objets String sont immuables, vous ne pouvez pas manipuler les caractères d'un objet String de manière directe. Si vous avez besoin d'objets modifiables, utilisez plutôt java.lang.StringBuilder.

java.lang.StringBuilder
Méthodes Retour Explication
append(...) StringBuilder Ajoute à la chaîne déjà existante une autre chaîne, un caractère, une valeur numérique, un objet, etc.
charAt(int index) char Récupère un caractère particulier dans la chaîne dont la position est précisée en argument.
delete(int début, int fin)
StringBuider Enlève les caractères de la chaîne dans la limite spécifiée en argument.
deleteCharAt(int index) StringBuilder Enlève un seul caractère.
indexOf(String sousChaîne)
indexOf(String sousChaîne, int décalage)
int Cherche la première occurence d'une partie de la chaîne.
insert(int index, ...) StringBuilder Insère à la chaîne déjà existante une autre chaîne, un caractère, une valeur numérique, un objet, etc. à l'emplacement spécifié par index.
lastIndexOf(String sousChaîne)
lastIndexOf(String sousChaîne, int index)
int Cherche la dernière occurence d'une partie de la chaîne.
length() int Renvoie la longueur de la chaîne.
replace(int début, int fin, String nouvelle) StringBuilder Remplace une partie de la chaîne
reverse()
StringBuilder Inverse la chaîne.
setCharAt(int index, char ch)   Remplace dans la chaîne le caractère spécifié par rapport à l'indice proposé.
setLenght(int taille)   Impose une nouvelle taille de la chaîne.
subString(int début)
subString(int début, int fin)
String Retourne une partie de la chaîne. Attention, le caractère indicé par début est pris en compte, alors que le caractère correspondant à fin ne l'est pas ; fin représente la limite supérieure exclusive.
toString() String Récupération de la chaîne de caractère en format String.
public class Principal {
   public static void main(String[] args) {
      StringBuilder b = new StringBuilder("N'est"); // Création du tampon de chaîne avec la valeur "N'est"
      char car = b.charAt(0); // Retourne N
      b.setCharAt(0, 'C'); // changer un caractère : "C'est" : on ne peut pas faire cela avec un objet String
      b.append(' '); // ajoute un espace à la fin de la chaîne "C'est "
      b.append("Le moment."); // ajoute une chaine à la fin "C'est le moment."
      b.append(23); // ajoute un entier ou une valeur quelconque "C'est le moment.23"
      b.insert(6, "pas "); // insère une chaîne de caractère "C'est pas le moment.23"
      b.replace(2, 9, "est "); // remplace une partie de la chaîne "C'est le moment.23"
      b.delete(15, 18); // supprime une partie de la chaîne "C'est le moment"
      b.deleteCharAt(2); // supprime le troisème caractère "C'st le moment" 
      b.setLength(5); // Tronque la chaîne pour aboutir à la taille désirée "C'st "
      b.reverse(); // Inverse les caractères " ts'C"
      String s = b.substring(1, 2); // extrait une partie de chaîne "t"
      s = b.toString(); // reconversion vers une chaîne immuable.
      System.out.println(s);
      System.out.println(b); // fait automatiquement appel à la méthode toString() de StringBuilder
      b.setLength(0); // écrase le tampon ; il est désormais prêt à être réutilisé
   }
}
Résultats

ts'C
ts'C

 

Choix du chapitre Travail avec un seul caractère - la classe Character

Comme vous le savez, les caractères individuels sont représentés en Java par le type primitif char. La plate-forme Java possède également une classe Character qui définit des méthodes de classe pour contrôler le type d'un caractère ou pour modifier la casse d'un caractère.

java.lang.Character
Méthodes Retour Explication
static getNumericValue(char ch) int Retourne la valeur entière correspondante au chiffre exprimé dans le caractère. Prend donc en compte uniquement les caractères suivants : de '0' à '9'.
static isDigit(char ch) boolean Teste si le caractère représente un chiffre.
static isLetter(char ch) boolean Teste si le caractère représente une lettre.
static isLetterOrDigit(char ch) boolean Teste si le caractère représente une lettre ou un chiffre (pas de symbole de ponctuation).
static isLowerCase(char ch) boolean Teste si le caractère est en minuscule.
static isSpaceChar(char ch) boolean Teste si le caractère est le caractère espace.
static isUpperCase(char ch) boolean Teste si le caractère est en majuscule.
static isWhiteSpace(char ch) String Cherche et renvoie une instance unique de la chaîne dans l'ensemble des chaînes partagées.
static toLowerCase(char ch) int Renvoie le caractère en minuscule.
static toUpperCase(char ch) int Renvoie le caractère en majuscule.
public class Principal {
   public static void main(String[] args) {
      char lettre = 'A';
      char chiffre = '7';
      char accent = 'é';
      char espace = ' ';
      char blanc = '\n';
      System.out.println(Character.getNumericValue(chiffre));   // 7
      System.out.println(Character.isDigit(lettre));                      // false
      System.out.println(Character.isDigit(chiffre));                    // true
      System.out.println(Character.isLetter(lettre));                   // true
      System.out.println(Character.isLetter(chiffre));                 // false
      System.out.println(Character.isLetter(accent));                 // true
      System.out.println(Character.isLetterOrDigit(chiffre));     // true
      System.out.println(Character.isLowerCase(lettre));          // false
      System.out.println(Character.isSpaceChar(lettre));           // false
      System.out.println(Character.isSpaceChar(espace));        // true
      System.out.println(Character.isUpperCase(lettre));          // true
      System.out.println(Character.isWhitespace(lettre));         // false
      System.out.println(Character.isWhitespace(blanc));         // true
   }
}

 

Choix du chapitre Enveloppe d'objets et autoboxing

Il est parfois nécessaire de convertir un type primitif - comme int - en un objet. Tous les types primitifs ont une contrepartie sous forme de classe. Par exemple, il existe une classe Integer correspondant au type primitif int. Une classe de cette catégorie est généralement appelée classe enveloppe (object wrapper). Les classes enveloppes portent des noms correspondant aux types : Integer, Long, Float, Short, Byte, Character, Void et Boolean (les six premières héritent de la super classe Number). Les classes enveloppes sont inaltérables : vous ne pouvez pas modifier une valeur enveloppée, une fois l'enveloppe construite. Elles sont aussi final, vous ne pouvez donc pas en faire des sous-classes.

Supposons que nous voulions travailler sur une liste d'entiers. Nous pensons, tout de suite, utiliser la collection ArrayList<int>. Malheureusement, le paramètre de type entre les signes <> ne peut pas être un type primitif, c'est obligatoirement un objet. C'est à ce niveau là que la classe enveloppe devient intéressante. Vous pouvez ainsi déclarer une liste de tableau d'objet Integer :

ArrayList<Integer> liste = new ArrayList<Integer>() ;

Attention : un ArrayList<Integer> est bien moins efficace qu'un tableau int[] car chaque valeur est enveloppée séparément dans un objet. Vous ne devriez utiliser cette construction que pour les petites collections lorsque la commodité du programmeur est plus importante que l'efficacité.

Autoboxing

Une autre innovation du JDK 5.0 facilite l'ajout et la récupération d'éléments de tableau. L'appel :

liste.add(3);

est automatiquement traduit en :

liste.add(new Integer(3));

Cette conversion est appelée autoboxing.

Unboxing

A l'inverse, lorsque vous attribuez un objet Integer à une valeur int, il est automatiquement déballé (unboxing). En fait, le compilateur traduit :

int n = liste.get(i) ;

en

int n = liste.get(i).intValue() ;

Autoboxing et Unboxing

Les opérations d'autoboxing et d'unboxing fonctionne même avec les expressions arithmétiques. Vous pouvez, par exemple, appliquer l'opération d'incrémentation en une référence d'enveloppe :

Integer n = 3;
n++ ;

Le compilateur insère automatiquement des instructions pour déballer l'objet, augmenter la valeur du résultat et le remballer.

Dans la plupart des cas, vous avez l'illusion que les types primitifs et leurs enveloppes ne sont qu'un seul et même élément. Ils ne diffèrent considérablement qu'en un seul point : l'identité.

Enveloppes de nombres et chaînes de caractères

Vous verrez souvent les enveloppes de nombres pour une autre raison. Les concepteurs Java ont découvert que les enveloppes constituent un endroit pratique pour y stocker certaines méthodes de base, comme celles qui permettent de convertir des chaînes de chiffres en nombres.

Pour transformer une chaîne en entier, vous devez utiliser l'instruction suivante :

int x = Integer.parseInt(s) ;

Ceci n'a rien à voir avec les objets Integer ; parseInt() est une méthode statique. Mais la classe Integer constitue un bon endroit pour l'y placer. Pour transformer un entier en une chaîne représentant la suite des chiffres du nombre entier, utilisez la méthode toString() :

String chiffres = Integer.toString(324) ; // chiffres <-- "324"

Voici un exemple :

Test.java
package test;

import java.util.Scanner;
import static java.lang.System.*;

public class Test {
   public static void main(String[] args) {
       Scanner clavier = new Scanner(in);
       out.print("Introduisez votre nombre entier : ");
       String chaîne = clavier.next();          
       Integer  X  = new Integer(chaîne) ;   
       int x = X;                              // ou plus rapidement : int x = clavier.nextInt();
       out.println("Valeur entière : "+x);
       out.println("Chaîne de caractère équivalente : "+X);  // équivalent à Integer.toString(x);
       out.println("Valeur binaire correspondante : "+Integer.toBinaryString(x));
   }
}

A titre d'exemple, voici les descriptions de la classe enveloppe Integer et de la classe enveloppe Double

Vous avez ci-dessous, les tableaux montrant toutes les méthodes utiles sur les entiers et les réels dont les deux classes enveloppes Integer et Double sont les principales représentantes.

java.lang.Integer
Attributs
static final int MAX_VALUE Valeur maximale que peut représenter un nombre entier : 231-1.
static final int MIN_VALUE Valeur minimale que peut représenter un nombre entier : -231.
Méthodes
Integer(int valeur) Construit la classe enveloppe correspondante à partir de l'entier passé en argument.
Integer(String valeur) Construit la classe enveloppe correspondante à partir d'une chaîne de caractères représentant la suite des chiffres du nombre entier.
static int bitcount(int valeur) Retourne le nombre de bit à 1 dans la représentation binaire en complément à deux de la valeur entière spécifiée en argument de la méthode.
int compareTo(Integer autre) Compare la valeur numérique entre deux objets de type Integer. Si la valeur retournée est positive, cela signifie que la valeur numérique de l'objet est supérieur à la valeur numérique de l'objet passé en argument. Une valeur négative est retournée dans le cas contraire. Enfin, si les deux objets possèdent la même valeur numérique, c'est la valeur nulle qui est renvoyée.
static Integer decode(String valeur)

Méthode statique qui renvoie un objet de type Integer qui enveloppe la valeur numérique représentée par la suite des chiffres spécifiés dans la chaîne de caractères. Cette méthode gère les représentations octale, décimale ou hexadécimale suivant le préfixe numérique de la chaîne de caractères.

static int highestOneBit(int valeur) Méthode statique qui renvoie la valeur entière correspondante au bit 1 le plus à gauche, dans la représentation binaire du nombre entier passé en argument.
static int lowestOneBit(int valeur) Méthode statique qui renvoie la valeur entière correspondante au bit 1 le plus à droite, dans la représentation binaire du nombre entier passé en argument.
static int numberOfLeadingZeros(int valeur) Retourne le nombre de zéro, en partant de la gauche, dans la représentation binaire en complément à deux de la valeur entière spécifiée en argument.
static int numberOfTrailingZeros(int valeur) Retourne le nombre de zéro, en partant de la droite, dans la représentation binaire en complément à deux de la valeur entière spécifiée en argument.
static int parseInt(String valeur)
static
int parseInt(String valeur, int base)
Méthode statique qui retourne une valeur entière représentée par la suite des chiffres spécifiés dans la chaîne de caractères donnée en argument. Vous pouvez également précisez une autre base que la base 10. La valeur numérique retournée est en base 10. Elle est alors convertie à partir de la chaîne proposée, exprimée dans la base souhaitée.
static int reverse(int valeur) Méthode statique qui retourne une valeur entière dont l'ordre des bits est inversé par rapport à l'entier passé en argument.
static int rotateLeft(int valeur, int nombre ) Méthode statique qui retourne un entier qui a subit une rotation vers la gauche de l'entier valeur de nombre de position.
static int rotateRight(int valeur, int nombre ) Méthode statique qui retourne un entier qui a subit une rotation vers la droite de l'entier valeur de nombre de position.
static int signum(int valeur) Renvoie le signe de l'entier sous la forme -1, 0, 1.
static String toBinaryString(int valeur) Méthode statique qui retourne une chaîne de caractères qui représente une valeur entière en format binaire d'un nombre entier passé en argument.
static String toHexString(int valeur) Méthode statique qui retourne une chaîne de caractères qui représente une valeur entière en format hexadécimal d'un nombre entier passé en argument.
static String toOctalString(int valeur) Méthode statique qui retourne une chaîne de caractères qui représente une valeur entière en format octal d'un nombre entier passé en argument.
String toString() Méthode qui retourne une chaîne de caractère qui est une représentation du nombre entier de l'objet.
static String toString(int valeur, int base) Méthode statique qui retourne une chaîne de caractères qui représente une valeur entière dont la base est déterminé par base du nombre entier valeur passé en argument.
static Integer valueOf(int valeur) Méthode statique qui retourne un objet de type Integer correspondant à la valeur entière passée en argument.
static Integer valueOf(String valeur) Méthode statique qui retourne un objet de type Integer correspondant à la chaîne de caractères passée en argument qui représente une valeur entière.
static Integer valueOf(String valeur, int base) Même méthode que précédemment. Toutefois, le nombre est représenté dans une base quelconque qu'il s'agit de spécifier au moyen de l'argument base.
java.lang.Double
Attributs
static final int MAX_VALUE Valeur maximale que peut représenter un nombre réel : 1.7976931348623157 E308.
static final int MIN_VALUE Valeur minimale que peut représenter un nombre entier : 4.9 E-324.
static final int NaN Correspond à une valeur spéciale du type double ne représentant pas un nombre (Not a Number).
static final int POSITIVE_INFINITY Infini positif.
static final int NEGATIVE_INFINITY Infini négatif.
Méthodes
Double(double valeur) Construit la classe enveloppe correspondante à partir du réel passé en argument.
Double(String valeur) Construit la classe enveloppe correspondante à partir d'une chaîne de caractères représentant la suite des chiffres du nombre réel.
static int compare(double un, double deux) Compare la valeur numérique entre deux objets de type double.
static boolean isInfinite(double valeur) Détermine si la valeur réelle passée en argument est une valeur infinie.
static double parseDouble(String valeur) Méthode statique qui retourne une valeur réelle représentée par la suite des chiffres spécifiés dans la chaîne de caractères donnée en argument.
static Double valueOf(double valeur) Méthode statique qui retourne un objet de type Double correspondant à la valeur réelle passée en argument.
static Double valueOf(String valeur) Méthode statique qui retourne un objet de type Double correspondant à la chaîne de caractères passée en argument qui représente une valeur réelle.
public class Principal {
   public static void main(String[] args) {
      int nombre = 44;
      System.out.println("Représentation binaire : "+Integer.toBinaryString(nombre));
      System.out.println("Nombre de bit à 1 : "+Integer.bitCount(nombre));
      System.out.println("Valeur décimale du bit 1 le plus fort : "+Integer.highestOneBit(nombre));
      System.out.println("Valeur décimale du bit 1 le plus faible : "+Integer.lowestOneBit(nombre));
      System.out.println("Nombre de bit 0 en partant de la gauche : "+Integer.numberOfLeadingZeros(nombre));
      System.out.println("Nombre de bit 0 en partant de la droite : "+Integer.numberOfTrailingZeros(nombre));
      System.out.println("Symétrie horizontale du nombre binaire : "+Integer.toBinaryString(Integer.reverse(nombre)));
      System.out.println("Décalage vers la gauche de 2 unités : "+Integer.toBinaryString(Integer.rotateLeft(nombre, 2)));
      System.out.println("Représentation hexadécimale : "+Integer.toHexString(nombre));
      System.out.println("Représentation octale : "+Integer.toOctalString(nombre));
      System.out.println("Signe : "+Integer.signum(nombre));
      System.out.println("Valeur décimale du nombre binaire 1101 : "+Integer.valueOf("1101", 2));
   }
}
Résultats

Représentation binaire : 101100
Nombre de bit à 1 : 3
Valeur décimale du bit 1 le plus fort : 32
Valeur décimale du bit 1 le plus faible : 4
Nombre de bit 0 en partant de la gauche : 26
Nombre de bit 0 en partant de la droite : 2
Symétrie horizontale du nombre binaire : 110100000000000000000000000000
Décalage vers la gauche de 2 unités : 10110000
Représentation hexadécimale : 2c
Représentation octale : 54
Signe : 1
Valeur décimale du nombre binaire 1101 : 13

 

Tous les calculs en virgule flottante respectent la spécification IEEE 754. En particulier, il existe trois valeurs spéciales en virgule flottante, qui servent à indiquer les dépassements et les erreurs :
  1. Infinité positive : Double.POSITIVE_INFINITY : Le résultat d'un nombre positif par 0 est "infinité positive".
  2. Infinité négative : Double.NEGATIVE_INFINITY : Le résultat d'un nombre négatif par 0 est "infinité négative".
  3. Pas un nombre : Double.NaN (Not a Number) : Le calcul 0/0 ou la racine carré d'un nombre négatif donne "NaN".
Ces constantes permettent de représenter ces valeurs spéciales. Elle sont cependant rarement utilisées dans la pratique. En particulier, vous ne pouvez faire le test suivant pour vérifier, si un résultat est égal à Double.NaN :
if (x == Double.NaN) // n'est jamais vrai

Toutes les valeurs "pas un nombre" sont considérées comme distinctes. Pour cela vous devez employer la méthode isNaN() de la classe enveloppe Double pour réaliser ce genre de test :

if (Double.isNaN(x)) // vérifier si x est "pas un nombre"

Attention : Les nombres à virgule flottante ne conviennent pas aux calculs financiers, dans lesquels les erreurs d'arrondi sont inadmissibles. La commande System.out.println(2.0-1.1) produit 0.899999999999999, et non 0.9 comme vous pourriez le penser. Ces erreurs d'arrondi proviennent u fait que les nombres à virgule flottante sont représentés dans un système de nombres binaires. Il n'existe pas de représentation binaire précise de la fraction 1/10, tout comme il n'existe pas de représentation précise de la fraction 1/3 dans un système décimal. Si vous voulez obtenir des calculs numériques précis sans erreurs d'arrondi, utilisez la classe BigDecimal.

 

Choix du chapitre Conversion entre les nombres (et les objets) et les chaînes de caractères

Dans le développement d'applications, nous sommes souvent confrontés devant deux genres de situations classiques. Tout d'abord, nous avons besoin de récupérer une valeur numérique à partir d'une chaîne de caractères saisie au clavier ou venant d'un flux quelconque. Ainsi, une fois que nous avons récupérée la valeur, nous pouvons librement réaliser tous les traitements souhaitables. Ensuite, nous devons souvent afficher ce résultat numérique en association avec du texte.

Ce chapitre va nous aider à y voir plus clair sur ce sujet, sans être toutefois exhaustif puisque les chapitres suivants vont nous aider à aller encore plus loin dans ce domaine.

Conversion d'une chaîne vers un nombre

Comment un programme Java qui agit sur les nombres reçoit-t-il ses valeurs d'entrée ? Souvent, un tel programme lit une représentation textuelle d'un nombre et doit la convertir en une représentation numérique. Comme nous venons juste de la voir dans le chapitre précédent, les diverses sous-classes de Number : Integer, Double, Boolean, etc. définissent des méthodes de conversion très utiles :

public class Principal {
   public static void main(String[] args) {
      String s = "-42";
      byte b = Byte.parseByte(s); // s sous forme d'octet
      short sh = Short.parseShort(s); // s sous forme de valeur
      int i = Integer.parseInt(s); // s sous forme de valeur
      long l = Long.parseLong(s); // s sous forme de valeur
      float  f = Float.parseFloat(s); // s sous forme de valeur
      double d = Double.parseDouble(s); // s sous forme de valeur
      // les routines de conversion entière gère les nombres en diverses bases
      b = Byte.parseByte("1011", 2); // 1011 en binaire donne b=11 en base 10
      sh = Short.parseShort("ff", 16); // ff en base 16 donne sh=255 en base 10
      // la méthode valueOf() peut gérer les bases arbitraires
      i = Integer.valueOf("egg", 17); // Base 17
      // la méthode decode() gère les représentation octale, décimale ou 
      // hexadécimale suivant le préfixe numérique de la chaîne de caractères
      sh = Short.decode("0377"); // un 0 de tête signifie base 8
      i = Integer.decode("0xff"); // un 0x de tête signifie base 16
      l = Long.decode("255"); // Les autres nombres sont en base 10
   }
}
Même si vous pouvez effectuer ce type de conversion dans des cas simples, nous n'avons pas tenu compte des conventions internationales. Si vous tentez de récupérer une valeur réelle exprimée normalement comme en France, avec les espaces entre les milliers et avec l'opérateur virgule pour la séparation avec les décimales, vous risquez d'avoir une exception de type NumberFormatException :

double valeur = Double.parseDouble("1 234,56"); // erreur lors de l'analyse du nombre exprimé dans la chaîne de caractères.

Si vous devez travailler avec ce type d'écriture, revoyez l'étude précédente qui nous a montré comment gérer les formats des nombres au niveau international, ou alors consultez les chapitres suivants.

Conversion d'un nombre ou d'un objet vers une chaîne

Toujours en se servant de ces classes enveloppes, vous avez la possibilité de passer cette fois-ci d'une valeur primitive vers la chaîne de caractères correspondante. Vous pouvez même envisager de contrôler la base du nombre et ainsi d'avoir une représentation très sophistiquée dans la chaîne de caractères :

int nombre = 42;
String décimal = Integer.toString(nombre);
String binaire = Integer.toBinaryString(nombre);
String octal = Integer.toOctalString(nombre);
String hexadécimal = Integer.toHexString(nombre);
String base36 = Integer.toString(nombre, 36);
hexadécimal = Integer.toHexString(nombre).toUpperCase(); 

Il est possible d'obtenir une représentation sous forme de chaîne de caractères de la plupart des types, grâce à la méthode statique valueOf() de la classe String. Plusieurs versions surchargées de cette méthode donnent des valeurs pour tous les types de base :

String un = String.valueOf(1);
String deux = String.valueOf(2.384f);
String pasVrai = String.valueOf(false);

Tous les objets Java possèdent une méthode toString() héritée de la classe Object. Pour des références de type classe, valueOf() appelle en réalité la méthode toString() de l'objet pour récupérer sa représentation sous forme de chaîne. Si la référence est égale à null, le résultat est une chaîne "null".

import java.util.Date;

public class Principal {
   public static void main(String[] args) {
      String aujourdhui = String.valueOf(new Date());
      System.out.println("La date d'aujourd'hui est "+aujourdhui);
   }
}
Résultats

La date d'aujourd'hui est Mon Mar 12 09:52:35 CET 2007

La représentation de la date n'est pas terrible. Encore une fois, reportez-vous sur l'étude précédente pour avoir un formatage de la date qui respecte vos désirs, ou alors consultez les chapîtres suivants.

 

 

Comme la concaténation se sert en interne de la méthode valueOf(), il est courant d'utiliser une chaîne vide et l'opérateur + pour représenter n'importe quel objet sous forme de chaîne de caractères. La plupart du temps d'ailleurs, votre but est d'afficher une information relative à ces valeurs numériques en relation avec un message correpondant. Ainsi grâce à la concaténation, vous n'avez même pas besoin de passer par cette phase intermédiaire.
import java.util.Date;

public class Principal {
   public static void main(String[] args) {
      // récupérer la chaîne de caractères équivalente et afficher
      String un = ""+1;
      String deux = ""+2.384f;
      String aujourdhui = ""+new Date();
      System.out.println(un);
      System.out.println(deux);
      System.out.println("La date d'aujourd'hui est "+aujourdhui);
      
      // alternatives beaucoup plus souvent utilisées -> Affichage direct
      System.out.println("Valeur entière : "+1);
      System.out.println("Valeur réelle : "+2.384f);
      System.out.println("La date d'aujourd'hui est "+new Date());
   }
}
Résultats

1
2.384
La date d'aujourd'hui est Mon Mar 12 10:11:16 CET 2007
Valeur entière : 1
Valeur réelle : 2.384
La date d'aujourd'hui est Mon Mar 12 10:11:16 CET 2007

 

Choix du chapitre Formatage de texte de nombre et de date avec printf() et format()

Une tâche courante lorsque nous travaillons avec du texte en sortie consiste à combiner les valeurs de divers types en un unique bloc de texte lisible par un être humain. Comme nous venons de le découvrir dans le chapitre précédent, une manière d'accomplir cette tâche se base sur les aptitudes de l'opérateur Java de concaténation de chaînes. Cela produit du code ressemblant à ceci :

String utilisateur = "Emmanuel REMY";
int essais = 15;
Date date = new Date();
...
System.out.println(utilisateur+" connecté après "+essais+" essais. Dernière connexion le "+date);
Résultat

Emmanuel REMY connecté après 15 essais. Dernière connexion le lun. mars 12 14:09:45 CET 2007

La méthode printf(), la chaîne de formatage et les spécificateurs de format

Depuis Java 5.0, il existe maintenant une alternative plus familière pour les programmeurs C : une méthode printf(). printf est l'abbrévation de print formatted, c'est-à-dire impression formatée, et combine des fonctions d'impression et de formatage en un seul appel. La méthode printf() a été rajoutée aux classes de flux de sortie PrintWriter et PrintStream. Il s'agit d'une méthode qui attend un nombre variable d'arguments. Le premier argument est la chaîne de formatage. Cette dernière spécifie le texte à afficher et inclut typiquement un ou plusieurs spécificateurs de format sous forme de séquences d'échappement commençant par le caractère %. Les arguments restants de printf() sont des valeurs à convertir en chaînes et à substituer aux spécificateurs de format dans la chaîne de formatage. Les spécificateurs de format influencent les types des arguments restants et spécifient de manière exacte comment ils doivent être convertis en chaînes.

La concaténation de chaînes illustrée ci-dessus peut être réécrite de la manière suivante :

String utilisateur = "Emmanuel REMY";
int essais = 15;
Date date = new Date();
...
System.out.printf("%s connecté après %d essais. Dernière connexion le %tc%n", utilisateur, essais, date); 

Au spécificateur de format %s se substitue simplement une chaîne de caractères. %d indique que l'argument doit être entier. %tc attend un objet Date, Calendar ou un nombre en millisecondes et convertit cette valeur en une représentation textuelle de la date et de l'heure. %n ne réalise aucune conversion : il produit simplement une terminaison de ligne spécifique à la plate-forme, tout comme la méthode println().

Les conversions réalisées par printf() sont toutes proprement localisées. Ainsi, les heures et les dates sont affichées avec une ponctuation adaptée aux paramètres de localisation. De même, si vous souhaitez qu'un nombre soit affiché avec un séparateur de milliers, la ponctuation sera également spécifique aux paramètres de localisation (un espace en France, une virgule aux USA, etc).

La méthode format()

Outre la méthode printf() de base, les classes PrintWriter et PrintStream définissent aussi une méthode synonyme nommée format() : elle accepte exactement les mêmes arguments et se comporte de la même manière. La classe String possède également une méthode format(). Cette méthode statique String.format() se comporte comme la méthode PrintWriter.format(), à la différence près qu'elle retourne la chaîne de caractères plutôt que de l'envoyer dans un flux.

Les spécificateurs de format

Chaque spécificateur de format commence donc par un signe pourcentage et se termine par un type de conversion d'un ou de deux caractères, lequel spécifie la plupart des détails relatifs à la conversion et à l'affichage. Entre ces deux éléments se trouvent les drapeaux facultatifs qui fournissent d'autres détails de formatage. La syntaxe générale d'un spécificateur de format est la suivante. Les parenthèses carrées indiquent les éléments facultatifs :

%[argument][drapeaux][largeur][.précision]type

Le type de conversion

Nous allons décrire chacun des éléments qui composent le spécificateur de format. Nous commençons tout d'abord par le type.

type de Conversion
d Formate l'argument sous forme d'entier en base 10. L'argument doit être d'un type entier compatible 159
x, X Formate l'argument sous forme d'entier en base 16 (hexadécimal). Représentation en majuscule pour le type X. 9F
o Formate l'argument sous forme d'entier en base 8 (octal). 237
f Formate l'argument sous forme de nombre à virgule flottante en base 10, sans utiliser la notation exponentielle. 15.9
e, E Formate l'argument sous forme de nombre à virgule flottante en base 10, en utilisant la notation exponentielle. Représentation en majuscule pour le type E. 1.59e+01
g, G Virgule flottante générale (le plus court entre e et f). Représentation en majuscule pour le type G.  
a Formate l'argument en utilisant un format à virgule flottante hexadécimal. 0x1.FCCDp3
s, S Chaîne de caractères (en majuscule pour S) Salut
c, C Caractère (en majuscule pour C) S
b, B Valeur booléenne ( en majuscule pour B) true
t ou T Date et heure (en majuscule pour T) - 2 lettres, la deuxième indique comment exécuter le formatage (voir plus loin)
% Symbole de pourcentage %
n Séparateur de ligne en fonction de la plateforme  

Voici un exemple simple qui permet de découvrir le résultat suivant le type de conversion souhaité :

int entier = 30;
double réel = 124.78;
char lettre = 'a';
boolean test = false;
String message = "bonjour";
String motif = "Entier : %d%nEntier : %X%nRéel : %f%nRéel : %e%nRéel : %g%nLettre : %C%nTest : %B%nMessage : %S";
System.out.printf(motif, entier, entier, réel, réel, réel, lettre, test, message);
Résultats
Entier : 30
Entier : 1E
Réel : 124,780000
Réel : 1.247800e+02
Réel : 124.780
Lettre : A
Test : FALSE
Message : BONJOUR

Spécificateur d'argument < et $

Tout spécificateur de format au sein d'une chaîne de formatage (à l'exception de %% et de %n) nécessite un argument contenant la valeur à formater. Ces arguments suivent la chaîne de formatage dans l'invocation de format() ou de printf(). Par défaut, un spécificateur de format utilise le prochain argument non utilisé. Ainsi, dans l'invocation de printf() suivante, les premier et deuxième spécificateurs de format %s formatent respectivement le deuxième et troisième argument de la méthode :

String nom = "Rémy";
String prénom = "Emmanuel";
System.out.printf("Nom : %s %s%n", prénom, nom); // Affichage -> Nom : Emmanuel Rémy

Si un spécificateur de format inclut le caractère < après le symbole %, il indique que l'argument du spécificateur de format précédent doit être réutilisé. Cela permet à un même élément d'être formaté à plusieurs reprises (cette technique sera généralement utilisée pour le formatage des dates).

En reprenant l'exemple ci-dessus, voici ce que nous pouvons faire :

String nom = "Rémy";
String prénom = "Emmanuel";
System.out.printf("Nom : %s %s. Bonjour Mr %<S%n", prénom, nom); // Affichage -> Nom : Emmanuel Rémy. Bonjour Mr RÉMY

En reprenant le mélange des différents types, voici comment changer le code pour que le nombre des arguments soit réellement plus faible :

int entier = 30;
double réel = 124.78;
char lettre = 'a';
boolean test = false;
String message = "bonjour";
String motif = "Entier : %d%nEntier : %<X%nRéel : %f%nRéel : %<e%nRéel : %<g%nLettre : %C%nTest : %B%nMessage : %S";
System.out.printf(motif, entier, réel, lettre, test, message);
Résultats
Entier : 30
Entier : 1E
Réel : 124,780000
Réel : 1.247800e+02
Réel : 124.780
Lettre : A
Test : FALSE
Message : BONJOUR
 

La position des arguments peut aussi être spécifiée de façon absolue. Si le signe % est suivi par un ou plusieurs chiffres, puis par le signe $, ces chiffres spécifient une position d'argument. Par exemple, %1$d indique que le premier argument suivant la chaîne de formatage doit être formaté sous forme de nombre entier.

En reprenant encore une fois l'exemple du dessus, voici ce que nous pouvons faire :

String nom = "Rémy";
String prénom = "Emmanuel";
System.out.printf("Nom : %s %s. Bonjour Mr %1$S%n", nom, prénom); // Affichage -> Nom : Rémy Emmanuel. Bonjour Mr RÉMY

Les drapeaux

A la suite du spécificateur d'argument facultatif, un spécificateur de format peut inclure un ou plusieurs drapeaux.

drapeaux
+
Ce drapeau indique que les données de sortie numériques doivent toujours inclure un signe : une valeur non négative sera donc préfixée avec un +. +3333.33
espace Un espace blanc représente un drapeau (un peu difficile à déceler, il est vrai) indique que les valeurs non négatives doivent être préfixées avec un espace. Ce drapeau s'avère particulièrement utile pour aligner nombres positifs et négatifs dans une colonne. |  3333.33|
0 Le chiffre 0 utilisé sous forme de drapeau indique que les valeurs numériques doivent être remplies à gauche (après le signe, s'il y en a un) avec des zéros. Ce drapeau ne peut être utilisé que si une largeur est spécifiée, et jamais en conjonction avec le drapeau -. 003333.33
- Un tiret indique que la valeur formatée doit être justifiée à gauche au sein de la largeur spécifiée. Lorsqu'une largeur est spécifiée sans ce drapeau, la chaîne formatée doit être remplie à gauche afin de produire une justification à droite. |3333.33|
( Ce drapeau indique que les nombres négatifs doivent être produits entre parenthèses, comme c'est la norme avec les rapports financiers. -(3333.33)
, Ce drapeau indique que les nombres doivent être formatés en utilisant le séparateur spécifique aux paramètres de localisation. Par exemple, la France utilise l'espace comme séparateur de milliers, alors que les USA utilisent une virgule. 3 333,33
# (pour format f) Inclut toujours une décimale. 3,333
# (pour format x ou o) Ajoute le préfixe 0x ou 0. 0xcafe

L'exemple ci-dessous exploite quelques drapeaux :

int positif = 2345;
int négatif = - 4567;
double réel = 8;
String motif = "% ,d%n% ,d%n%1$#x%n%#f";
System.out.printf(motif, positif, négatif, réel);
Résultats
 2 345
-4 567
0x929
8,000000

La largeur

La largeur d'un spécificateur de format se compose d'une ou plusieurs positions spécifiant le nombre minimal de caractères à produire. Si la valeur formatée est moins large que la largeur spécifiée, elle est remplie à gauche avec des espaces blancs (par défaut), produisant ainsi une valeur justifiée à droite. Les drapeaux - et 0 peuvent être utilisés afin de spécifier une justification à gauche ou pour réaliser un remplissage de zéros.

Voici un exemple qui met en oeuvre la justification à droite avec la gestion de la largeur :

String nom = "Rémy";
String prénom = "Emmanuel";
int essais = 15;
System.out.printf("Nom : %13s%nPrénom : %10s%nEssais : %10d", nom, prénom, essais);
Résultats
Nom :          Rémy
Prénom :   Emmanuel
Essais :         15

La précision

La précision d'un spécificateur de format se compose d'une ou plusieurs positions suivant le point décimal. La signification de ce nombre dépend du type de formatage avec lequel elle est utilisée :

  1. Avec les types %e, %E et %f, la précision spécifie le nombre de chiffres significatifs après le point décimal. Des zéros sont ajoutés en suffixe, si nécessaire. La précision par défaut est égale à 6.
  2. Avec les types %g et %G, la précision spécifie le nombre total de chiffres à afficher. En d'autres termes, elle spécifie les valeurs les plus grandes et les plus petites qui peuvent être affichées sans avoir recours à la notation exponentielle. La précision par défaut est égale à 6. Si une précision de 0 est indiquée, elle est traitée comme une précision égale à 1.
  3. Avec les types de formatage %s, %h et %b, ainsi que leurs variantes en majuscules, la précision spécifie le nombre maximal de caractères à produire. Si aucune précision n'est spécifiée, il n'y a pas de maximum. Si les données de sortie formatées dépassent la précision des caractères, elles sont tronquées. Si la précision est inférieure à la largeur, la valeur formatée est d'abord tronquée, puis remplie selon la largeur spécifiée.

Voici un exemple qui permet de placer des réels sur une colonne avec une justification à droite, une notation typiquement française et pour finir avec 2 chiffres significatifs après la virgule :

for (double x : new double[] {34.6, 6745.7865, 0.3425, 12345})
   System.out.printf("%,15.2f%n", x);
Résultats
          34,60
       6 745,79
           0,34
      12 345,00
 

En reprenant un des exemples proposés plus haut, voici ce que nous pouvons faire en prenant en compte toutes les techniques que nous venons d'évoquer, savoir : les spécificateurs d'argument, les drapeaux, la largeur et la précision.

int positif = 2345;
int négatif = - 4567;
double réel = 84532;
String motif = "%,10d%n%,10d%n%1$0#10x%n%,13.2f";
System.out.printf(motif, positif, négatif, réel);
Résultats
     2 345
    -4 567
0x00000929
    84 532,00

Formatage des nombres

Après avoir recenser toutes les techniques de formatage de valeurs de différentes natures, nous allons nous consacrer uniquement au formatage des nombres. Ceci dit, la plupart des exemples proposés exploitent déjà largement ce type de valeur. Nous allons donc exploiter un petit exemple qui est souvent utile dans la représentation des nombres réels, comme la justification à droite et la prise en compte d'un certain nombre de chiffres après la virgule. Il s'agit de la conversion entre les €uros et les francs :

Résultats
Valeur en €uro ?
169000
  169 000,00 €uros
1 108 597,75 Francs
 
import java.text.ChoiceFormat;
import static java.lang.System.*;
import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(in);
      out.println("valeur en €uro ?");
      double €uro = clavier.nextDouble();
      affiche("€uro", €uro);
      affiche("Franc", €uro*6.55975);
   }
   public static void affiche(String monnaie, double valeur) {
      ChoiceFormat pluriel = new ChoiceFormat("0#|1#s");
      out.printf("%,12.2f %s%s%n", valeur, monnaie, pluriel.format(valeur));      
   }
}

Formatage des dates et des heures

Nous avons vu que la concaténation, ou l'appel à la méthode valueOf(), de la classe String permettent d'intégrer des objets dans une chaîne de caractères. Dans ce cas là, c'est la méthode toString() qui est automatiquement sollicitée. Toutefois, la méthode toString() de la classe Date produit bien une représentation textuelle d'une date et d'une heure, mais ne réalise aucune localisation et ne permet pas de personnaliser les champs (par exemple, jour, mois, année, heures et minutes) à afficher. Cette méthode toString() ne devrait être utilisée que pour produire une heure lisible par une machine, pas par une chaîne lisible par un être humain.

Tout comme les nombres, les dates et les heures peuvent être converties en chaînes de caractères en utilisant les méthodes format() ou printf() suivant le cas. Les chaînes de formatage pour l'affichage des dates et des heures se composent de séquences de deux caractères commençant par la lettre t. La seconde lettre de chaque séquence spécifie le ou les champs de la date et de l'heure à afficher. Ainsi, %tR affiche les champs heures et minutes en utilisant un format sur 24 heures, alors que %tD affiche les champs jour, mois et année séparés par des slashs. La méthode String.format() sait formater une date ou une heure spécifiée en tant que valeur long, Date ou Calendar.

Date et heure
tc
Date et heure complètes ven. févr. 17 17:40:05 CET 2006
tF Date au format ISO 8601 2006-02-17
tD Date au format américain 02/17/06
tT Heure sur 24 heures 17:43:57
tr heure sur 12 heures 05:44:37 PM
tR Heure sur 24 heures sans secondes 17:45
tY Année sur quatre chiffres (avec zéros préalables, le cas échéant) 2006
ty Deux derniers chiffres de l'année (avec zéro préalable, le cas échéant) 06
tC Deux premiers chiffres de l'année (avec zéro préalable, le cas échéant) 20
tB Nom complet du mois février
tb ou th Nom du mois abrégé févr.
tm Mois sur deux chiffres (avec zéro préalable le cas échéant) 02
td Jour sur deux chiffres (avec zéro préalable, le cas échéant) 17
te Jour sur deux chiffres. 9
tA Jour de la semaine complet vendredi
ta Jour de la semaine abrégé ven.
tj Jour de l'année sur trois chiffres (avec zéros préalables, le cas échéant), entre 001 et 366. 048
tH Heure sur deux chiffres (avec zéro préalable, le cas échéant) , entre 00 et 23 21
tk Heure sur deux chiffres (sans zéro préalable, le cas échéant) entre 0 et 23 8
tI Heure sur deux chiffres (avec zéro préalable, le cas échéant) entre 01 et 12 09
tl Heure sur deux chiffres (sans zéro préalable) , entre 1 et 12 9
tM Minutes sur deux chiffres (avec zéro préalable, le cas échéant) 06
tS Secondes sur deux chiffres (avec zéro préalable, le cas échéant) 09
tL Millième de seconde, sur trois chiffres (avec zéros préalables, le cas échéant) 921
tN Nanosecondes sur neuf chiffres (avec zéros préalables, le cas échéant) 656000000
tP Indicateur du matin ou de l'après midi en majuscules PM
tp Indicateur du matin ou de l'après midi en minuscule pm
tz Décalage numérique RFC 822 du GMT +0100
tZ Fuseau horaire CET
ts Secondes depuis le 1970-01-01 00:00:00 GMT 1140209926
tE Millièmes de seconde depuis le 1970-01-01 00:00:00 GMT 1140209926047

Voici un petit exemple qui donne des solutions pour l'affichage des dates et des heures :

import java.util.Date;

public class Principal {
   public static void main(String[] args) {
      long maintenant = System.currentTimeMillis();
      Date date = new Date(maintenant);
      System.out.printf("Heure : %tR%n", maintenant);
      System.out.printf("Date : %tD%n", date); // Attention, date au format américain
      System.out.printf("Aujourd'hui, nous sommes le %tA %<te %<tB %<tY", new Date());
   }
}
Résultats

Heure : 15:09
Date : 03/13/07
Aujourd'hui, nous sommes le mardi 13 mars 2007

 

Vous remarquez dans cet exemple que l'affichage de la date au complet est un peu long. Par ailleurs, la localisation n'est pas gérée. Il peut être plus judicieux de prendre plutôt les formatages de date soit par la classe DateFormat, soit par la classe MessageFormat, sur lesquelles nous avons déjà travaillés dans l'étude précédente.

import java.text.*;
import java.util.Date;

public class Principal {
   public static void main(String[] args) {
      Date date = new Date();
      // première solution
      System.out.println("Heure : "+DateFormat.getTimeInstance(DateFormat.SHORT).format(date));
      System.out.println("Date : "+DateFormat.getDateInstance(DateFormat.SHORT).format(date)); 
      System.out.println("Actuellement, nous sommes le "+DateFormat.getDateInstance(DateFormat.FULL).format(date));
      // deuxième solution
      String motif = "Heure : {0, time, short}\nDate : {0, date, short}\nActuellement, nous sommes le {0, date, full}";
      System.out.println(MessageFormat.format(motif, date));
   }
}
Résultats

Heure : 15:29
Date : 13/03/07
Actuellement, nous sommes le mardi 13 mars 2007
Heure : 15:29
Date : 13/03/07
Actuellement, nous sommes le mardi 13 mars 2007

 

Choix du chapitre Mise en correspondance de motifs à l'aide d'expressions régulières

Une expression régulière décrit un motif dans le texte. Par motif, nous entendons tout élément que vous pouvez identifier dans le texte à partir de simples caractères qui ont un sens particulier dans l'interprétation qui en est faite. Ceci couvre des éléments comme les mots, les groupes de mots, les lignes de paragraphes, la ponctuation, la casse et plus généralement, les chaînes de caractères et les nombres structurés de manière particulière, comme les numéros de téléphone, les adresse mail, etc.

Avec les expressions régulières, vous pouvez chercher dans le dictionnaire tous les mots contenant un q sans son alter ego u juste à côté, ou les mots commençant et finissant par la même lettre. Une fois le motif construit, des outils simples permettent de chasser dans le texte ou de vérifier si une chaîne donnée lui correspond. Une expression régulière peut également vous aider à démembrer des parties spécifiques du texte, puis être utilisée comme texte de remplacement.

Lorsque vous construisez un motif, vous devez spécifier précisément la suite des caractères qui constituera une concordance autorisée. Il existe une syntaxe spéciale à utiliser pour décrire un motif.

Voici un exemple simple d'expression régulière :

[Jj]ava.+

L'expression régulière précédente correspond à n'importe quelle chaîne de caractères ayant la forme suivante :

  1. La première lettre doit être un J ou un j.
  2. Les trois lettres suivantes sont impérativement ava.
  3. Le reste de la chaîne est constitué d'un ou de plusieurs caractères arbitraires.

Par exemple, la chaîne "javanais" correspond à cette expression, à la différence de la chaîne "le monde de Java".

Mise en correspondance

La méthode la plus simple de la classe String acceptant un argument sous forme d'expression régulière s'appelle matches() ; elle retourne la valeur true si la chaîne de caractères correspond au motif défini par l'expression régulière spécifiée.

Résultats

true
false

String motif = "[Jj]ava.+";
String premier = "javanais";
String deuxième = "le monde de Java";
System.out.println(premier.matches(motif));
System.out.println(deuxième.matches(motif));

Chercher/Remplacer

Outre les mises en correspondance, nous pouvons utiliser les expressions régulières lors d'opérations de type chercher/remplacer. Les méthodes replaceFirst() et replaceAll() de la classe String cherche dans une chaîne de caractères respectivement la première occurence de la sous-chaîne recherchée, ou l'ensemble des sous-chaînes correspondant à un motif donné, avant de les remplacer par le texte alternatif spécifié, et de retourner enfin une nouvelle chaîne de caractères contenant les remplacements.

Résultats

Javanais
le monde de JAVA

String motif = "[Jj]ava";
String premier = "javanais";
String deuxième = "le monde de Java";
System.out.println(premier.replaceAll(motif, "Java"));
System.out.println(deuxième.replaceFirst(motif, "JAVA"));

Décomposition en plusieurs sous-chaînes

L'autre méthode de la classe String utilisant les expressions régulières s'appelle split(). Elle retourne un tableau contenant les sous-chaînes d'une chaîne de caractères, séparées par des délimiteurs correspondant au motif spécifié.

Par exemple, pour obtenir un tableau contenant les mots d'une chaîne de caractères séparés par un nombre quelconque d'espaces ou de saut de lignes, utiliser le motif suivant [ \t\n\r] :

String phrase = "Voici une phrase\tavec une deuxième colonne\net une ligne en plus";
System.out.println(phrase);
String[] mots = phrase.split("[ \t\n\r]");
for (String mot : mots) System.out.println(mot); 
Résultats
Voici une phrase        avec une deuxième colonne
et une ligne en plus
Voici
une
phrase
avec
une
deuxième
colonne
et
une
ligne
en
plus

Récupérer une sous-chaîne

Les méthodes matches(), replaceFirst(), replaceAll() et split() conviennent lorsque vous n'utilisez une expression régulière qu'une seule fois. Si vous souhaitez utiliser une expression régulière pour des mises en correspondance multiples, vous devez utiliser explicitement les classes Pattern et Matcher du paquetage java.util.regex.

En premier lieu, créez un objet Pattern représentant votre expression régulière à l'aide de la méthode statique compile(). A partir de là, vous devez créer un objet de type Matcher qui procure un certain nombre de méthodes équivalente à celles que nous avons découvert au travers de la classe String. Toutefois, cette classe Matcher offre la capacité supplémentaire de vous délivrer toutes les sous-chaînes correspondant au motif souhaité. Deux méthodes de Matcher me paraissent intéressantes pour répondre à ce critère particulier :

  1. find() : La première fois que la méthode find() est appelée après la création d'un objet Matcher, elle commence sa recherche au début de la chaîne de caractères. Si elle trouve une correspondance, elle enregistre les positions de départ et de fin du texte mis en correspondance, et renvoie la valeur booléenne true. Ces positions sont accessibles au travers des méthodes start() et end(). Le prochain appel à la méthode find() relance la recherche, mais cette fois-ci à partir de la position end()+1. De cette manière, et à l'aide d'une boucle, il est possible de trouver toutes les correspondances d'un motif au sein d'une chaîne de caractères simplement en appelant la méthode find() à plusieurs reprises jusqu'à ce qu'elle retourne la valeur false, indiquant ainsi qu'aucune correspondance à été trouvée.
  2. group() : Dès lors, après chaque appel répété à la méthode find(), vous pouvez récupérer la sous-chaîne correspondante au travers de la méthode group().
Résultats

<gras>
</gras>

String phrase = "<gras>Agissez</gras>, au lieu de parler";
Matcher correspondance = Pattern.compile("</?.*?>").matcher(phrase);
while (correspondance.find())
   System.out.println(correspondance.group());

 

Définition des éléments constituant une expression régulières

Nous avons décelé un petit peu l'utilité des expressions régulières. Il maintenant temps de voir la syntaxe à respecter pour optenir le résultat souhaité. Nous allons donc recenser, sans être toutefois exhaustif, la plupart des éléments qui constitue une expression régulière.

Caractère d'échappement : \n ... \\d

Notez que beaucoup d'éléments d'une expression régulière incluent le caractère antislash (\) qui permet ainsi de proposer une syntaxe raccourcie. Par exemple, \d indique qu'il s'agit d'un caractère représentant un chiffre compris entre 0 et 9. Toutefois, les chaînes de caractères Java utilisent également l'antislash pour exprimer ses propres caractères spéciaux, qui ont ainsi une influence particulière sur la gestion même de la chaîne, comme le font par exemple \n, \t, \r, etc. Pour éviter le conflit entre ces deux types d'interprétation, vous devez doubler les caractères antislashs sur les éléments qui correspondent à un motif particulier de l'expression régulière. En reprenant l'élément de l'expression régulière évoqué dans cette rubrique, voici donc ce que nous devons écrire : "\\d".

String code = "1234\t3456\t3214";
System.out.println(code);
for (String valeur : code.split("\t"))
   if (valeur.matches("\\d{4}")) System.out.println(Integer.parseInt(valeur));

// résultats --------------------------------------------
1234		3456		3214
1234
3456
3214

Le programme ci dessus permet de récupérer des nombres entiers qui sont constitués exactement de 4 chiffres.
.

Remarquez bien, que dans ce programme, \t (tabulation) agit tout simplement comme un caractère de contrôle dans une chaîne de caractères classique. Il est donc considéré comme un autre caractère puisqu'il agit également sur l'affichage, alors que \\d est un caractère spécifique interprété de façon particulière dans l'expression régulière. Pour résumer, dans un cas, vous devez utiliser qu'un seul antislah, dans l'autre cas vous en mettez deux, chacun ayant son propre rôle.

Il peut arriver, pour certains caractères qui ont normalement une interprétation particulière dans les expressions régulières, que vous souhaitiez les utiliser, cette fois-ci, de façon classique (de manière littérale). C'est le cas notamment pour les caractères ., *, +, les accolades {} et les parenthèses ( ) (leurs interprétations seront spécifiées par la suite). Pour que vous puissiez donc les utiliser de manière littérale, placez un antislash devant ce caractère. Ainsi, avec la séquence \., vous désirez tout simplement prendre en compte le point dans votre chaîne de caractères sans qu'il soit interprété par l'expression régulière.

Caractères et classes de caractères prédéfinies : . \\s \\d \\w

La forme la plus simple d'une expression régulière est du texte plein, littéral, dont la signification correspond exactement à ce texte. Cela peut être un simple caractère ou plus. Généralement, ce type d'expression ne présente pas beaucoup d'intérêt, puisque dans ce cas de figure, il suffit de faire un test d'égalité entre deux chaînes sans passer par le système d'expression régulière.

Il existe quand même un cas où cela peut être intéressant, c'est lorsque nous utilisons la méthode split(). Effectivement, dans ce cas de figure, nous imposons souvent un séparateur bien spécifique :

String message = "Le monde de java est-il parfait ?";
for (String souschaine : message.split(" java "))
   System.out.println(souschaine);

Résultats --------------------------------------------------
Le monde de
est-il parfait ?

Le programme ci-dessus permet de récupérer un début et une fin de chaîne. Le début correspond à tout ce qui se trouve avant " java ", et la fin à tout, ce qui se trouve après.

Toutefois, la plupart du temps, nous avons besoin de plus de souplesse. Plutôt que de demander impérativement un caractère spécifique, vous pouvez solliciter un ensemble de caractères qui correspond à un critère. Dans ce cas là, nous mettons en oeuvre ce que nous appelons une classe de caractères. Nous avons la possiblité de définir nos propres classes de carcatères, toutefois un certain nombre de classessont déjà prédéfinies :

  1. N'importe quel caractère : point (.) : Le caractère spécial point (.) correspond à n'importe quel caractère. Ainsi, le motif ".ose" permet de prendre en compte les chaînes "rose", "pose" et " ose" (un blanc suivi de ose) ou à tout autre caractère suivi de la séquence ose. Deux points correspondent à deux caractères, et ainsi de suite. Nous pouvons définir le (.) comme le groupe ou la classe de tous les caractères.
  2. Caractère d'espacement ou pas : \s, \S : Le caractère spécial \s correspond littéralement à un espace ou à l'un des caractères suivants : \t (tabulation), \r (retour chariot), \n (nouvelle ligne), \f (saut de page) et espace arrière. Le caractère spécial \S fait l'inverse, et correspond à n'importe quel caractère sauf un caractère d'espacement.
  3. Caractère numérique ou pas : \d, \D : \d correspond à n'importe quel chiffre de 0 à 9. \D fait l'inverse, il correspond à tous les caractères, sauf aux chiffres.
  4. Caractères alphanumériques ou pas : \w, \W : \w correspond à un caractère de type "mot", c'est-à-dire, les lettres en majuscules, de A à Z, en minuscules, de a à z, les chiffres de 0 à 9, et le tiret bas (_). Attention, les caractères accentués ne sont pas pris en compte. \W correspond à tous les caractères sauf ceux-là, c'est-à-dire, tous les caractères de ponctuations ainsi que les symboles, comme les accolades et les parenthèses, { }[ ]( ), les opérateurs +, -, *, etc., et pour finir, les caractères accentués.
String phrase = "Une phrase\tavec une {deuxième} colonne\net une (ligne) en plus\tet des chiffres 453267";
System.out.println(phrase);
String[] mots = phrase.split("\\s");
for (String mot : mots) {
   if (mot.matches(".*\\W.*")) System.out.println("Récupérer mot avec symboles : "+mot);
   if (mot.matches(".*\\d.*")) System.out.println("Récupérer nombres : "+mot);
}
 
Résultats -----------------------------------------------------------------------------------

Une phrase      avec une {deuxième} colonne
et une (ligne) en plus  et des chiffres 453267
Récupérer mot avec symboles : {deuxième}
Récupérer mot avec symboles : (ligne)
Récupérer nombres : 453267
Classes de caractères définies par l'utilisateur : [...]

Vous pouvez définir vos propres classes de caractères. Une classe de caractères personnalisée est un jeu de caractères alternatifs, entourés de crochets. Voici quelques exemples qu'il est possible de réaliser :

  1. [abcxyz] : cette classe correspond à n'importe lequel des caractères a, b, c, x, y, z.
  2. [A-Za-z] : ici, le (-) tradruit une plage de caractères (tous ceux dont la valeur Unicode se trouve entre les deux limites). Cette classe définit toutes les lettres de l'alphabet en majuscule et en minuscule.
  3. [0-9] : cette classe exprime tous les chiffres. Elle est donc équivalente à \d.
  4. [0-9A-Fa-f] : chiffre hexadécimal.
  5. [x-y] : cette notation spéciale peut être utilisée comme un raccourci de tous les caractères alphanumériques. Elle est donc équivalente à \w.
  6. [^0-9] : un accent criconflexe (^) placé derrière le premier crochet droit inverse la classe. Ici, cette classe indique que nous désirons prendre tous les caractères qui ne sont pas des chiffres. Elle est donc équivalente à \D.
  7. [a-z&&[^p]] : la notation logique peut être utilisée pour définir une intersection (caractères en commun). Ici, cette classe indique que nous désirons prendre en compte toutes les lettres minuscules sauf p. Notez que les crochets autour de p sont absolument nécessaires.
String phrase = "Une phrase\tavec une {deuxième} colonne\net une (ligne) en plus\tet des chiffres 453267";
System.out.println(phrase);
String[] mots = phrase.split("\\s");
for (String mot : mots) 
   if (mot.matches("[a-z&&[^np]]*")) System.out.println(mot);

Résultats ----------------------------------------

Une phrase      avec une {deuxième} colonne
et une (ligne) en plus  et des chiffres 453267
avec
et
et
des
chiffres
Marqueurs de position : ^ $ \\b \\B

Il existe des caractères de position qui vous permettent de préciser la position relative d'une correspondance. Cette démarche est intéressante lorsque nous avons plusieurs occurences possibles par rapport aux critères de recherche spécifiés. Ainsi le motif ".ose" trouve trois correspondances dans la phrase suivante :

La rose n'est pas de couleur rose. C'est quand même la question qu'on se pose.

Voici comment préciser une correspondance particulière :

  1. ^ et $ : Les plus importantes sont ^ et $, qui positionnent, respectivement, la recherche en début ou en fin de ligne. "^.ose" correspond à rose du début de ligne et ".ose$" correspond à pose.
  2. \b, \B : Le marqueur de position \b donne la limite d'un mot. Si vous désirez savoir si un mot est présent dans votre chaîne, vous devez entourer ce mot de ce marqueur. Par exemple, le motif "\\brose\\b" correspond à rose, mais pas à rosemarie et couperose. Ce marqueur offre beaucoup plus de souplesse. Nous pouvons par exemple l'utiliser pour dire si un mot commence par certaines lettres. Par exemple, le motif "\\brose" correspond à rose et rosemarie, mais pas à couperose. De la même façon, nous pouvons contrôler si un mot se termine par certaines lettres. Par exemple, le motif "rose\\b" correspond à rose et couperose, mais pas à rosemarie. Nous pouvons même savoir si un mot commence par certaines lettres et se termine par d'autres. Par exemple, le motif "\\br\\w+e\\b" correspond à rose et rosemarie, mais pas à couperose. Comme d'habitude, \B correspond au critère inverse.
String première = "Le monde de java est-il parfait ?";
String deuxième = "Le javanais est-elle une langue intéressante ?";
if(première.matches(".*\\bjava\\b.*")) System.out.println("Première valide");
if(deuxième.matches(".*\\bjava\\b.*")) System.out.println("Deuxième valide");

Résultats ----------------------------------

Première valide

L'exemple ci-dessus permet de connaître si une phrase possède le mot java. C'est la première chaîne de caractères qui correspond à ce critère.
.

Multiplicités : + . ? {x} {x,y} {x, }

Mettre en correspondance de simples motifs de caractères fixes ne nous mène généralement pas bien loin. Dans les exemples que je propose d'ailleurs, il n'y en a pas. Nous allons maintenant examiner les opérateurs permettant de compter le nombre d'occurence d'un caractère ou d'un motif.

  1. N'importe quel nombre (zéro ou plus) : astérisque (*) : X* : Un astérisque (*) placé après un caractère (ou une classe de caractères) signifie "autorise n'importe quel nombre de caractère de ce type" - en d'autres termes, zéro ou plus. Par exemple, le motif "0*\\d" correspond à un chiffre avec n'importe quel nombre de zéros à gauche (peut-être aucun).
  2. Un certain nombre (un ou plus) : signe plus (+) : X+ : Le signe plus (+) signifie "une ou plusieurs" itérations. X+ est donc équivalent à XX*. Par exemple, le motif "0+\\d+" correspond à un nombre (d'un ou plusieurs chiffres) précédé d'au moins un zéro.

    String code = "1234 3456 3214 0045 0008";
    System.out.println(code);
    for (String valeur : code.split("\\s"))
       if (valeur.matches("0+\\d+")) System.out.println(Integer.parseInt(valeur));
    
    Résultats ----------------------------------------------
    
    1234 3456 3214 0045 0008
    45
    8
  3. Optionnel (zéro ou un) : point d'interrogation (?) : X? : L'opérateur point d'interrogation autorise précisément zéro ou une itération. Par exemple, le motif "\\d\\d/?\\d\\d" correspond à une date d'expiration de carte de crédit, qui peut contenir ou pas un slash :

    String code = "12/04 29-12 18 02 01?01 1008";
    System.out.println(code);
    for (String valeur : code.split("\\s"))
       if (valeur.matches("\\d\\d/?\\d\\d")) System.out.println(valeur);
    
    Résultats -----------------------------------------
    
    12/04 29-12 18 02 01?01 1008
    12/04
    1008
  4. Nombre de fois : accolades {nombre} : X{n} : Si vous désirez prendre en compte un caractère ou une classe de caractères avec un nombre précis de fois, vous devez spécifier votre nombre entre accolades. Par exemple, le motif "\\d{2}/?\\d{2}" correspond au même motif que précédemment.

    String code = "12/04 29-12 18 02 01?01 1008";
    System.out.println(code);
    for (String valeur : code.split("\\s"))
       if (valeur.matches("\\d{2}/?\\d{2}")) System.out.println(valeur);
    
    Résultats -----------------------------------------
    
    12/04 29-12 18 02 01?01 1008
    12/04
    1008
  5. Nombre correspondant à un intervalle de valeurs : (entre x et y itérations, bornes incluses) : {x,y} : L'opérateur {x,y} est l'opérateur le plus général. Il précise un intervalle de correspondance. Un intervalle prend deux arguments : une borne inférieure et une borne supérieure, séparées par une virgule. Le motif "\\w{4,6}" correspond à n'importe quel mot de quatre à six lettres, bornes incluses :

    String phrase = "Voici une phrase\tavec une deuxième colonne\net une ligne en plus";
    System.out.println(phrase);
    String[] mots = phrase.split("\\s");
    for (String mot : mots) 
       if (mot.matches("\\w{4,6}")) System.out.println(mot); 
    
    Résultats -----------------------------------------
    
    Voici une phrase        avec une deuxième colonne
    et une ligne en plus
    Voici
    phrase
    avec
    ligne
    plus
  6. Au moins un certain nombre : (x itération ou plus, y correspond à l'infini) : {x,} : Si vous omettez la borne supérieure, en ne saisissant aucune valeur après la virgule, elle devient infinie. C'est une façon de spécifier un minimum d'occurences, sans maximum. Le motif "\\w{4,}" correspond à n'importe quel mot composé d'au moins quatre lettres :

    String phrase = "Voici une phrase\tavec une deuxième colonne\net une ligne en plus";
    System.out.println(phrase);
    String[] mots = phrase.split("\\s");
    for (String mot : mots) 
       if (mot.matches("\\w{4,}")) System.out.println(mot); 
    
    Résultats -----------------------------------------
    
    Voici une phrase        avec une deuxième colonne
    et une ligne en plus
    Voici
    phrase
    avec
    colonne
    ligne
    plus
Regroupements : ( )

Comme dans des opérations logiques ou mathématiques, les parenthèses peuvent être utilisées dans les expressions régulières, pour créer des expressions secondaires ou pour fixer des limites à une exception. Cette possibilité permet d'étendre l'usage des opérateurs que nous venons de voir aux mots et autres expressions régulières. Le motif "(4\\d){2,}" permet de prendre en compte uniquement les nombres qui sont structurés systématiquement avec le chiffre 4 suivi d'un autre chiffre et ceci au moins deux fois dans le nombre concerné :

String phrase = "4345 3467 4940 453344 484341 4234";
System.out.println(phrase);
String[] nombres = phrase.split(" ");
for (String nombre : nombres) 
   if (nombre.matches("(4\\d){2,}")) System.out.println(Integer.parseInt(nombre));

Résultats -----------------------------

4345 3467 4940 3324 3841 4234
4345
4940 484341

Ici, nous appliquons {2,} (2 ou plus) sur le motif 4\\d, et non pas à un seul caractère.
.

En utilisant les regroupements, nous pouvons commencer à bâtir des expressions plus complexes. Par exemple, même si la plupart des adresses mail sont structurées en trois parties (par exemple, machin@bidule.fr), un nom de domaine peut comporter un nombre arbitraire d'éléments séparés par des points. Pour gérer ce phénomène correctement, nous pouvons utiliser une expression comme celle-ci :

\\w+@\\w+(\.\\w)+ // correspond à toute adresse mail

Cette expression correspond à un mot, suivi d'un symbole @, suivi d'un autre mot, puis d'un ou plusieurs mots séparés par des points, par exemple pat@pat.net, bob@truc.bidule.fr ou autre@truc.bidule.co.uk

Groupes de capture : (...) \\1 ou $1

En plus des opérations de regroupement de base, les parenthèses jouent un autre rôle important : le texte correspondant à chaque sous-expression peut être récupéré séparément. Ce qui veut dire que vous pouvez isoler le texte correspondant à chaque sous-expression. Il existe alors une syntaxe spéciale permettant, à l'intérieur de l'expression régulière, de se référer à chaque groupe de capture par un nombre. Cette fonctionnalité importante peut être utilisée dans deux cas.

  1. Premièrement, vous pouvez bâtir une expression régulière qui se réfère au texte déjà mis en correspondance, et l'utiliser par la suite comme un paramètre dans d'autres comparaisons. Cela vous permet d'élaborer des motifs très puissants. Le motif "\\b(\\w)\\w*\\1\\b" nous permet de rechercher tous les mots commençant et finissant par la même lettre.

Vous voyer le \\1 dans cette expression ? c'est une référence au premier groupe de capture de l'expression \\w. Les références à des groupes de capture sont de la forme \\n n est le numéro du groupe de capture, en comptant de gauche à droite. Dans cet exemple, le premier groupe de capture correspond à un caractère en début de mot. Il est alors précisé que cette lettre peut être suivie par n'importe quel nombre de lettres jusqu'à la référence spéciale \\1 (également suivi par une borne de mot). Le \\1 signifie "la valeur doit correspondre au groupe de capture 1". Comme ces caractères doivent être les mêmes, ce motif identifie les mots commençant et finissant par la même lettre.

  1. La deuxième utilisation des groupes de capture a trait aux portions de mots utilisés dans les remplacements. La chaîne de remplacement transmise aux méthodes replaceAll() et replaceFirst() ne doit pas forcément représenter une simple chaîne de caractères littérale ; elle peut aussi contenir des références à du texte correspondant à des sous-expressions entre parenthèses au sein d'un motif. Ces références prennent la forme du signe dollar ($) suivi par le numéro de la sous-expression.

    String phrase = "Les JavaBeans ainsi que JavaScript sont souvent utilisées";
    String nouvelle = phrase.replaceAll("\\bJava([A-Z]\\w+)", "J$1");
    System.out.println(phrase);
    System.out.println(nouvelle);
       
    Résultats ----------------------------------------------
    
    Les JavaBeans ainsi que JavaScript sont souvent utilisées
    Les JBeans ainsi que JScript sont souvent utilisées
    L'exemple ci-dessus permet de chercher les mots comme JavaBean, JavaScript, JavaOs et JavaVM (mais pas Java ou Javanaise), et remplace ensuite le préfixe Java de ces mots par la lettre J sans altérer le suffixe (correspondant au groupe de capture). Vous remarquez dans ce cas là, que le signe dollar ($) sert de séparateur entre la lettre (ou le mot) à prendre en compte et l'indice du sous-groupe dans l'expression régulière.

Bien entendu, les groupes de capture peuvent contenir plus d'un caractère, et vous pouvez utiliser autant de groupes que vous voulez.
.

Numérotation des groupes de capture : un ((deux) (trois (quatre)))

Les groupes de capture sont numérotés à partir de 1 et de gauche à droite, en comptant le nombre de parenthèses ouvrantes qui les précèdent. Le nombre spécial 0 fait toujours référence au résultat global de l'expression régulière. Considérons, par exemple, l'expression ci-dessous :

un ((deux) (trois (quatre)))

Elle fournit les résultats suivants :

Groupe 0 : un deux trois quatre
Groupe 1 : deux trois quatre
Groupe 2 : deux
Groupe 3 : trois quatre
Groupe 4 : quatre

Plus tôt dans ce chapitre, nous avons découvert la méthode group() de la classe Matcher, qui sans argument, propose la sous-chaîne correspondant à la recherche effectuée sur un motif sans prise en compte de groupe de capture. Il existe une deuxième méthode group() de cette classe Matcher, qui possède un agument entier. Cette méthode est alors capable de récupérer la sous-chaîne correspondant, cette fois-ci au groupe souhaité. Pour cela, votre motif doit mettre en oeuvre des groupes de captures, et lors de l'appel de la méthode, vous spécifiez le numéro du groupe qui vous intéresse. En voici un exemple :

Résultats

Texte global : 10h53mn
Heures et minutes : 10h53
Heures uniquement : 10
Minutes uniquement : 53

 
String phrase = "10h53mn";
String motif = "(([1-2]?[0-9])h([0-5][0-9]))mn";
Matcher correspondance = Pattern.compile(motif).matcher(phrase);
if (correspondance.find()) {
   System.out.println("Texte global : "+correspondance.group(0));
   System.out.println("Heures et minutes : "+correspondance.group(1));
   System.out.println("Heures uniquement : "+correspondance.group(2));
   System.out.println("Minutes uniquement : "+correspondance.group(3));
} 
Permutation : |

La barre verticale (|) signale l'opérateur logique "ou", également appelé permutation ou choix. L'opérateur | n'agit pas sur les caractères individuels mais sur ce qui les entoure. Il décompose l'expression en deux. Par exemple, nous pourrions analyser une date comme suit :

\\w+, \\d{2} \\w+ \\d{4}|\\d{2}/\\d{2}/\\d{4}

Dans cette expression, la partie gauche correspond à des dates du type : (lundi, 19 mars 2007), et celle de droite à des dates de type (19/03/2007).

L'expression régulière suivante permet de trouver les adresses mail de trois domaines (net, edu, et gouv) :

\\w+@[\\w\.]*\.(net|edu|gouv)

Options spéciales : (?i) (?s) (?m) (?d)

Plusieurs options spéciales permettent de modifier le fonctionnement d'une expression régulière. Pour cela, il faut utiliser la syntaxe suivante (?x) x est l'option que vous souhaitez activer. En général, ceci est fait en début d'expression régulière. Vous pouvez également désactiver des options en rajoutant un signe moins (?-x), ce qui vous permet de n'appliquer les options qu'à certaines parties de votre motif. Les options suivantes sont disponibles :

  1. Insensible à la casse : (?i) : L'option (?i) indique que l'expression régulière ne doit pas tenir compte de la casse. "(?i)java" correspond aussi bien à java que Java, JAVA, etc.
  2. Dot all : (?s) : L'option (?s) passe en mode "dot all" (dot : point), permettant au caractère point (.) de remplacer n'importe quel caractères, y compris les caractères de fin de ligne. Ceci est utile lorsque nous analysons des chaînes sur plusieurs lignes.
  3. Multiligne : (?m) : Par défaut ^ et $ ne correspondent pas aux début et à la fin de chaque ligne (définis par des retours chariots ou combinaisons de saut de ligne) ; ils correspondent au début et à la fin de tout le texte. Activer le mode multiligne avec l'option (?m) les obligent à tenir compte du début et de la fin de chaque ligne et de ceux du texte complet.
  4. Lignes UNIX : (?d) : L'option (?d) remplace les caractères spéciaux de fin de ligne ^, $ et . par la syntaxe UNIX (\n). Par défaut, le retour chariot combiné à une nouvelle ligne est également autorisé (\r\n).
String phrase = "Les JavaBeans sont souvent utilisées, le javanais un peu moins.";
for (String mot : phrase.split(" "))
   if (mot.matches("(?i)java(\\w+)")) System.out.println(mot);
   
Résultats ------------------------------------------

JavaBeans
javanais
Gourmandise : *?, +*, {x,y}?, {x}?, {x, }?

Faisons une petite expérience. Supposons que nous voulions identifier toutes les balises XML pour, par exemple, les supprimer par la suite. Nous pourrions donc proposer le motif suivant :

</?.*> // cherche <, éventuellement /, et tous les caractères avant >

Faisons un petit programme qui permet de valider ce motif :

String phrase = "<gras>Agissez</gras>, au lieu de parler";
Matcher correspondance = Pattern.compile("</?.*>").matcher(phrase);
while (correspondance.find())
   System.out.println(correspondance.group());

Résultat ----------------------------------

<gras>Agissez</gras>

Malheureusement, le résultat ne correspond pas à notre attente. pour quelle raison ? Le problème est que l'opérateur .*, comme tous les opérateurs d'itération d'ailleurs, sont par défaut "gourmand", et ingurgitent tout ce qu'ils peuvent, jusqu'à ce qu'ils atteignent la dernière occurence du caractère de fin.

Pour résoudre ce problème, nous pouvons prendre un opérateur réluctant. En effet, chaque opérateur d'itération peut être utilisé sous une forme alternative, plus économe et qui se contente du minimum de caractères possible, tout en continuant à examiner les caractères qui suivent. Les opérateurs réluctants sont identiques à l'opérateur standard suivi d'un (?), comme *?, +?, {x}?, {x,}?, {x,y}?.

En reprenant notre exemple, voici ce que nous pourrions faire :

</?.*?> // cherche <, éventuellement /, le minimum de caractères, puis >

Testons le :

String phrase = "<gras>Agissez</gras>, au lieu de parler";
Matcher correspondance = Pattern.compile("</?.*?>").matcher(phrase);
while (correspondance.find())
   System.out.println(correspondance.group());

Résultats ----------------------------------

<gras>
</gras>

 

Choix du chapitre Utilisation de délimiteurs pour décomposer du texte

Dans de toutes petites applications, il est quelque fois utile de prévoir un simple fichier qui sert de base de données rudimentaire. Dans ce cas là, il est nécessaire de prévoir des délimitations particulières pour stocker les informations prévues. Ces délimitations sont bien utiles pour permettre par la suite de reconnaître chacune des données élémentaires.

Ainsi, chaque ligne de votre fichier texte peut servir à stocker l'identité d'un employé dans une entreprise. Chaque élément de l'identité, comme le nom, le prénom, son emploi, sa date de naissance, etc. sont séparés par un délimiteur particulier comme le (|) :



Si nous faisons une petite application qui doit consulter ce type de fichier, nous obtenons pour chaque ligne de texte, une longue chaîne de caractères qu'il va falloir découper en sous-chaînes distinctes afin de récupérer chacun des champs utiles. Cela implique de repérer les délimiteurs (|) puis de procéder au découpage en séquences de caractères allant jusqu'aux délimiteurs suivants (ces éléments ainsi obtenus sont appelés des tokens).

La classe StringTokenizer de java.util a été conçue dans le but d'offrir une méthode rapide pour découper une vaste chaîne contenant du texte délimité : c'est un analyseur lexical. L'idée de base est de lier un objet instance de l'analyseur lexical à une chaîne. Lorsque nous construisons cet objet, nous spécifions à ce moment là, les délimiteurs à prendre en compte. Par exemple, dans le cas qui nous préoccupe, nous utilisons :

StringTokenizer découpe = new StringTokenizer(ligne, "|");

Il est également possible de spécifier plusieurs délimiteurs dans la chaîne, par exemple :

StringTokenizer découpe = new StringTokenizer(ligne, "|,;");

Ainsi, tout caractère de chaîne peut servir de délimiteur.

Si vous ne spécifiez pas de jeu de délimiteurs, le paramètre par défaut est " \t\n\r", à savoir tous les caractères d'espace vide (espace, tabulation, nouvelle ligne et retour chariot).

Une fois l'analyseur lexical construit, ses méthodes vont permettre d'extraire efficacement les portions de la chaîne. La méthode nextTocken() renvoie la portion suivante se trouvant encore dans la chaîne. La méthode hasMoreTokens() renvoie true s'il reste encore des portions de chaîne. Enfin la méthode countTockens() renvoie le nombre de portions de chaîne présentes encore dans la chaîne globale.

Les méthodes de la classe java.util.StringTokenizer
StringTokenizer(String chaîne, String délimiteurs)
Construit un analyseur synthaxique de chaîne de caractères avec le jeu de délimiteurs spécifié.
StringTokenizer(String chaîne)
Construit un analyseur synthaxique de chaîne de caractères avec, par défaut, le jeu de délimiteurs suivant : " \t\n\r".
boolean hasMoreTokens()
Renvoie true s'il reste des portions de chaîne à analyser.
String nextToken()
Renvoie la portion de chaîne suivante. Lance une exception NoSuchElementException s'il ne reste plus de portion de chaîne à analyser.
String nextToken(String délimiteurs)
Renvoie la portion de chaîne suivante après passage à un autre ensemble de délimiteurs. Le nouvel ensemble de délimiteurs remplace le précédent.
int countTokens()
Renvoie le nombre de portions de chaîne restant dans la chaîne globale.
Exemple de codage
package délimiteurs;

import java.util.*;

public class Délimiteurs {
   public static void main(String[] args) { 
      Scanner clavier = new Scanner(System.in);
      System.out.println("Insérer votre chaîne avec délimiteur | pour en récupérer les portions");
      System.out.println();
      String chaîne = clavier.nextLine();
      System.out.println();
      StringTokenizer analyseur = new StringTokenizer(chaîne, "|");
      System.out.println("Nombre de parties : "+analyseur.countTokens());
      while (analyseur.hasMoreTokens()) System.out.println(analyseur.nextToken());
   }
}
Résultat

Insérer votre chaîne avec délimiteur | pour en récupérer les portions

Voici mon texte|avec un certain|nombre de délimiteurs|à bientôt

Nombre de parties : 4
Voici mon texte
avec un certain
nombre de délimiteurs
à bientôt

 

 

Choix du chapitre Décomposition de texte à l'aide de la classe Scanner

Nous avons déjà largement utilisé la classe Scanner, notamment dans la gestion des flux d'entrée de texte. Nous l'avons également utilisée pour récupérer toutes les saisies issues du clavier. En réalité, la classe Scanner est une classe beaucoup plus polyvalente. Elle tire profit des expressions régulières que nous venons d'étudier. Avant tout, sa spécialité, c'est la décomposition de texte. Certe, cette classe Scanner peut prendre en entrée des textes venant d'un fichier ou d'un flux quelconque, mais également et tout simplement des chaînes de caractères, et c'est là que ça devient intéressant.

Un objet Scanner peut décomposer le texte qu'il reçoit en entrée en occurences séparées par un espace blanc ou par tout autre caractère de délimitation ou expression régulière. Scanner définit aussi une variété de méthodes utilitaires pour analyser les occurences sous forme de valeur booléenne, entières ou à virgule flottante, avec une analyse syntaxique des nombres prenant en compte les paramètres de localisation. Elle possède des méthodes skip() pour éviter les occurences correspondant à un motif spécifié, ainsi que des méthodes permettant de chercher vers l'avant des occurences correspondante à un motif spécifié.

La classe Scanner n'analyse pas seulement des littéraux entiers ou réels. Elle sait aussi reconnaître les séparateurs de milliers ainsi que le séparateur de la partie décimale conformément au pays concerné. Dans le cas de la France, nous pouvons donc avoir en entrée la valeur suivante : 1 234,45.

java.util.Scanner
Méthodes Retour Explication
Scanner(InputStream flux)   Construit un objet Scanner à partir du flux d'entrée. Le flux est alors interprété sous forme de flux de texte. Toutefois, cette classe est capable de décomposer du texte en élément. Ces éléments peuvent être de différentes natures. Ainsi par exemple, il est possible de récupérer une suite de caractères représentant une valeur entière directement transformée dans le type int.
Scanner(String text)   Construit un objet Scanner à partir cette fois-ci d'une chaîne de caractères qui sera alors possible de décomposer en éléments de nature différente.
findInLine(String motif)
findInLine(Pattern motif)
String Cherche, dans la ligne courante, du texte semblable à l'expression régulière spécifiée. Si une correspondance est trouvée, le Scanner avance jusqu'au texte et le retourne.
hasNext() boolean Retourne true si un mot est présent dans la ligne courante. En réalité, Scanner découpe la chaîne de caractères d'entrée en plusieurs morceaux. Une série d'espaces blancs constitue le délimiteur par défaut. Nous pouvons spécifer un autre délimiteur au travers de la méthode useDelimiteur().
hasNext(String motif)
hasNext(Pattern motif)
boolean Cette méthode est similaire à la précédente. Ici, la méthode accepte une expression régulière en argument et retourne true si les données en entrée sont semblables à cette expression.
hasNextBoolean()
hasNextByte()
hasNextByte(int base)
hasNextDouble()
hasNextFloat()
hasNextInt()
hasNextInt(int base)
hasNextLong()
hasNextLong(int base)
hasNextShort()
hasNextShort(int base)
boolean Contrôle si la prochaine valeur en entrée (suite de caractères) peut se transformer dans la valeur correspondant au type spécifié dans le nom de la méthode. Pour les types entiers, ces méthodes sont mêmes capables de déterminer si la suite de caractères proposée respecte la base spécifiée en argument.
hasNextLine()
boolean Contrôle si une ligne est présente. Cette fois-ci, il s'agit d'une chaîne de caractères qui se termine par une fin de ligne.
next()
next(String motif)
next(Pattern motif)
String Découpe les données en entrée en une série d'occurence String, séparés par des espaces blancs ou par le délimiteur spécifié par la méthode useDelimiter(). Chaque appel à next(), par défaut, retourne le mot pointé par le curseur courant. Il est également possible de travailler avec les expressions régulières pour récupérer la valeur correspond au motif établi.
nextBoolean()
nextByte()
nextByte(int base)
nextDouble()
nextFloat()
nextInt()
nextInt(int base)
nextLong()
nextLong(int base)
nextShort()
nextShort(int base)
boolean
byte
byte
double
float
int
int
long
long
short
short
Interprète la chaîne de caractère présente dans la ligne courante et la tranforme dans le type correspondant à la méthode choisie. Si la tranformation se déroule correctement la méthode renvoie la valeur. Pour les types entiers, il est possible de travailler avec des valeurs numériques de base quelconque. La méthode retourne alors la valeur décimale équivalente.
nextLine() String Retourne toute la chaîne jusqu'à la fin de ligne.
skip(String motif)
skip(Pattern motif)
Scanner Cette méthode ignore les délimiteurs et saute le texte correspondant à l'expression régulière spécifiée.
useDelimiter(String motif)
useDelimiter(Pattern motif)
Scanner Cette méthode spécifie une expression régulière qui représente le délimiteur d'occurence. Une série d'espace blancs constitue le délimiteur par défaut.
useLocale(Locale pays)
Scanner Spécifie le pays pour analyser les nombres. Cela peut influencer des éléments tels que les caractères utilisés pour le point décimal ou comme le séparateur de milliers.
useRadix(int base) Scanner Spécifie la base sur laquelle les nombres devraient être analysés. Toutes les valeurs entre 2 et 36 sont autorisées.

 

Nous allons mettre au point un premier programme qui utilise la classe Scanner dont le but est de traiter une chaîne de caractères issue du clavier. Cette chaîne correspond à un numéro de compte qui possède une structure particulière. Normalement, ce numéro est composé de trois parties. Il doit comporter d'abord trois lettres en majuscules suivi de 5 chiffres binaires et enfin de trois chifres décimaux. Les différentes parties sont séparées par un espace. Voici l'exemple d'un numéro valide : DUF 10110 342.

import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);
      System.out.println("Récupérer un numéro de compte avec des espaces de séparation");
      System.out.println("Votre numéro de compte : ");
      String compte = clavier.nextLine();
      Scanner saisie = new Scanner(compte);
      if (saisie.hasNext("[A-Z]{3}")) System.out.println("Référence de base : "+saisie.next());
      else System.err.println("Le 1er groupe doit posséder 3 lettres");
      if (saisie.hasNext("[0-1]{5}")) System.out.println("Valeur décimale du premier nombre : "+saisie.nextInt(2));
      else System.err.println("Le 2ème groupe doit être une valeur binaire sur 5 chiffres");
      if (saisie.hasNext("\\d{3}")) System.out.println("Deuxième nombre : "+saisie.nextInt());
      else System.err.println("Le 3ème groupe doit posséder 3 chiffres");
   }
}
Résultats

Votre numéro de compte :
DUF 10110 342
Référence de base : DUF
Valeur décimale du premier nombre : 22
Deuxième nombre : 342

---------------------------------------------------

Votre numéro de compte :
DUF10110342
Le 1er groupe doit posséder 3 lettres
Le 2ème groupe doit être une valeur binaire sur 5 chiffres
Le 3ème groupe doit posséder 3 chiffres

---------------------------------------------------

Votre numéro de compte :
DUF 10110 34U
Référence de base : DUF
Valeur décimale du premier nombre : 22
Le 3ème groupe doit posséder 3 chiffres

 

Nous allons reprendre le même type d'exercice. Cette fois-ci, par contre, le numéro de compte ne possède pas d'espace séparateur. Voici un numéro de compte valide : DUF10110342.

import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);
      System.out.println("Récupérer un numéro de compte avec des espaces de séparation");
      System.out.println("Votre numéro de compte : ");
      String compte = clavier.nextLine();      
      if (compte.matches("[A-Z]{3}[0-1]{5}\\d{3}")) {
         Scanner saisie = new Scanner(compte);
         System.out.println("Référence de base : "+saisie.findInLine("[A-Z]{3}"));
         System.out.println("Valeur décimale du premier nombre : "+Integer.parseInt(saisie.findInLine("[0-1]{5}"), 2));
         System.out.println("Deuxième nombre : "+Integer.parseInt(saisie.findInLine("\\d{3}")));
      }
      else System.err.println("Votre numéro n'est pas bon");
   }
}
Résultats

Votre numéro de compte :
DUF10110342
Référence de base : DUF
Valeur décimale du premier nombre : 22
Deuxième nombre : 342

 

En reprenant le même exemple, voici comment utiliser les méthodes useDelimiter() et skip(). Cette fois-ci, le nombre binaire est évalué après :

import java.util.Scanner;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);
      System.out.println("Récupérer un numéro de compte avec des espaces de séparation");
      System.out.println("Votre numéro de compte : ");
      String compte = clavier.nextLine();      
      if (compte.matches("[A-Z]{3}[0-1]{5}\\d{3}")) {
         Scanner saisie = new Scanner(compte);
         saisie.useDelimiter("\\d");
         System.out.println("Référence de base : "+saisie.next());
         saisie.useDelimiter("\\s");
         System.out.println("Deuxième nombre : "+saisie.skip("[0-1]{5}").nextInt());
         System.out.println("Valeur décimale du premier nombre : "+new Scanner(compte.substring(3,8)).nextInt(2));
      }
      else System.err.println("Votre numéro n'est pas bon");
   }
}
Résultats

Votre numéro de compte :
DUF10110347
Référence de base :
DUF
Deuxième nombre :
347
Valeur décimale du premier nombre :
22