4. Classi di uso comune
In questo capitolo presentiamo alcune classi Java di uso comune. Queste dispongono di numerosi attributi, metodi e costruttori. In ogni caso, presentiamo solo una piccola parte delle classi. I dettagli relativi a queste sono disponibili nella guida di Java che presentiamo ora.
4.1. La documentazione
Se avete installato il JDK di Sun nella cartella <jdk>, la documentazione è disponibile nella cartella <jdk>\docs:

A volte si dispone di un file jdk ma senza documentazione. Quest’ultima è reperibile sul sito di Sun http://www.sun.com. Nella cartella docs si trova un file index.html che costituisce il punto di partenza della guida di JDK:


Il link API & Language riportato sopra consente di accedere alle classi Java. Il link Demos/Tutorials è particolarmente utile per trovare esempi di programmi Java. Seguiamo il link API & Language:

Seguiamo il link Java 2 Platform API:

Questa pagina è il vero punto di partenza della documentazione sulle classi. È possibile creare un collegamento a questa pagina per accedervi rapidamente. Il percorso di URL è <jdk>\docs\api\index.html. Qui si trovano collegamenti a centinaia di classi Java del JDK. Quando si è alle prime armi, la difficoltà principale è capire a cosa servono queste diverse classi. In un primo momento, quindi, questa guida è utile solo se si conosce il nome della classe su cui si desiderano informazioni. Ci si può anche lasciare guidare dai nomi delle classi, che di solito indicano la funzione della classe stessa.
Facciamo un esempio e cerchiamo informazioni sulla classe Vector che implementa un array dinamico. È sufficiente cercare nell’elenco delle classi nel riquadro di sinistra il link alla classe Vector:

e cliccare sul link per visualizzare la definizione della classe:

Qui si trova
- la gerarchia in cui si trova la classe, in questo caso java.util.Vector
- l'elenco dei campi (attributi) della classe
- l'elenco dei costruttori
- l'elenco dei metodi
Di seguito presentiamo diverse classi. Invitiamo il lettore a verificare sistematicamente la definizione completa delle classi utilizzate.
4.2. Le classi di prova
Gli esempi che seguono utilizzano talvolta le classi personne e enseignant. Ne riportiamo qui la definizione.
public class personne{
// cognome, nome, età
private String prenom;
private String nom;
private int age;
// costruttore 1
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// costruttore 2
public personne(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
// toString
public String toString(){
return "personne("+prenom+","+nom+","+age+")";
}
// accessori
public String getPrenom(){
return prenom;
}
public String getNom(){
return nom;
}
public int getAge(){
return age;
}
//modificatori
public void setPrenom(String P){
this.prenom=P;
}
public void setNom(String N){
this.nom=N;
}
public void setAge(int age){
this.age=age;
}
}
La classe enseignant deriva dalla classe personne ed è definita come segue:
class enseignant extends personne{
// attributi
private int section;
// costruttore
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
// toString
public String toString(){
return "etudiant("+super.toString()+","+section+")";
}
}
Utilizzeremo inoltre una classe etudiant derivata dalla classe personne e definita come segue:
class etudiant extends personne{
String numero;
public etudiant(String P, String N, int age,String numero){
super(P,N,age);
this.numero=numero;
}
public String toString(){
return "etudiant("+super.toString()+","+numero+")";
}
}
4.3. La classe String
La classe String rappresenta le stringhe di caratteri. Supponiamo che nom sia una variabile di tipo stringa:
String nome;
nom è un riferimento a un oggetto non ancora inizializzato. È possibile inizializzarlo in due modi:
nom="cavallo" oppure nom=new String("cavallo")
I due metodi sono equivalenti. Se in seguito si scrive nom="pesce", nom farà riferimento a un nuovo oggetto. Il vecchio oggetto String("cheval") andrà perso e lo spazio di memoria che occupava verrà recuperato.
La classe String è ricca di attributi e metodi. Eccone alcuni:
public char charAt(int i) | restituisce il carattere i della stringa, dove il primo carattere ha indice 0. Quindi String("cheval").charAt(3) è uguale a 'v' |
public int compareTo(stringa2) | chaine1.compareTo(stringa2) confronta stringa1 con stringa2 e restituisce 0 se stringa1 = stringa2, 1 se stringa1 > stringa2, -1 se stringa1 < stringa2 |
public boolean equals(Object anObject) | chaine1.equals(stringa2) restituisce vero se stringa1 = stringa2, falso in caso contrario |
public String toLowerCase() | chaine1.toLowerCase() converte stringa1 in minuscolo |
public String toUpperCase() | chaine1.toUpperCase() converte stringa1 in maiuscolo |
public String trim() | chaine1.trim() rimuove gli spazi iniziali e finali da stringa1 |
public String substring(int beginIndex, int endIndex) | String("chapeau").subString(2,4) restituisce la stringa "ape" |
public char[] toCharArray() | consente di inserire i caratteri della stringa in un array di caratteri |
int length() | numero di caratteri della stringa |
int indexOf(String stringa2) | restituisce la prima posizione di chaine2 nella stringa corrente oppure -1 se chaine2 non è presente |
int indexOf(String stringa2, int startIndex) | restituisce la prima posizione di chaine2 nella stringa corrente oppure -1 se chaine2 non è presente. La ricerca inizia a partire dal carattere n. startIndex. |
int lastIndexOf(String stringa2) | restituisce l'ultima posizione di chaine2 nella stringa corrente oppure -1 se chaine2 non è presente |
boolean startsWith(String stringa2) | restituisce vero se la stringa corrente inizia con chaine2 |
boolean endsWith(String stringa2) | restituisce vero se la stringa corrente termina con chaine2 |
boolean matches(String regex) | restituisce vero se la stringa corrente corrisponde all'espressione regolare regex. |
String[] split(String regex) | La stringa corrente è composta da campi separati da una sequenza di caratteri modellata dall'espressione regolare regex. Il metodo split consente di recuperare i campi in un array. |
String replace(char oldChar, char newChar) | sostituisce nella stringa corrente il carattere oldChar con il carattere newChar. |
Ecco un programma di esempio:
// importazioni
import java.io.*;
public class string1{
// una classe di dimostrazione
public static void main(String[] args){
String uneChaine="l'oiseau vole au-dessus des nuages";
affiche("uneChaine="+uneChaine);
affiche("uneChaine.Length="+uneChaine.length());
affiche("chaine[10]="+uneChaine.charAt(10));
affiche("uneChaine.IndexOf(\"vole\")="+uneChaine.indexOf("vole"));
affiche("uneChaine.IndexOf(\"x\")="+uneChaine.indexOf("x"));
affiche("uneChaine.LastIndexOf('a')="+uneChaine.lastIndexOf('a'));
affiche("uneChaine.LastIndexOf('x')="+uneChaine.lastIndexOf('x'));
affiche("uneChaine.substring(4,7)="+uneChaine.substring(4,7));
affiche("uneChaine.ToUpper()="+uneChaine.toUpperCase());
affiche("uneChaine.ToLower()="+uneChaine.toLowerCase());
affiche("uneChaine.Replace('a','A')="+uneChaine.replace('a','A'));
String[] champs=uneChaine.split("\\s+");
for (int i=0;i<champs.length;i++){
affiche("champs["+i+"]=["+champs[i]+"]");
}//per
affiche("(\" abc \").trim()=["+" abc ".trim()+"]");
}//Main
// visualizza
public static void affiche(String msg){
// visualizza msg
System.out.println(msg);
}//visualizza
}//classe
e i risultati ottenuti:
uneChaine=l'oiseau vole au-dessus des nuages
uneChaine.Length=34
chaine[10]=o
uneChaine.IndexOf("vole")=9
uneChaine.IndexOf("x")=-1
uneChaine.LastIndexOf('a')=30
uneChaine.LastIndexOf('x')=-1
uneChaine.substring(4,7)=sea
uneChaine.ToUpper()=L'OISEAU VOLE AU-DESSUS DES NUAGES
uneChaine.ToLower()=l'oiseau vole au-dessus des nuages
uneChaine.Replace('a','A')=l'oiseAu vole Au-dessus des nuAges
champs[0]=[l'oiseau]
champs[1]=[vole]
champs[2]=[au-dessus]
champs[3]=[des]
champs[4]=[nuages]
(" abc ").trim()=[abc]
4.4. La classe Vector
Un vettore è un array dinamico i cui elementi sono riferimenti a oggetti. Si tratta quindi di un array di oggetti la cui dimensione può variare nel tempo, cosa che non è possibile con gli array statici che abbiamo visto finora. Ecco alcuni campi, costruttori o metodi di questa classe:
public Vector() | crea un vettore vuoto |
public final int size() | numero di elementi del vettore |
public final void addElement(Object obj) | aggiunge l'oggetto a cui fa riferimento obj al vettore |
public final Object elementAt(int index) | riferimento all'oggetto n. index del vettore - gli indici partono da 0 |
public final Enumeration elements() | l'insieme degli elementi del vettore sotto forma di enumerazione |
public final Object firstElement() | riferimento al primo elemento del vettore |
public final Object lastElement() | riferimento all'ultimo elemento del vettore |
public final boolean isEmpty() | restituisce true se il vettore è vuoto |
public final void removeElementAt(int index) | rimuove l'elemento con indice index |
public final void removeAllElements() | svuota il vettore di tutti i suoi elementi |
public final String toString() | restituisce una stringa di identificazione del vettore |
Ecco un programma di prova:
// le classi importate
import java.util.*;
public class test1{
// il programma principale main - statico - metodo di classe
public static void main(String arg[]){
// la creazione degli oggetti (istanze delle classi)
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// il polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// un vettore
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
for(int i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
} // fine di main
}// fine classe
Compiliamo questo programma:
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>dir
10/06/2002 10:41 1 134 personne.class
10/06/2002 10:41 619 enseignant.class
10/06/2002 10:41 610 etudiant.class
10/06/2002 10:42 1 035 test1.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>javac test1.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>dir
10/06/2002 10:41 1 134 personne.class
10/06/2002 10:41 619 enseignant.class
10/06/2002 10:41 610 etudiant.class
10/06/2002 10:42 1 035 test1.java
10/06/2002 10:43 1 506 test1.class
Eseguiamo il file test1.class:
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>java test1
p=personne(Jean,Dupont,30)
en=etudiant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=etudiant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
Taille du vecteur V = 3
V[0]=personne(Jean,Dupont,30)
V[1]=etudiant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)
In seguito, non ripeteremo più il processo di compilazione ed esecuzione dei programmi di test. È sufficiente ripetere quanto fatto in precedenza.
4.5. La classe ArrayList
La classe ArrayList è analoga alla classe Vector. Si differenzia da essa sostanzialmente solo quando viene utilizzata contemporaneamente da più thread di esecuzione. I metodi di sincronizzazione dei thread per l’accesso a un Vector o a un ArrayList differiscono. Al di fuori di questo caso, è possibile utilizzare indifferentemente l’uno o l’altro. Ecco alcuni campi, costruttori o metodi di questa classe:
ArrayList() | crea un array vuoto |
int size() | numero di elementi dell'array |
void add(Object obj) | aggiunge l'oggetto a cui fa riferimento obj all'array |
void add(int index, Object obj) | aggiunge l'oggetto a cui fa riferimento obj all'array nella posizione index |
Object get(int index) | riferimento all'oggetto n. index dell'array - gli indici partono da 0 |
boolean isEmpty() | restituisce vero se l'array è vuoto |
void remove(int index) | rimuove l'elemento con indice index |
void clear() | svuota l'array di tutti i suoi elementi |
Object[] toArray() | converte l'array dinamico in un array classico |
String toString() | restituisce una stringa di identificazione dell'array |
Ecco un programma di prova:
// le classi importate
import java.util.*;
public class test1{
// il programma principale main - static - metodo di classe
public static void main(String arg[]){
// creazione di oggetti istanze di classi
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p);
System.out.println("en="+en);
System.out.println("et="+et);
// il polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2);
personne p3=(personne)et;
System.out.println("p3="+p3);
// un vettore
ArrayList personnes=new ArrayList();
personnes.add(p);personnes.add(en);personnes.add(et);
System.out.println("Nombre de personnes = "+personnes.size());
for(int i=0;i<personnes.size();i++){
p2=(personne) personnes.get(i);
System.out.println("personnes["+i+"]="+p2);
}
} // fine main
}// fine classe
I risultati ottenuti sono gli stessi di prima.
4.6. La classe Arrays
La classe java.util.Arrays fornisce l'accesso a metodi statici che consentono diverse operazioni sugli array, in particolare l'ordinamento e la ricerca di elementi. Ecco alcuni di questi metodi:
static void sort(array) | ordina tableau utilizzando l’ordine implicito del tipo di dati dell’array, ovvero numeri o stringhe di caratteri. |
static void sort (Object[] array, Comparator C) | ordina tableau utilizzando la funzione di confronto C per confrontare gli elementi |
static int binarySearch(array, elemento) | restituisce la posizione di élément in tableau oppure un valore <0 in caso contrario. L'array deve essere stato precedentemente ordinato. |
static int binarySearch(Object[] array, Object elemento, Comparator C) | idem, ma utilizza la funzione di confronto C per confrontare due elementi dell'array. |
Ecco un primo esempio:
import java.util.*;
public class sort2 implements Comparator{
// una classe interna privata
private class personne{
private String nom;
private int age;
public personne(String nom, int age){
this.nom=nom; // nome della persona
this.age=age; // la sua età
}
// recuperare l'età
public int getAge(){
return age;
}
// identità della persona
public String toString(){
return ("["+nom+","+age+"]");
}
}; // classe persona
// costruttore
public sort2() {
// un array di persone
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// ordinamento dell'array di persone
Arrays.sort(amis,this);
// verifica
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//creatore
// la funzione che confronta le persone
public int compare(Object o1, Object o2){
// deve restituire
// -1 se o1 è "minore di" o2
// 0 se o1 è "uguale a" o2
// +1 se o1 è "maggiore di" o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//confronta
// funzione di verifica
public static void main(String[] arg){
new sort2();
}//mano
}//classe
Analizziamo questo programma. La funzione main crea un oggetto sort2. Il costruttore della classe sort2 è il seguente:
// costruttore
public sort2() {
// un elenco di persone
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// ordinamento dell'elenco di persone
Arrays.sort(amis,this);
// verifica
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//produttore
L'array da ordinare è un array di oggetti personne. La classe personne è definita in modo privato (private) all'interno della classe sort2. Il metodo statico sort della classe Arrays non è in grado di ordinare un array di oggetti personne, pertanto in questo caso è necessario utilizzare la forma void sort(Object[] obj, Comparator C). Comparator è un’interfaccia che definisce un solo metodo:
e che deve restituire 0: se o1=o2, -1 se o1 < o2, +1 se o1 > o2. Nel prototipo void sort(Object[] obj, Comparator C), il secondo argomento C deve essere un oggetto che implementi l'interfaccia Comparator. Nel costruttore sort2, è stato scelto l'oggetto corrente this:
Ciò ci obbliga a fare due cose:
- indicare che la classe sort2 implementa l’interfaccia Comparator
- scrivere la funzione compare nella classe sort2.
Il codice è il seguente:
// la funzione che confronta le persone
public int compare(Object o1, Object o2){
// deve restituire
// -1 se o1 è "minore di" o2
// 0 se o1 è "uguale a" o2
// +1 se o1 è "maggiore di" o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//confronta
Per confrontare due oggetti personne, in questo caso si utilizza l'età (si sarebbe potuto utilizzare il nome).
I risultati dell'esecuzione sono i seguenti:
Si sarebbe potuto procedere in modo diverso per implementare l'interfaccia Comparator:
import java.util.*;
public class sort2 {
// una classe privata interna
private class personne{
…….
}; // classe persona
// costruttore
public sort2() {
// un array di persone
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// ordinamento dell'array di persone
Arrays.sort(amis,
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//confronto
}//classe
);
// verifica
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//generatore
// la funzione che confronta le persone
public int compare1(Object o1, Object o2){
// deve restituire
// -1 se o1 è "minore di" o2
// 0 se o1 è "uguale a" o2
// +1 se o1 è "maggiore di" o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//confronta1
// main
public static void main(String[] arg){
new sort2();
}//main
}//classe
L'istruzione di ordinamento è diventata la seguente:
// ordinamento dell'array delle persone
Arrays.sort(amis,
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//confronta
}//classe
);
Il secondo parametro del metodo sort deve essere un oggetto che implementi l'interfaccia Comparator. Qui creiamo un oggetto di questo tipo tramite new java.util.Comparator() e il testo che segue {….} definisce la classe di cui si crea un oggetto. Si parla di classe anonima poiché non ha un nome. In questa classe anonima, che deve implementare l’interfaccia Comparator, si definisce il metodo compare di tale interfaccia. Quest’ultimo si limita a richiamare il metodo compare1 della classe sort2. Ci si ritrova quindi nella situazione precedente.
La classe sort2 non implementa più l'interfaccia Comparator. Pertanto, la sua dichiarazione diventa:
Ora testiamo il metodo binarySearch della classe Arrays con il seguente esempio:
import java.util.*;
public class sort4 {
// una classe privata interna
private class personne{
// attributi
private String nom;
private int age;
// costruttore
public personne(String nom, int age){
this.nom=nom; // nome della persona
this.age=age; // la sua età
}
// recuperare il nome
public String getNom(){
return nom;
}
// recuperare l'età
public int getAge(){
return age;
}
// identità della persona
public String toString(){
return ("["+nom+","+age+"]");
}
}; // classe persona
// costruttore
public sort4() {
// un array di persone
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// comparatori
java.util.Comparator comparateur1=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//confronta
}//classe
;
java.util.Comparator comparateur2=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare2(o1,o2);
}//confronta
}//ordina
;
// ordinamento della tabella delle persone
Arrays.sort(amis,comparateur1);
// verifica
for(int i=0;i<3;i++)
System.out.println(amis[i]);
// ricerche
cherche("milou",amis,comparateur2);
cherche("xx",amis,comparateur2);
}//generatore
// la funzione che confronta le persone
public int compare1(Object o1, Object o2){
// deve restituire
// -1 se o1 è "minore di" o2
// 0 se o1 è "uguale a" o2
// +1 se o1 è "maggiore di" o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//compare1
// la funzione che confronta una persona con un nome
public int compare2(Object o1, Object o2){
// o1 è una persona
// o2 è una stringa, il nome nome2 di una persona
// deve restituire
// -1 se o1.nom è "minore di" nom2
// 0 se o1.nom è "uguale a" nom2
// +1 se o1.nom è "maggiore di" nome2
personne p1=(personne)o1;
String nom1=p1.getNom();
String nom2=(String)o2;
return nom1.compareTo(nom2);
}//confronta2
public void cherche(String ami,personne[] amis, Comparator comparateur){
// cerca amico nella tabella amici
int position=Arrays.binarySearch(amis,ami,comparateur);
// trovato?
if(position>=0)
System.out.println(ami + " a " + amis[position].getAge() + " ans");
else System.out.println(ami + " n'existe pas dans le tableau");
}//cerca
// mano
public static void main(String[] arg){
new sort4();
}//mano
}//classe
In questo caso abbiamo proceduto in modo leggermente diverso rispetto agli esempi precedenti. I due oggetti Comparator necessari per i metodi sort e binarySearch sono stati creati e assegnati alle variabili comparateur1 e comparateur2.
// comparatori
java.util.Comparator comparateur1=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//confronta
}//classe
;
java.util.Comparator comparateur2=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare2(o1,o2);
}//confronta
}//classe
;
Nel generatore di sort4 viene eseguita due volte una ricerca dicotomica sulla tabella amis:
Il metodo cherche riceve tutti i parametri necessari per richiamare il metodo binarySearch:
public void cherche(String ami,personne[] amis, Comparator comparateur){
// cerca un amico nella tabella degli amici
int position=Arrays.binarySearch(amis,ami,comparateur);
// trovato?
if(position>=0)
System.out.println(ami + " a " + amis[position].getAge() + " ans");
else System.out.println(ami + " n'existe pas dans le tableau");
}//cerca
Il metodo binarySearch opera con il comparatore comparateur2, che a sua volta richiama il metodo compare2 della classe sort4. Il metodo rend restituisce la posizione del nome cercato nell'array, se presente, oppure un numero <0 in caso contrario. Il metodo compare2 serve a confrontare un oggetto personne con un nome di tipo String.
// la funzione che confronta una persona con un nome
public int compare2(Object o1, Object o2){
// o1 è una persona
// o2 è una stringa, il nome "nome2" di una persona
// deve restituire
// -1 se o1.nom "è minore di" nome2
// 0 se o1.nom "uguale a" nome2
// +1 se o1.nom "maggiore di" nome2
personne p1=(personne)o1;
String nom1=p1.getNom();
String nom2=(String)o2;
return nom1.compareTo(nom2);
}//confronta2
A differenza del metodo sort, il metodo binarySearch non riceve due oggetti personne, ma un oggetto personne e un oggetto String in quest'ordine. Il primo parametro è un elemento dell’array amis, il secondo è il nome della persona ricercata.
4.7. La classe Enumeration
Enumeration è un'interfaccia e non una classe. Dispone dei seguenti metodi:
public abstract boolean hasMoreElements() | restituisce vero se l’enumerazione contiene ancora elementi |
public abstract Object nextElement() | restituisce il riferimento all'elemento successivo dell'enumerazione |
Come si utilizza un'enumerazione? In genere in questo modo:
Enumeration e=… // si recupera un oggetto enumerazione
while(e.hasMoreElements()){
// utilizzare l'elemento e.nextElement()
}
Ecco un esempio:
// le classi importate
import java.util.*;
public class test1{
// il programma principale main - statico - metodo di classe
public static void main(String arg[]){
// creazione di oggetti (istanze di classe)
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// il polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// un vettore
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
int i;
for(i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
// un'enumerazione
Enumeration E=V.elements();
i=0;
while(E.hasMoreElements()){
p2=(personne) E.nextElement();
System.out.println("V["+i+"]="+p2.toString());
i++;
}
}// fine main
}//fine classe
Si ottengono i seguenti risultati:
p=personne(Jean,Dupont,30)
en=enseignant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=enseignant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
Taille du vecteur V = 3
V[0]=personne(Jean,Dupont,30)
V[1]=enseignant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)
V[0]=personne(Jean,Dupont,30)
V[1]=enseignant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)
4.8. La classe Hashtable
La classe Hashtable consente di implementare un dizionario. È possibile considerare un dizionario come una tabella a due colonne:
chiave | valore |
chiave1 | valore1 |
chiave2 | valore2 |
.. | ... |
Le chiavi sono univoche, c.a.d. Non possono esserci due chiavi identiche. I principali metodi e proprietà della classe Hashtable sono i seguenti:
public Hashtable() | costruttore - crea un dizionario vuoto |
public int size() | numero di elementi nel dizionario - dove un elemento è una coppia (chiave, valore) |
public Object put(Object key, Object value) | aggiunge la coppia (chiave, valore) al dizionario |
public Object get(Object key) | recupera l'oggetto associato alla chiave key oppure null se la chiave key non esiste |
public boolean containsKey(Object key) | vero se la chiave key esiste nel dizionario |
public boolean contains(Object value) | vero se il valore value è presente nel dizionario |
public Enumeration keys() | restituisce le chiavi del dizionario sotto forma di enumerazione |
public Object remove(Object key) | rimuove la coppia (chiave, valore) dove chiave=key |
public String toString() | identifica il dizionario |
Ecco un esempio:
// le classi importate
import java.util.*;
public class test1{
// il programma principale main - statico - metodo di classe
public static void main(String arg[]){
// la creazione di oggetti istanze di classi
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// il polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// un dizionario
Hashtable H=new Hashtable();
H.put("personne1",p);
H.put("personne2",en);
H.put("personne3",et);
Enumeration E=H.keys();
int i=0;
String cle;
while(E.hasMoreElements()){
cle=(String) E.nextElement();
p2=(personne) H.get(cle);
System.out.println("clé "+i+"="+cle+" valeur="+p2.toString());
i++;
}
}//fine di main
}//fine classe
I risultati ottenuti sono i seguenti:
p=personne(Jean,Dupont,30)
en=enseignant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=enseignant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
clé 0=personne3 valeur=etudiant(personne(Chris,Garot,22),19980405)
clé 1=personne2 valeur=enseignant(personne(Paula,Hanson,56),27)
clé 2=personne1 valeur=personne(Jean,Dupont,30)
4.9. I file di testo
4.9.1. Scrivere
Per scrivere in un file, è necessario disporre di un flusso di scrittura. A tal fine è possibile utilizzare la classe FileWriter. I costruttori più comunemente utilizzati sono i seguenti:
FileWriter(String fileName) | crea il file denominato fileName - è quindi possibile scriverci dentro - un eventuale file con lo stesso nome viene sovrascritto |
FileWriter(String fileName, boolean append) | idem – un eventuale file con lo stesso nome può essere utilizzato aprendolo in modalità aggiunta (append=true) |
La classe FileWriter offre una serie di metodi per scrivere in un file, metodi ereditati dalla classe Writer. Per scrivere in un file di testo, è preferibile utilizzare la classe PrintWriter, i cui costruttori più comunemente utilizzati sono i seguenti:
PrintWriter(Writer out) | l’argomento è di tipo Writer, c.a.d. un flusso di scrittura (in un file, in rete, …) |
PrintWriter(Writer out, boolean autoflush) | idem. Il secondo argomento gestisce il buffering delle righe. Quando è impostato su false (valore predefinito), le righe scritte sul file passano attraverso un buffer in memoria. Quando quest’ultimo è pieno, il contenuto viene scritto nel file. Ciò migliora l’accesso al disco. Detto questo, a volte questo comportamento è indesiderato, in particolare quando si scrive in rete. |
I metodi utili della classe PrintWriter sono i seguenti:
void print(Type T) | scrive il dato T (String, int, …) |
void println(Type T) | come sopra, terminando con un carattere di fine riga |
void flush() | svuota il buffer se non si è in modalità autoflush |
void close() | chiude il flusso di scrittura |
Ecco un programma che scrive alcune righe in un file di testo:
// importazioni
import java.io.*;
public class ecrire{
public static void main(String[] arg){
// apertura del file
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// scrittura nel file
try{
fic.println("Jean,Dupont,27");
fic.println("Pauline,Garcia,24");
fic.println("Gilles,Dumond,56");
} catch (Exception e){
Erreur(e,3);
}
// chiusura del file
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// fine main
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Errore
}//classe
Il file out ottenuto dall'esecuzione è il seguente:
4.9.2. Lettura
Per leggere il contenuto di un file, è necessario disporre di un flusso di lettura associato al file. A tal fine è possibile utilizzare la classe FileReader e il seguente costruttore:
FileReader(String nomeFile) | apre un flusso di lettura dal file specificato. Genera un'eccezione se l'operazione non va a buon fine. |
La classe FileReader dispone di una serie di metodi per la lettura da un file, metodi ereditati dalla classe Reader. Per leggere righe di testo da un file di testo, è preferibile utilizzare la classe BufferedReader con il seguente costruttore:
BufferedReader(Reader in) | apre un flusso di lettura bufferizzato da un flusso di input in. Questo flusso di tipo Reader può provenire dalla tastiera, da un file, dalla rete, ... |
I metodi utili della classe BufferedReader sono i seguenti:
int read() | legge un carattere |
String readLine() | legge una riga di testo |
int read(char[] buffer, int offset, int taille) | legge taille caratteri dal file e li inserisce nell'array buffer a partire dalla posizione offset. |
void close() | chiude il flusso di lettura |
Ecco un programma che legge il contenuto del file creato in precedenza:
// classi importate
import java.util.*;
import java.io.*;
public class lire{
public static void main(String[] arg){
personne p=null;
// apertura del file
BufferedReader IN=null;
try{
IN=new BufferedReader(new FileReader("out"));
} catch (Exception e){
Erreur(e,1);
}
// dati
String ligne=null;
String[] champs=null;
String prenom=null;
String nom=null;
int age=0;
// gestione di eventuali errori
try{
while((ligne=IN.readLine())!=null){
champs=ligne.split(",");
prenom=champs[0];
nom=champs[1];
age=Integer.parseInt(champs[2]);
System.out.println(""+new personne(prenom,nom,age));
}// fine while
} catch (Exception e){
Erreur(e,2);
}
// chiusura del file
try{
IN.close();
} catch (Exception e){
Erreur(e,3);
}
}// fine main
// Errore
public static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}
}// fine classe
L'esecuzione del programma fornisce i seguenti risultati:
4.9.3. Salvataggio di un oggetto persona
Applichiamo quanto appena visto per fornire alla classe personne un metodo che consenta di salvare in un file gli attributi di una persona. Aggiungiamo il metodo sauveAttributs nella definizione della classe personne:
// ------------------------------
// salvataggio in file di testo
// ------------------------------
public void sauveAttributs(PrintWriter P){
P.println(""+this);
}
Prima di definire la classe personne, non dimenticare di importare il pacchetto java.io:
Il metodo sauveAttributs riceve come unico parametro il flusso PrintWriter in cui deve scrivere. Un programma di prova potrebbe essere il seguente:
// importazioni
import java.io.*;
// import persona;
public class sauver{
public static void main(String[] arg){
// apertura del file
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// scrittura nel file
try{
new personne("Jean","Dupont",27).sauveAttributs(fic);
new personne("Pauline","Garcia",24).sauveAttributs(fic);
new personne("Gilles","Dumond",56).sauveAttributs(fic);
} catch (Exception e){
Erreur(e,3);
}
// chiusura del file
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// fine main
// Errore
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Errore
}//classe
Compiliamo ed eseguiamo questo programma:
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>javac sauver.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>dir
10/06/2002 10:52 1 352 personne.class
10/06/2002 10:53 842 sauver.java
10/06/2002 10:53 1 258 sauver.class
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>java sauver
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>dir
10/06/2002 10:52 1 352 personne.class
10/06/2002 10:53 842 sauver.java
10/06/2002 10:53 1 258 sauver.class
10/06/2002 10:53 83 out
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>more out
personne(Jean,Dupont,27)
personne(Pauline,Garcia,24)
personne(Gilles,Dumond,56)
4.10. I file binari
4.10.1. La classe RandomAccessFile
La classe RandomAccessFile consente di gestire i file binari, in particolare quelli a struttura fissa come quelli che conosciamo nel linguaggio C/C++. Ecco alcuni metodi e costruttori utili:
RandomAccessFile(String nomeFile, String modalità) | costruttore - apre il file specificato nella modalità indicata. Quest’ultima assume i seguenti valori: r: apertura in lettura rw: apertura in lettura e scrittura |
void writeTTT(TTT valore) | scrive valore nel file. TTT rappresenta il tipo di valore. La rappresentazione in memoria di valore viene scritta così com'è nel file. Si trovano quindi writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat,... Per scrivere una stringa, si utilizza writeBytes(String stringa). |
TTT readTTT() | legge e restituisce un valore di tipo TTT. Si trovano quindi readBoolean, readByte, readInt, readDouble, readLong, readFloat,... Il metodo read() legge un byte. |
long length() | dimensione del file in byte |
long getFilePointer() | posizione corrente del puntatore del file |
void seek(long pos) | posiziona il cursore del file sul byte pos |
4.10.2. La classe articolo
Tutti gli esempi che seguiranno utilizzeranno la seguente classe article:
// la struttura dell'articolo
private static class article{
// si definisce la struttura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe articolo
La classe Java article sopra riportata sarà equivalente alla seguente struttura article in C
struct article{
char code[4];
char nom[20];
double prix;
int stockActuel;
int stockMinimum;
}//struttura
In questo modo limiteremo il codice a 4 caratteri e il nome a 20.
4.10.3. Scrivere un record
Il seguente programma scrive un record in un file denominato "data":
// classi importate
import java.io.*;
public class test1{
// verifica la scrittura di una struttura (nel senso del linguaggio C) in un file binario
// la struttura articolo
private static class article{
// si definisce la struttura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe articolo
public static void main(String arg[]){
// si definisce il file binario in cui saranno archiviati gli articoli
RandomAccessFile fic=null;
// si definisce un articolo
article art=new article();
art.code="a100";
art.nom="velo";
art.prix=1000.80;
art.stockActuel=100;
art.stockMinimum=10;
// si definisce il file
try{
fic=new RandomAccessFile("data","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// si scrive
try{
ecrire(fic,art);
} catch (IOException E){
erreur("Erreur lors de l'écriture de l'enregistrement",2);
}//try-catch
// è finito
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}//main
// metodo di scrittura
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// codice
fic.writeBytes(art.code);
// il nome è limitato a 20 caratteri
art.nom=art.nom.trim();
int l=art.nom.length();
int nbBlancs=20-l;
if(nbBlancs>0){
String blancs="";
for(int i=0;i<nbBlancs;i++) blancs+=" ";
art.nom+=blancs;
} else art.nom=art.nom.substring(0,20);
fic.writeBytes(art.nom);
// il prezzo
fic.writeDouble(art.prix);
// le scorte
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// fine scrittura
// ------------------------errore
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fine errore
}// fine classe
È il seguente programma che ci permette di verificare che l'esecuzione sia avvenuta correttamente.
4.10.4. Leggere un record
// classi importate
import java.io.*;
public class test2{
// verifica la scrittura di una struttura (nel senso del C) in un file binario
// la struttura article
private static class article{
// si definisce la struttura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe articolo
public static void main(String arg[]){
// si definisce il file binario in cui saranno archiviati gli articoli
RandomAccessFile fic=null;
// si apre il file in lettura
try{
fic=new RandomAccessFile("data","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// si legge l'unico articolo dal file
article art=new article();
try{
lire(fic,art);
} catch (IOException E){
erreur("Erreur lors de la lecture de l'enregistrement",2);
}//try-catch
// si visualizza il record letto
affiche(art);
// fine
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}// fine manuale
// metodo di lettura
public static void lire(RandomAccessFile fic, article art) throws IOException{
// lettura del codice
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nome
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prezzo
art.prix=fic.readDouble();
// scorte
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fine scrittura
// ---------------------visualizza
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fine visualizzazione
// ------------------------errore
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fine errore
}// fine classe
I risultati dell'esecuzione sono i seguenti:
E:\data\serge\JAVA\random>java test2
code : a100
nom : velo
prix : 1000.8
Stock actuel : 100
Stock minimum : 10
Si recupera correttamente il record che era stato scritto dal programma di scrittura.
4.10.5. Conversione da testo a binario
Il programma seguente è un'estensione del programma di scrittura di un record. Ora si scrivono più record in un file binario denominato data.bin. I dati sono tratti dal seguente file data.txt:
E:\data\serge\JAVA\random>more data.txt
a100:velo:1000:100:10
b100:pompe:65:6:2
c100:arc:867:10:5
d100:fleches - lot de 6:450:12:8
e100:jouet:10:2:3
// classi importate
import java.io.*;
import java.util.*;
public class test3{
// file di testo --> file binario
// la struttura dell'articolo
private static class article{
// si definisce la struttura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe articolo
public static void main(String arg[]){
// si definisce il file binario in cui saranno archiviati gli articoli
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin",1);
}
// i dati vengono prelevati da un file di testo
BufferedReader dataTxt=null;
try{
dataTxt=new BufferedReader(new FileReader("data.txt"));
} catch (IOException E){
erreur("Impossible d'ouvrir le fichier data.txt",2);
}
// file .txt --> file .bin
String ligne=null;
String[] champs=null;
int numLigne=0;
String champ=null;
article art=new article(); // articolo da creare
try{
while((ligne=dataTxt.readLine())!=null){
// una riga di +
numLigne++;
// scomposizione in campi
champs=ligne.split(":");
// sono necessari 5 campi
if(champs.length!=5)
erreur("Ligne "+numLigne+" erronée dans data.txt",3);
//codice
art.code=champs[0];
if(art.code.length()!=4)
erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
// cognome, nome
art.nom=champs[1];
// prezzo
try{
art.prix=Double.parseDouble(champs[2]);
} catch (Exception E){
erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
}
// stock attuale
try{
art.stockActuel=Integer.parseInt(champs[3]);
} catch (Exception E){
erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
}
// stock attuale
try{
art.stockActuel=Integer.parseInt(champs[3]);
} catch (Exception E){
erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
}
// si scrive il record
try{
ecrire(dataBin,art);
} catch (IOException E){
erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
}
// si passa alla riga successiva
}// fine while
} catch (IOException E){
erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
}
// finito
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",10);
}
try{
dataTxt.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.txt",11);
}
}// fine main
// metodo di scrittura
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// codice
fic.writeBytes(art.code);
// il nome è limitato a 20 caratteri
art.nom=art.nom.trim();
int l=art.nom.length();
int nbBlancs=20-l;
if(nbBlancs>0){
String blancs="";
for(int i=0;i<nbBlancs;i++) blancs+=" ";
art.nom+=blancs;
} else art.nom=art.nom.substring(0,20);
fic.writeBytes(art.nom);
// il prezzo
fic.writeDouble(art.prix);
// le scorte
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// fine scrittura
// ------------------------errore
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fine errore
}// fine classe
Il seguente programma consente di verificare che il precedente abbia funzionato correttamente.
4.10.6. Conversione da binario a testo
Il seguente programma legge il contenuto del file binario data.bin creato in precedenza e ne inserisce il contenuto nel file di testo data.text. Se tutto va bene, il file data.text dovrebbe essere identico al file originale data.txt.
// classi importate
import java.io.*;
import java.util.*;
public class test5{
// file di testo --> file binario
// la struttura dell'articolo
private static class article{
// si definisce la struttura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe articolo
// main
public static void main(String arg[]){
// si definisce il file binario in cui verranno archiviati gli articoli
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
}
// i dati vengono scritti in un file di testo
PrintWriter dataTxt=null;
try{
dataTxt=new PrintWriter(new FileWriter("data.text"));
} catch (IOException E){
erreur("Impossible d'ouvrir le fichier data.text en écriture",2);
}
// file .bin --> file .text
article art=new article(); // articolo da creare
// si analizza il file binario
int numRecord=0;
long l=0; // dimensione del file
try{
l=dataBin.length();
} catch (IOException e){
erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
}
long pos=0; // posizione corrente nel file
try{
pos=dataBin.getFilePointer();
} catch (IOException e){
erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
}
// finché non si supera la fine del file
while(pos<l){
// leggere il record corrente ed elaborarlo
numRecord++;
try{
lire(dataBin,art);
} catch (Exception e){
erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
}
affiche(art);
// scrivere la riga di testo corrispondente in dataTxt
dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);
// si continua?
try{
pos=dataBin.getFilePointer();
} catch (IOException e){
erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
}
}// fine while
// è finito
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",2);
}
try{
dataTxt.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.text",2);
}
}// fine main
// metodo di lettura
public static void lire(RandomAccessFile fic, article art) throws IOException{
// lettura codice
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nome
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prezzo
art.prix=fic.readDouble();
// scorte
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fine scrittura
// ---------------------visualizza
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fine visualizzazione
// ------------------------errore
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fine errore
}// fine classe
Ecco un esempio di esecuzione:
E:\data\serge\JAVA\random>java test5
code : a100
nom : velo
prix : 1000.0
Stock actuel : 100
Stock minimum : 0
code : b100
nom : pompe
prix : 65.0
Stock actuel : 6
Stock minimum : 0
code : c100
nom : arc
prix : 867.0
Stock actuel : 10
Stock minimum : 0
code : d100
nom : fleches - lot de 6
prix : 450.0
Stock actuel : 12
Stock minimum : 0
code : e100
nom : jouet
prix : 10.0
Stock actuel : 2
Stock minimum : 0
E:\data\serge\JAVA\random>more data.text
a100:velo:1000.0:100:0
b100:pompe:65.0:6:0
c100:arc:867.0:10:0
d100:fleches - lot de 6:450.0:12:0
e100:jouet:10.0:2:0
4.10.7. Accesso diretto ai record
Quest'ultimo programma illustra la possibilità di accedere direttamente ai record di un file binario. Visualizza il record del file data.bin, il cui numero viene passato come parametro, dove il primo record ha il numero 1.
// classi importate
import java.io.*;
import java.util.*;
public class test6{
// file di testo --> file binario
// la struttura dell'articolo
private static class article{
// si definisce la struttura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe articolo
// main
public static void main(String[] args){
// si verificano gli argomenti
int nbArguments=args.length;
String syntaxe="syntaxe : pg numéro_de_fiche";
if(nbArguments!=1)
erreur(syntaxe,20);
// verifica del numero di scheda
int numRecord=0;
try{
numRecord=Integer.parseInt(args[0]);
} catch(Exception e){
erreur(syntaxe+"\nNuméro de fiche incorrect",21);
}
// si apre il file binario in lettura
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
}
// ci si posiziona sulla scheda desiderata
try{
dataBin.seek((numRecord-1)*40);
} catch (Exception e){
erreur("La fiche "+numRecord+" n'existe pas",23);
}
// la si legge
article art=new article();
try{
lire(dataBin,art);
} catch (Exception e){
erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
}
// la si visualizza
affiche(art);
// il processo è terminato
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",2);
}//try-catch
}// fine main
// metodo di lettura
public static void lire(RandomAccessFile fic, article art) throws IOException{
// codice di lettura
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nome
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prezzo
art.prix=fic.readDouble();
// scorte
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fine scrittura
// ---------------------visualizza
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fine visualizzazione
// ------------------------errore
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fine errore
}// fine classe
Ecco alcuni esempi di esecuzione:
E:\data\serge\JAVA\random>java test6 2
code : b100
nom : pompe
prix : 65.0
Stock actuel : 6
Stock minimum : 0
E:\data\serge\JAVA\random>java.bat test6 20
Erreur lors de la lecture de l'enregistrement 20
4.11. Utilizzo delle espressioni regolari
4.11.1. Il pacchetto java.util.regex
Il pacchetto java.util.regex consente l'utilizzo di espressioni regolari. Queste consentono di verificare il formato di una stringa di caratteri. In questo modo è possibile verificare che una stringa che rappresenta una data sia effettivamente nel formato gg/mm/aa. A tal fine si utilizza un modello e si confronta la stringa con tale modello. In questo esempio, quindi, j, m e a devono essere cifre. Il modello di un formato di data valido è quindi "\d\d/\d\d/\d\d", dove il simbolo \d indica una cifra. I simboli utilizzabili in un modello sono i seguenti (documentazione Microsoft):
Carattere | Descrizione |
\ | Contrassegna il carattere seguente come carattere speciale o letterale. Ad esempio, "n" corrisponde al carattere "n". "\n" corrisponde a un carattere di nuova riga. La sequenza "\\" corrisponde a "\", mentre "\(" corrisponde a "(". |
^ | Corrisponde all'inizio dell'immissione. |
$ | Corrisponde alla fine dell'input. |
* | Corrisponde al carattere precedente zero o più volte. Pertanto, "zo*" corrisponde a "z" o a "zoo". |
+ | Corrisponde al carattere precedente una o più volte. Pertanto, "zo+" corrisponde a "zoo", ma non a "z". |
? | Corrisponde al carattere precedente zero o una volta. Ad esempio, "a?ve?" corrisponde a "ve" in "lever". |
. | Corrisponde a qualsiasi singolo carattere, eccetto il carattere di nuova riga. |
(modello) | Cerca modèle e memorizza la corrispondenza. La sottostringa corrispondente può essere estratta dalla collezione Matches ottenuta, utilizzando Item [0]...[n]. Per trovare corrispondenze con caratteri tra parentesi ( ), utilizzare "\(" o "\)". |
x|y | Corrisponde sia a x sia a y. Ad esempio, "z|foot" corrisponde a "z" o a "foot". "(z|f)oo" corrisponde a "zoo" o a "foo". |
{n} | n è un numero intero non negativo. Corrisponde esattamente a n volte il carattere. Ad esempio, "o{2}" non corrisponde a "o" in "Bob," ma alle prime due "o" in "fooooot". |
{n,} | n è un numero intero non negativo. Corrisponde ad almeno n volte il carattere. Ad esempio, "o{2,}" non corrisponde alla "o" in "Bob", ma a tutte le "o" in "fooooot". "o{1,}" equivale a "o+" e "o{0,}" equivale a "o*". |
{n,m} | m e n sono numeri interi non negativi. Corrisponde a un numero di occorrenze del carattere compreso tra un minimo di n e un massimo di m. Ad esempio, "o{1,3}" corrisponde alle prime tre "o" in "foooooot" e "o{0,1}" equivale a "o?". |
[xyz] | Insieme di caratteri. Corrisponde a uno dei caratteri indicati. Ad esempio, "[abc]" corrisponde alla "a" in "plat". |
[^xyz] | Set di caratteri negativo. Corrisponde a qualsiasi carattere non indicato. Ad esempio, "[^abc]" corrisponde alla "p" in "plat". |
[a-z] | Intervallo di caratteri. Corrisponde a qualsiasi carattere compreso nella serie specificata. Ad esempio, "[a-z]" corrisponde a qualsiasi carattere alfabetico minuscolo compreso tra "a" e "z". |
[^m-z] | Intervallo di caratteri negativo. Corrisponde a qualsiasi carattere non presente nella serie specificata. Ad esempio, "[^m-z]" corrisponde a qualsiasi carattere non compreso tra "m" e "z". |
\b | Corrisponde a un delimitatore che rappresenta una parola, ovvero alla posizione tra una parola e uno spazio. Ad esempio, "er\b" corrisponde a "er" in "lever", ma non a "er" in "verbe". |
\B | Corrisponde a un limite che non rappresenta una parola. "en*t\B" corrisponde a "ent" in "bien entendu". |
\d | Corrisponde a un carattere che rappresenta una cifra. Equivale a [0-9]. |
\D | Corrisponde a un carattere che non rappresenta una cifra. Equivale a [^0-9]. |
\f | Corrisponde a un carattere di fine pagina. |
\n | Corrisponde a un carattere di nuova riga. |
\r | Corrisponde a un carattere di ritorno a capo. |
\s | Corrisponde a qualsiasi carattere di spaziatura, inclusi spazio, tabulazione, salto di pagina, ecc. Equivale a "[ \f\n\r\t\v]". |
\S | Corrisponde a qualsiasi carattere non bianco. Equivale a "[^ \f\n\r\t\v]". |
\t | Corrisponde a un carattere di tabulazione. |
\v | Corrisponde a un carattere di tabulazione verticale. |
\w | Corrisponde a qualsiasi carattere che rappresenti una parola e includa un trattino basso. Equivale a "[A-Za-z0-9_]". |
\W | Corrisponde a qualsiasi carattere che non rappresenti una parola. Equivale a "[^A-Za-z0-9_]". |
\num | Corrisponde a num, dove num è un numero intero positivo. Si riferisce alle corrispondenze memorizzate. Ad esempio, "(.)\1" corrisponde a due caratteri identici consecutivi. |
|
Un elemento in un modello può essere presente una o più volte. Consideriamo alcuni esempi relativi al simbolo \d, che rappresenta una cifra:
modello | significato |
\d | una cifra |
\d? | 0 o 1 cifra |
\d* | 0 o più cifre |
\d+ | 1 o più cifre |
\d{2} | 2 cifre |
\d{3,} | almeno 3 cifre |
\d{5,7} | da 5 a 7 cifre |
Immaginiamo ora il modello in grado di descrivere il formato previsto per una stringa di caratteri:
stringa ricercata | modello |
una data nel formato gg/mm/aa | \d{2}/\d{2}/\d{2} |
un'ora nel formato hh:mm:ss | \d{2}:\d{2}:\d{2} |
un numero intero senza segno | \d+ |
una sequenza di spazi, eventualmente vuota | \s* |
un numero intero senza segno che può essere preceduto o seguito da spazi | \s*\d+\s* |
un numero intero che può essere con segno e preceduto o seguito da spazi | \s*[+|-]?\s*\d+\s* |
un numero reale senza segno che può essere preceduto o seguito da spazi | \s*\d+(.\d*)?\s* |
un numero reale che può essere con segno e preceduto o seguito da spazi | \s*[+|]?\s*\d+(.\d*)?\s* |
una stringa contenente la parola "giusto" | \bjusto\b |
È possibile specificare dove cercare il modello nella stringa:
modello | significato |
^modello | il modello inizia la stringa |
modello$ | il modello conclude la stringa |
^modello$ | il modello inizia e termina la stringa |
modello | il modello viene cercato in tutta la stringa a partire dall'inizio della stessa. |
stringa cercata | modello |
una stringa che termina con un punto esclamativo | !$ |
una stringa che termina con un punto | \.$ |
una stringa che inizia con la sequenza // | ^// |
una stringa composta da una sola parola, eventualmente preceduta o seguita da spazi | ^\s*\w+\s*$ |
una stringa contenente due parole, eventualmente precedute o seguite da spazi | ^\s*\w+\s*\w+\s*$ |
una stringa contenente la parola "secret" | \bsecret\b |
I sottoinsiemi di un modello possono essere "recuperati". In questo modo, non solo è possibile verificare che una stringa corrisponda a un modello specifico, ma è anche possibile recuperare all'interno di tale stringa gli elementi corrispondenti ai sottoinsiemi del modello racchiusi tra parentesi. Pertanto, se si analizza una stringa contenente una data gg/mm/aa e si desidera inoltre estrarre gli elementi gg, mm, aa di tale data, si utilizzerà il modello (\d\d)/(\d\d)/(\d\d).
4.11.2. Verificare che una stringa corrisponda a un modello dato
La classe Pattern consente di verificare se una stringa corrisponde a un modello dato. A tal fine si utilizza il metodo statico
con: modèle: il modello da verificare, chaine: la stringa da confrontare con il modello. Il risultato è il valore booleano true se la stringa corrisponde al modello, false in caso contrario.
Ecco un esempio:
import java.io.*;
import java.util.regex.*;
// gestione delle espressioni regolari
public class regex1 {
public static void main(String[] args){
// un'espressione regolare modello
String modèle1="^\\s*\\d+\\s*$";
// confronto di un'istanza con il modello
String exemplaire1=" 123 ";
if (Pattern.matches(modèle1,exemplaire1)){
affiche("["+exemplaire1 + "] correspond au modèle ["+modèle1+"]");
}else{
affiche("["+exemplaire1 + "] ne correspond pas au modèle ["+modèle1+"]");
}//if
String exemplaire2=" 123a ";
if (Pattern.matches(modèle1,exemplaire2)){
affiche("["+exemplaire2 + "] correspond au modèle ["+modèle1+"]");
}else{
affiche("["+exemplaire2 + "] ne correspond pas au modèle ["+modèle1+"]");
}//if
}//main
public static void affiche(String msg){
System.out.println(msg);
}//visualizza
}//classe
e i risultati dell'esecuzione:
Si noti che nel modello "^\s*\d+\s*$" il carattere \ deve essere duplicato a causa della particolare interpretazione che Java dà di questo carattere. Si scrive quindi: String modello1="^\\s*\\d+\\s*$";
4.11.3. Trovare tutti gli elementi di una stringa che corrispondono a un modello
Consideriamo il modello "\d+" e la stringa " 123 456 789 ". Il modello si trova in tre punti diversi della stringa. Le classi Pattern e Matcher consentono di recuperare le diverse occorrenze di un modello in una stringa. La classe Pattern è la classe che gestisce le espressioni regolari. Un'espressione regolare utilizzata più di una volta deve essere "compilata". Ciò accelera le ricerche del modello nelle stringhe. Il metodo statico compile svolge questo compito:
Accetta come parametro la stringa del modello e restituisce un oggetto Pattern. Per confrontare il modello di un oggetto Pattern con una stringa di caratteri si utilizza la classe Matcher. Questa classe consente il confronto tra un modello e una stringa di caratteri. Partendo da un oggetto Pattern, è possibile ottenere un oggetto di tipo Matcher con il metodo matcher:
input è la stringa di caratteri che deve essere confrontata con il modello.
Pertanto, per confrontare il modello "\d+" con la stringa " 123 456 789 ", è possibile creare un oggetto Matcher nel modo seguente:
Partendo dall’oggetto résultats precedente, sarà possibile recuperare le diverse occorrenze del modello nella stringa. A tal fine, si utilizzano i metodi suivantes della classe Matcher:
Il metodo find cerca nella stringa analizzata la prima occorrenza del modello. Una seconda chiamata a find cercherà l'occorrenza successiva. E così via. Il metodo restituisce true se trova il modello, altrimenti false. La porzione di stringa corrispondente all'ultima occorrenza trovata da find si ottiene con il metodo group e la sua posizione con il metodo start. Pertanto, se si prosegue con l'esempio precedente e si desidera visualizzare tutte le occorrenze del modello "\d+" nella stringa " 123 456 789 ", si scriverà:
while(résultats.find()){
System.out.println("séquence " + résultats.group() + " trouvée en position " + résultats.start());
}//while
Il metodo reset consente di reimpostare l’oggetto Matcher all’inizio della stringa confrontata con il modello. In questo modo, il metodo find troverà nuovamente la prima occorrenza del modello.
Ecco un esempio completo:
import java.io.*;
import java.util.regex.*;
// gestione delle espressioni regolari
public class regex2 {
public static void main(String[] args){
// più occorrenze del modello nell'esemplare
String modèle2="\\d+";
Pattern regex2=Pattern.compile(modèle2);
String exemplaire3=" 123 456 789";
// ricerca delle occorrenze del modello nell'esemplare
Matcher matcher2=regex2.matcher(exemplaire3);
while(matcher2.find()){
affiche("séquence " + matcher2.group() + " trouvée en position " + matcher2.start());
}//while
}//Main
public static void affiche(String msg){
System.out.println(msg);
}//visualizza
}//classe
Risultati dell'esecuzione:
Modèle=[\d+],exemplaire=[ 123 456 789 ]
Il y a 3 occurrences du modèle dans l'exemplaire
123 en position 2
456 en position 7
789 en position 12
4.11.4. Estrazione di parti di un modello
È possibile "estrarre" sottoinsiemi di un modello. In questo modo, non solo è possibile verificare che una stringa corrisponda a un modello specifico, ma è anche possibile estrarre da tale stringa gli elementi corrispondenti ai sottoinsiemi del modello racchiusi tra parentesi. Pertanto, se si analizza una stringa contenente una data gg/mm/aa e si desidera inoltre estrarre gli elementi gg, mm, aa di tale data, si utilizzerà il modello (\d\d)/(\d\d)/(\d\d).
Esaminiamo il seguente esempio:
import java.io.*;
import java.util.regex.*;
// gestione delle espressioni regolari
public class regex3 {
public static void main(String[] args){
// acquisizione di elementi nel modello
String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
Pattern regex3=Pattern.compile(modèle3);
String exemplaire4="Il est 18:05:49";
// verifica del modello
Matcher résultat=regex3.matcher(exemplaire4);
if (résultat.find()){
// l'istanza corrisponde al modello
affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
// vengono visualizzati i gruppi
for (int i=0;i<=résultat.groupCount();i++){
affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
}//for
}else{
// l'esemplare non corrisponde al modello
affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
}
}//Home
public static void affiche(String msg){
System.out.println(msg);
}//visualizza
}//classifica
L'esecuzione di questo programma produce i seguenti risultati:
L'exemplaire [Il est 18:05:49] correspond au modèle [(\d\d):(\d\d):(\d\d)]
groupes[0]=[18:05:49] en position 7
groupes[1]=[18] en position 7
groupes[2]=[05] en position 10
groupes[3]=[49] en position 13
La novità si trova nella seguente porzione di codice:
// verifica modello
Matcher résultat=regex3.matcher(exemplaire4);
if (résultat.find()){
// l'esemplare corrisponde al modello
affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
// vengono visualizzati i gruppi
for (int i=0;i<=résultat.groupCount();i++){
affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
}//per
}else{
// l'esemplare non corrisponde al modello
affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
}
La stringa exemplaire4 viene confrontata con il modello regex3 tramite il metodo find. Viene quindi individuata un'occorrenza del modello regex3 nella stringa exemplaire4. Se il modello comprende sottoinsiemi racchiusi tra parentesi, questi sono disponibili tramite vari metodi della classe Matcher:
public int groupCount()
public String group(int group)
public int start(int group)
Il metodo groupCount restituisce il numero di sottoinsiemi trovati nel modello e group(i) restituisce il sottoinsieme n. i. Quest'ultimo viene individuato nella stringa in una posizione specificata da start(i). Pertanto, nell’esempio:
L'exemplaire [Il est 18:05:49] correspond au modèle [(\d\d):(\d\d):(\d\d)]
groupes[0]=[18:05:49] en position 7
groupes[1]=[18] en position 7
groupes[2]=[05] en position 10
groupes[3]=[49] en position 13
La prima chiamata al metodo find individuerà la stringa 18:05:49 e creerà automaticamente i tre sottoinsiemi definiti dalle parentesi del modello, rispettivamente 18, 05 e 49.
4.11.5. Un programma di esercitazione
Trovare l’espressione regolare che ci consenta di verificare se una stringa corrisponde a un determinato modello è talvolta una vera sfida. Il programma seguente permette di esercitarsi. Richiede un modello e una stringa e indica quindi se la stringa corrisponde o meno al modello.
import java.io.*;
import java.util.regex.*;
// gestione delle espressioni regolari
public class regex4 {
public static void main(String[] args){
// dati
String modèle=null,chaine=null;
Pattern regex=null;
BufferedReader IN=null;
Matcher résultats=null;
int nbOccurrences=0;
// gestione degli errori
try{
// all'utente vengono richiesti i modelli e gli esemplari da confrontare con questo
while(true){
// flusso di input
IN=new BufferedReader(new InputStreamReader(System.in));
// viene richiesto il modello
System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
modèle=IN.readLine();
// finito?
if(modèle.trim().toLowerCase().equals("fin")) break;
// si crea l'espressione regolare
regex=Pattern.compile(modèle);
// si chiede all’utente quali copie confrontare con il modello
while(true){
System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
chaine=IN.readLine();
// finito?
if(chaine.trim().toLowerCase().equals("fin")) break;
// si crea l'oggetto matcher
résultats=regex.matcher(chaine);
// si cercano le occorrenze del modello
nbOccurrences=0;
while(résultats.find()){
// si trova un'occorrenza
nbOccurrences++;
// la si visualizza
System.out.println("J'ai trouvé la correspondance ["+résultats.group()
+"] en position "+résultats.start());
// visualizzazione dei sottoelementi
if(résultats.groupCount()!=1){
for(int j=1;j<=résultats.groupCount();j++){
System.out.println("\tsous-élément ["+résultats.group(j)+"] en position "+
résultats.start(j));
}//for j
}//if
// stringa successiva
}//while(résultats.find())
// è stata trovata almeno un'occorrenza?
if(nbOccurrences==0){
System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
}//if
// modello successivo
}//while(true)
}//while(true)
}catch(Exception ex){
// errore
System.err.println("Erreur : "+ex.getMessage());
// fine con errore
System.exit(1);
}//try-catch
// fine
System.exit(0);
}//Main
}//classe
Ecco un esempio di esecuzione:
Tapez le modèle à tester ou fin pour arrêter :\d+
Tapez la chaîne à comparer au modèle [\d+] ou fin pour arrêter :123 456 789
J'ai trouvé la correspondance [123] en position 0
J'ai trouvé la correspondance [456] en position 4
J'ai trouvé la correspondance [789] en position 8
Tapez la chaîne à comparer au modèle [\d+] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :(\d\d):(\d\d)
Tapez la chaîne à comparer au modèle [(\d\d):(\d\d)] ou fin pour arrêter :14:15
abcd 17:18 xyzt
J'ai trouvé la correspondance [14:15] en position 0
sous-élément [14] en position 0
sous-élément [15] en position 3
J'ai trouvé la correspondance [17:18] en position 11
sous-élément [17] en position 11
sous-élément [18] en position 14
Tapez la chaîne à comparer au modèle [(\d\d):(\d\d)] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :^\s*\d+\s*$
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou fin pour arrêter : 1456
J'ai trouvé la correspondance [ 1456] en position 0
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :^\s*(\d+)\s*$
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :1456
J'ai trouvé la correspondance [1456] en position 0
sous-élément [1456] en position 0
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :abcd 1
456
Je n'ai pas trouvé de correspondances
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :fin
4.11.6. Il metodo split della classe Pattern
Consideriamo una stringa composta da campi separati da una stringa di separazione espressa tramite un'espressione regolare. Ad esempio, se i campi sono separati dal carattere , preceduto o seguito da un numero qualsiasi di spazi, l'espressione regolare che modella la stringa di separazione dei campi sarebbe "\s*,\s*". Il metodo split della classe Pattern ci permette di recuperare i campi in un array:
public String[] split(CharSequence input)
La stringa input viene scomposta in campi, separati da un separatore corrispondente al modello dell'oggetto Pattern corrente. Per recuperare i campi di una riga il cui separatore di campi è la virgola preceduta o seguita da un numero qualsiasi di spazi, si scriverà:
// una riga
String ligne="abc ,, def , ghi";
// un modello
Pattern modèle=Pattern.compile("\\s*,\\s*");
// scomposizione della riga in campi
String[] champs=modèle.split(ligne);
È possibile ottenere lo stesso risultato con il metodo split della classe String:
public String[] split(String regex)
Ecco un programma di prova:
import java.io.*;
import java.util.regex.*;
// gestione delle espressioni regolari
public class split1 {
public static void main(String[] args){
// una riga
String ligne="abc ,, def , ghi";
// un modello
Pattern modèle=Pattern.compile("\\s*,\\s*");
// scomposizione di una riga in campi
String[] champs=modèle.split(ligne);
// visualizzazione
for(int i=0;i<champs.length;i++){
System.out.println("champs["+i+"]=["+champs[i]+"]");
}//for
// un altro modo di procedere
champs=ligne.split("\\s*,\\s*");
// visualizzazione
for(int i=0;i<champs.length;i++){
System.out.println("champs["+i+"]=["+champs[i]+"]");
}//for
}//Home
}//classe
Risultati dell'esecuzione:
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
4.12. Esercizi
4.12.1. Esercizio 1
In Unix, i programmi vengono spesso richiamati nel modo seguente:
$ pg -o1 v1 v2 ... -o2 v3 v4 …
dove -oi rappresenta un'opzione e vi un valore associato a tale opzione. Si desidera creare una classe "options" che consenta di analizzare la stringa di argomenti -o1 v1 v2 ... -o2 v3 v4 … al fine di costruire le seguenti entità:
optionsValides | dizionario (Hashtable) le cui chiavi sono le opzioni oi valide. Il valore associato alla chiave oi è un vettore (Vector) i cui elementi sono i valori v1 v2 … associati all’opzione -oi |
optionsInvalides | dizionario (Hashtable) le cui chiavi sono le opzioni oi non valide. Il valore associato alla chiave oi è un vettore (Vector) i cui elementi sono i valori v1 v2 … associati all’opzione -oi |
optionsSans | stringa (String) che fornisce l’elenco dei valori vi non associati a un’opzione |
erreur | numero intero pari a 0 se non ci sono errori nella riga degli argomenti, altrimenti un valore diverso: 1: sono presenti parametri di chiamata non validi 2: sono presenti opzioni non valide 4: sono presenti valori non associati ad alcuna opzione Se sono presenti più tipi di errori, questi valori si sommano. |
Un oggetto «options» può essere creato in 4 modi diversi:
public options (String arguments, String optionsAcceptables)
arguments | la riga di argomenti -o1 v1 v2 ... -o2 v3 v4 … da analizzare |
optionsAcceptables | l'elenco delle opzioni oi accettabili |
Esempio di chiamata: options opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");
In questo caso, entrambi gli argomenti sono stringhe di caratteri. Saranno accettati i casi in cui tali stringhe siano state suddivise in parole inserite in un array di stringhe di caratteri. Ciò richiede altri tre costruttori:
public options (String[] arguments, String optionsAcceptables)
public options (String arguments, String[] optionsAcceptables)
public options (String[] arguments, String[] optionsAcceptables)
La classe "options" presenterà la seguente interfaccia (metodi di accesso):
restituisce il riferimento alla tabella optionsValides creata al momento della creazione dell'oggetto "options"
restituisce il riferimento all'array optionsInvalides creato al momento della creazione dell'oggetto "options"
restituisce il riferimento alla stringa optionsSans creata al momento della creazione dell'oggetto "opzioni"
restituisce il valore dell'attributo "errore" generato al momento della creazione dell'oggetto "opzioni"
se non ci sono errori, visualizza i valori degli attributi optionsValides, optionsInvalides, optionsSans, altrimenti visualizza il numero dell'errore.
Ecco un programma di esempio:
import java.io.*;
//opzioni di importazione;
public class test1{
public static void main (String[] arg){
// apertura del flusso di input
String ligne;
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
affiche(e);
System.exit(1);
}
// lettura degli argomenti del costruttore options(String, string)
String options=null;
String optionsAcceptables=null;
while(true){
System.out.print("Options : ");
try{
options=IN.readLine();
} catch (Exception e){
affiche(e);
System.exit(2);
}
if(options.length()==0) break;
System.out.print("Options acceptables: ");
try{
optionsAcceptables=IN.readLine();
} catch (Exception e){
affiche(e);
System.exit(2);
}
System.out.println(new options(options,optionsAcceptables));
}// fine while
}//fine main
public static void affiche(Exception e){
System.err.println("Erreur : "+e);
}
}//fine classe
Alcuni risultati:
C:\Serge\java\options>java test1
Options : 1 2 3 -a a1 a2 -b b1 -c c1 c2 c3 -b b2 b3
Options acceptables: -a -b
Erreur 6
Options valides :(-b,b1,b2,b3) (-a,a1,a2)
Options invalides : (-c,c1,c2,c3)
Sans options : 1 2 3
4.12.2. Esercizio 2
Si desidera creare una classe stringtovector che consenta di trasferire il contenuto di un oggetto String in un oggetto Vector. Questa classe deriverebbe dalla classe Vector:
e avrebbe il codice sorgente seguente:
private void stringtovector(String S, String separateur, int[] tChampsVoulus,
boolean strict){
// crea un vettore con i campi della stringa S
// la stringa è costituita da campi separati dal separatore
// se separatore=null, la stringa forma un unico campo
// sono richiesti solo i campi i cui indici si trovano nell'array tChampsVoulus
// sono richiesti. Gli indici partono da 1
// se tChampsvoulus è nullo o di dimensione nulla, vengono presi tutti i campi
// se strict=vero, tutti i campi richiesti devono essere presenti
La classe avrebbe il seguente attributo privato:
Questo attributo viene impostato dal costruttore precedente con i seguenti valori:
0: la creazione è andata a buon fine
4: alcuni campi richiesti sono assenti nonostante strict=true
La classe avrà inoltre due metodi:
che restituisce il valore dell'attributo privato erreur.
che visualizza il valore dell’oggetto nella forma (errore, elemento 1, elemento 2, …) dove gli elementi i sono gli elementi del vettore costruito a partire dalla stringa.
Un programma di test potrebbe essere il seguente:
import java.io.*;
//import stringtovector;
public class essai2{
public static void main(String arg[]){
int[] T1={1,3};
System.out.println(new stringtovector("a : b : c :d:e",":",T1,true).identite());
int[] T2={1,3,7};
System.out.println(new stringtovector("a : b : c :d:e",":",T2,true).identite());
int [] T3={1,4,7};
System.out.println(new stringtovector("a : b : c :d:e",":",T3,false).identite());
System.out.println(new stringtovector("a : b : c :d:e","",T1,false).identite());
System.out.println(new stringtovector("a : b : c :d:e",null,T1,false).identite());
int[] T4={1};
System.out.println(new stringtovector("a : b : c :d:e","!",T4,true).identite());
int[] T5=null;
System.out.println(new stringtovector("a : b : c :d:e",":",T5,true).identite());
System.out.println(new stringtovector("a : b : c :d:e",null,T5,true).identite());
int[] T6=new int[0];
System.out.println(new stringtovector("a : b : c :d:e","",T6,true).identite());
int[] T7={1,3,4};
System.out.println(new stringtovector("a b c d e"," ",T6,true).identite());
}
}
I risultati:
(0,a,c)
(4,a,c)
(0,a,d)
(0,a : b : c :d:e)
(0,a : b : c :d:e)
(0,a : b : c :d:e)
(0,a,b,c,d,e)
(0,a : b : c :d:e)
(0,a : b : c :d:e)
(0,a,b,c,d,e)
Alcuni consigli:
- Per suddividere la stringa S in campi, utilizzare il metodo split della classe String.
- Inserire i campi di S in un dizionario D indicizzato in base al numero del campo
- Recuperare dal dizionario D solo i campi la cui chiave (indice) è presente nella tabella tChampsVoulus.
4.12.3. Esercizio 3
Si desidera aggiungere alla classe stringtovector il seguente costruttore:
public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
// crea un vettore con i campi della stringa S
// questa è costituita da campi separati dal separatore
// se separatore=null, la stringa forma un unico campo
// sono richiesti solo i campi i cui indici sono presenti in sChampsVoulus
// gli indici partono da 1
// se sChampsvoulus è nullo o "", vengono presi tutti i campi
// se strict=vero, tutti i campi richiesti devono essere presenti
L'elenco dei campi desiderati è quindi contenuto in una stringa (String) anziché in un array di interi (int[]). All'attributo privato erreur della classe può essere assegnato un nuovo valore:
2: la stringa degli indici dei campi desiderati non è corretta
Ecco un programma di esempio:
import java.io.*;
//import stringtovector;
public class essai1{
public static void main(String arg[]){
String champs=null;
System.out.println(new stringtovector("a: b :c :d:e ",":","1 3",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","1 3 7",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","1 4 7",false).identite());
System.out.println(new stringtovector("a: b :c :d:e ","","1 3",false).identite());
System.out.println(new stringtovector("a: b :c :d:e ",null,"1 3",false).identite());
System.out.println(new stringtovector("a: b :c :d:e ","!","1",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":",champs,true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",null,champs,true).identite());
System.out.println(new stringtovector("a: b :c :d:e ","","",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","1 !",true).identite());
System.out.println(new stringtovector("a b c d e "," ","1 3",false).identite());
}
}
Alcuni risultati:
(0,a,c)
(4,a,c)
(0,a,d)
(0,a: b :c :d:e)
(0,a: b :c :d:e)
(0,a: b :c :d:e)
(0,a,b,c,d,e)
(0,a,b,c,d,e)
(0,a: b :c :d:e)
(0,a: b :c :d:e)
(2)
(0,a,c)
Alcuni consigli:
- È necessario fare riferimento al caso del costruttore precedente, trasferendo i campi della stringa sChampsVoulus in un array di interi. A tal fine, suddividere sChampsVoulus in campi utilizzando un oggetto StringTokenizer, il cui attributo countTokens fornirà il numero di campi ottenuti. È quindi possibile creare un array di interi della dimensione corretta e riempirlo con i campi ottenuti.
- Per verificare se un campo è un numero intero, utilizzare il metodo Integer.parseInt per convertire il campo in un numero intero e gestire l’eccezione che verrà generata qualora tale conversione risulti impossibile.
4.12.4. Esercizio 4
Si desidera creare una classe filetovector che consenta di trasferire il contenuto di un file di testo in un oggetto *Vector. Questa classe deriverebbe dalla classe *Vector:
e avrebbe il seguente costruttore:
// --------------------- costruttore
public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
// crea un vettore con le righe del file di testo nomFichier
// le righe sono costituite da campi separati dal separatore
// se separatore=null, la riga forma un unico campo
// sono richiesti solo i campi i cui indici sono presenti in tChampsVoulus
// gli indici partono da 1
// se tChampsvoulus è nullo o vuoto, vengono presi tutti i campi
// se strict=vero, tutti i campi richiesti devono essere presenti
// in caso contrario, la riga non viene memorizzata e il suo indice
// viene inserito nel vettore lignesErronees
// le righe vuote vengono ignorate
// così come le righe che iniziano con tagCommentaire se tagCommentaire != null
La classe avrebbe i seguenti attributi privati:
L'attributo erreur viene impostato dal costruttore precedente con i seguenti valori:
0: la creazione è andata a buon fine
1: non è stato possibile aprire il file da elaborare
4: alcuni campi richiesti sono assenti nonostante strict=true
8: si è verificato un errore di I/O durante l'elaborazione del file
L'attributo lignesErronees è un vettore i cui elementi sono i numeri delle righe errate sotto forma di stringa di caratteri. Una riga è errata se non è in grado di fornire i campi richiesti mentre strict=true.
La classe disporrà inoltre di due metodi:
che restituisce il valore dell'attributo privato erreur.
che visualizza il valore dell'oggetto nella forma (errore, elemento 1, elemento 2 …,(l1,l2,…)), dove gli elementi i sono gli elementi del vettore costruito a partire dal file e li sono i numeri delle righe errate.
Ecco un esempio di test:
import java.io.*;
//import filetovector;
public class test2{
public static void main(String arg[]){
int[] T1={1,3};
System.out.println(new filetovector("data.txt",":",T1,false,"#").identite());
System.out.println(new filetovector("data.txt",":",T1,true,"#").identite());
System.out.println(new filetovector("data.txt","",T1,false,"#").identite());
System.out.println(new filetovector("data.txt",null,T1,false,"#").identite());
int[] T2=null;
System.out.println(new filetovector("data.txt",":",T2,false,"#").identite());
System.out.println(new filetovector("data.txt",":",T2,false,"").identite());
int[] T3=new int[0];
System.out.println(new filetovector("data.txt",":",T3,false,null).identite());
}
}
Risultati dell'esecuzione:
[0,(0,a,c) (0,1,3) (0,azerty,cvf) (0,s)]
[4,(0,a,c) (0,1,3) (0,azerty,cvf),[5]]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# commento) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# commento) (0,azerty,1,cvf,fff,qqqq) (0,s)]
Alcuni consigli
-
Il file di testo viene elaborato riga per riga. La riga viene suddivisa in campi grazie alla classe stringtovector vista in precedenza.
-
Gli elementi del vettore creato a partire dal file di testo sono quindi oggetti di tipo stringtovector.
-
Il metodo identite di filetovector potrà avvalersi del metodo stringtovector.identite() per visualizzare i propri elementi, nonché del metodo Vector.toString() per visualizzare i numeri delle eventuali righe errate.
4.12.5. Esercizio 5
Si desidera aggiungere alla classe **filetovector** il seguente costruttore:
public filetovector(String nomFichier, String separateur, String sChampsVoulus,
boolean strict, String tagCommentaire){
// crea un vettore con le righe del file di testo nomFichier
// le righe sono costituite da campi separati dal separatore
// se separatore=null, la riga costituisce un unico campo
// sono richiesti solo i campi i cui indici sono presenti in tChampsVoulus
// gli indici iniziano da 1
// se sChampsvoulus è nullo o vuoto, vengono presi tutti i campi
// se strict=vero, tutti i campi richiesti devono essere presenti
// in caso contrario, la riga non viene memorizzata e il suo indice
// viene inserito nel vettore lignesErronees
// le righe vuote vengono ignorate
// così come le righe che iniziano con tagCommentaire se tagCommentaire != null
L'elenco degli indici dei campi desiderati è ora contenuto in una stringa di caratteri (String) anziché in un array di numeri interi.
L'attributo privato erreur può assumere un ulteriore valore:
2: la stringa degli indici dei campi desiderati non è corretta
Ecco un esempio di test:
import java.io.*;
//import filetovector;
public class test1{
public static void main(String arg[]){
System.out.println(new filetovector("data.txt",":","1 3",false,"#").identite());
System.out.println(new filetovector("data.txt",":","1 3",true,"#").identite());
System.out.println(new filetovector("data.txt","","1 3",false,"#").identite());
System.out.println(new filetovector("data.txt",null," 1 3",false,"#").identite());
String S2=null;
System.out.println(new filetovector("data.txt",":",S2,false,"#").identite());
System.out.println(new filetovector("data.txt",":",S2,false,"").identite());
String S3="";
System.out.println(new filetovector("data.txt",":",S3,false,null).identite());
}
}
I risultati:
[0,(0,a,c) (0,1,3) (0,azerty,cvf) (0,s)][4,(0,a,c) (0,1,3) (0,azerty,cvf),[5]]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# commento) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# commento) (0,azerty,1,cvf,fff,qqqq) (0,s)]
Alcuni consigli
- Trasformeremo la stringa sChampsVoulus in una tabella di numeri interi tChampVoulus per tornare al caso del costruttore precedente.