Skip to content

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:

Image

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:

Image

Image

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:

Image

Seguiamo il link Java 2 Platform API:

Image

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:

Image

e cliccare sul link per visualizzare la definizione della classe:

Image

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:

    int compare(Object o1, Object o2)

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:

     // ordinamento della tabella delle persone
    Arrays.sort(amis,this);

Ciò ci obbliga a fare due cose:

  1. indicare che la classe sort2 implementa l’interfaccia Comparator
public class sort2 implements Comparator{
  1. 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:

[tournesol,40]
[milou,80]
[tintin,100]

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:

public class sort2 {

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:

     // ricerca
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);

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:

Jean,Dupont,27
Pauline,Garcia,24
Gilles,Dumond,56

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:

personne(Jean,Dupont,27)
personne(Pauline,Garcia,24)
personne(Gilles,Dumond,56)

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:

import 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.
\n
Corrisponde a n, dove n è un valore di escape ottale. I valori di escape ottali devono essere composti da 1, 2 o 3 cifre. Ad esempio, sia "\11" che "\011" corrispondono a un carattere di tabulazione. "\0011" equivale a "\001" e "1". I valori di escape ottali non devono superare 256. In caso contrario, nell'espressione verrebbero prese in considerazione solo le prime due cifre. Consente di utilizzare i codici ASCII nelle espressioni regolari.
\xn
Corrisponde a n, dove n è un valore di escape esadecimale. I valori di escape esadecimali devono obbligatoriamente comprendere due cifre. Ad esempio, "\x41" corrisponde a "A". "\x041" equivale a "\x04" e "1". Consente di utilizzare i codici ASCII nelle espressioni regolari.

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

boolean Matches(String modèle, String chaine)

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:

[  123  ] correspond au modèle [^\s*\d+\s*$]
[  123a  ] ne correspond pas au modèle [^\s*\d+\s*$]

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:

public static Pattern compile(String regex)

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:

public Matcher matcher(CharSequence input)

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:

Pattern regex=Pattern.compile("\\d+");
Matcher résultats=regex.matcher("  123  456  789  ");

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:

public boolean find()
public String group()
public int start()
public Matcher reset()

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):

public Hashtable getOptionsValides()

restituisce il riferimento alla tabella optionsValides creata al momento della creazione dell'oggetto "options"

public Hashtable getOptionsInvalides()

restituisce il riferimento all'array optionsInvalides creato al momento della creazione dell'oggetto "options"

public String getOptionsSans()

restituisce il riferimento alla stringa optionsSans creata al momento della creazione dell'oggetto "opzioni"

public int getErreur()

restituisce il valore dell'attributo "errore" generato al momento della creazione dell'oggetto "opzioni"

public String toString()

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:

class stringtovector extends 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:

        private int erreur;

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:

    public int getErreur()

che restituisce il valore dell'attributo privato erreur.

    public String identite(){

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:

  1. Per suddividere la stringa S in campi, utilizzare il metodo split della classe String.
  2. Inserire i campi di S in un dizionario D indicizzato in base al numero del campo
  3. 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:

  1. È 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.
  2. 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:

class filetovector extends 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:

        private int erreur=0;
        private Vector lignesErronees=null;

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:

    public int getErreur()

che restituisce il valore dell'attributo privato erreur.

    public String identite(){

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

  1. Il file di testo viene elaborato riga per riga. La riga viene suddivisa in campi grazie alla classe stringtovector vista in precedenza.

  2. Gli elementi del vettore creato a partire dal file di testo sono quindi oggetti di tipo stringtovector.

  3. 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

  1. Trasformeremo la stringa sChampsVoulus in una tabella di numeri interi tChampVoulus per tornare al caso del costruttore precedente.