Skip to content

4. Classi di uso comune in Java

In questo capitolo presentiamo una serie di classi Java comunemente utilizzate. Queste dispongono di numerosi attributi, metodi e costruttori. In ogni caso, presentiamo solo una piccola parte delle classi. I dettagli su 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 JDK ma non della documentazione. Questa è disponibile sul sito web di Sun all'indirizzo http://www.sun.com. Nella cartella docs troverete un file index.html, che funge da punto di partenza per la guida del JDK:

Image

Image

Il link "API & Language" sopra riportato 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 API della piattaforma Java 2:

Image

Questa pagina è il vero punto di partenza per la documentazione delle classi. È possibile creare un collegamento per accedervi rapidamente. L'URL è <jdk>\docs\api\index.html. Contiene collegamenti a centinaia di classi Java presenti nel JDK. Quando si è alle prime armi, la sfida principale è capire a cosa servono queste diverse classi. Inizialmente, questa risorsa è utile solo se si conosce il nome della classe su cui si desiderano informazioni. Puoi anche usare i nomi delle classi come guida, poiché di solito indicano lo scopo della classe.

Facciamo un esempio e cerchiamo informazioni sulla classe Vector, che implementa un array dinamico. Basta cercare il link alla classe Vector nell'elenco delle classi nel riquadro di sinistra:

Image

e clicca sul link per visualizzare la definizione della classe:

Image

Lì troverai

  • 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

Nelle sezioni seguenti presenteremo diverse classi. Invitiamo il lettore a verificare sistematicamente la definizione completa delle classi utilizzate.

4.2. Classi di test

Gli esempi seguenti utilizzano talvolta le classi Person e Teacher. Ne forniamo qui le definizioni.

public class personne{
    // last name, first name, age
  private String prenom;
  private String nom;
  private int age;

  // builder 1
  public personne(String P, String N, int age){
    this.prenom=P;
    this.nom=N;
    this.age=age;
  }

   // builder 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+")";
  }

  // accessors
  public String getPrenom(){
    return prenom;
  }
  public String getNom(){
    return nom;
  }
  public int getAge(){
    return age;
  }

  //modifiers
  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 Teacher deriva dalla classe Person ed è definita come segue:

class enseignant extends personne{
// attributes
  private int section;

    // manufacturer
  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 anche una classe studente derivata dalla classe persona 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 *name* sia una variabile di tipo stringa:

*String name;*

name è un riferimento a un oggetto che non è stato ancora inizializzato. Può essere inizializzato in due modi:

**name = &quot;horse&quot;** oppure **name = new String(&quot;horse&quot;)**

Entrambi i metodi sono equivalenti. Se in seguito scriviamo name=&quot;fish&quot;, name farà riferimento a un nuovo oggetto. Il vecchio oggetto String(&quot;horse&quot;) andrà perso e la memoria che occupava verrà recuperata.

La classe String ha molti attributi e metodi. Eccone alcuni:

public char charAt(int i)
restituisce il carattere all'indice i nella stringa, dove il primo carattere ha indice 0. Pertanto, `String("horse").charAt(3)` è uguale a 'h'
public int compareTo(string2)
string1.compareTo(string2) confronta string1 con string2 e restituisce 0 se string1 = string2, 1 se string1 > string2 e -1 se string1 < string2
public boolean equals(Object anObject)
string1.equals(string2) restituisce true se string1 = string2, false in caso contrario
public String toLowerCase()
string1.toLowerCase() converte string1 in minuscolo
public String toUpperCase()
string1.toUpperCase() converte string1 in maiuscolo
public String trim()
string1.trim() rimuove gli spazi iniziali e finali da string1
public String substring(int beginIndex, int endIndex)
String("chapeau").subString(2,4) restituisce la stringa "ape"
public char[] toCharArray()
converte i caratteri della stringa in un array di caratteri
int length()
numero di caratteri nella stringa
int indexOf(String string2)
restituisce la prima occorrenza di string2 nella stringa corrente, oppure -1 se string2 non è presente
int indexOf(String string2, int startIndex)
Restituisce la prima occorrenza di string2 nella stringa corrente, oppure -1 se string2 non viene trovata. La ricerca inizia dal carattere alla posizione startIndex.
int lastIndexOf(String string2)
Restituisce l'ultima posizione di string2 nella stringa corrente, oppure -1 se string2 non è presente
boolean startsWith(String string2)
restituisce true se la stringa corrente inizia con string2
boolean endsWith(String string2)
restituisce true se la stringa corrente termina con string2
boolean matches(String regex)
restituisce true se la stringa corrente corrisponde all'espressione regolare regex.
String[] split(String regex)
La stringa corrente è costituita da campi separati da una stringa di caratteri corrispondente all'espressione regolare regex. Il metodo split recupera i campi in un array.
String replace(char oldChar, char newChar)
Sostituisce il carattere oldChar con il carattere newChar nella stringa corrente.

Ecco un programma di esempio:

// imports
import java.io.*;

public class string1{
    // a demonstration class
    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]+"]");
        }//for
        affiche("(\"  abc  \").trim()=["+"  abc  ".trim()+"]");
    }//Main

  // poster
    public static void affiche(String msg){
        // poster msg
        System.out.println(msg);
    }//poster
}//class

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 impossibile 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 nel vettore
public final void addElement(Object obj)
aggiunge al vettore l'oggetto a cui fa riferimento obj
public final Object elementAt(int index)
riferimento all'oggetto all'indice nell'array - gli indici partono da 0
public final Enumeration elements()
l'insieme degli elementi nell'array come enumerazione
public final Object firstElement()
riferimento al primo elemento dell'array
public final Object lastElement()
riferimento all'ultimo elemento del vettore
public final boolean isEmpty()
restituisce true se l'array è vuoto
public final void removeElementAt(int index)
rimuove l'elemento all'indice
public final void removeAllElements()
cancella tutti gli elementi dall'array
public final String toString()
restituisce una stringa che rappresenta l'array

Ecco un programma di prova:


// imported classes
import java.util.*;
 
public class test1{
 
// main program - static - class method
 
  public static void main(String arg[]){
 
// creation of objects that are instances of classes
    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());
 
// polymorphism
    personne p2=(personne)en;
    System.out.println("p2="+p2.toString());
    personne p3=(personne)et;
    System.out.println("p3="+p3.toString());
 
// a vector
    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 hand
}// end of class

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)

D'ora in poi, non ripeteremo più il processo di compilazione ed esecuzione dei programmi di test. Basta ripetere ciò che è stato fatto sopra.

4.5. La classe ArrayList

La classe ArrayList è analoga alla classe Vector. Si differenzia da quest'ultima principalmente solo quando viene utilizzata contemporaneamente da più thread. I metodi di sincronizzazione dei thread per l'accesso a un Vector o a un ArrayList sono diversi. Al di fuori di questo caso, entrambe le classi possono essere utilizzate in modo intercambiabile. Di seguito sono riportati alcuni campi, costruttori o metodi di questa classe:

ArrayList()
crea un array vuoto
int size()
numero di elementi nell'array
void add(Object obj)
aggiunge all'array l'oggetto a cui fa riferimento obj
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 all'indice nell'array — gli indici partono da 0
boolean isEmpty()
restituisce true se l'array è vuoto
void remove(int index)
rimuove l'elemento all'indice
void clear()
cancella tutti gli elementi dall'array
Object[] toArray()
converte l'array dinamico in un array standard
String toString()
restituisce una stringa che rappresenta l'array

Ecco un programma di prova:

// imported classes
import java.util.*;

public class test1{

// main program - static - class method

  public static void main(String arg[]){

// creation of objects that are instances of classes
    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);

// polymorphism
    personne p2=(personne)en;
    System.out.println("p2="+p2);
    personne p3=(personne)et;
    System.out.println("p3="+p3);

// a vector
    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 hand
}// end of class

I risultati ottenuti sono gli stessi di prima.

4.6. La classe Arrays

La classe java.util.Arrays offre l'accesso a metodi statici che consentono di eseguire diverse operazioni sugli array, in particolare l'ordinamento e la ricerca di elementi. Ecco alcuni di questi metodi:

static void sort(array)
ordina l'array utilizzando l'ordine implicito del tipo di dati dell'array, che si tratti di numeri o stringhe.
static void sort(Object[] array, Comparator C)
Ordina l'array utilizzando la funzione di confronto C per confrontare gli elementi
static int binarySearch(array, element)
Restituisce la posizione dell'elemento nell'array, oppure un valore <0 in caso contrario. L'array deve essere stato ordinato in precedenza.
static int binarySearch(Object[] array, Object element, Comparator C)
Come sopra, ma utilizza la funzione di confronto C per confrontare due elementi nell'array.

Ecco un primo esempio:

import java.util.*;

public class sort2 implements Comparator{

   // an internal private class
  private class personne{
    private String nom;
    private int age;
    public personne(String nom, int age){
      this.nom=nom; // person's name
      this.age=age; // his age
    }
     // recover age
    public int getAge(){
      return age;
    }
     // identity of the person
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // class person

   // manufacturer
  public sort2() {
     // a table of people
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // sorting the people table
    Arrays.sort(amis,this);
     // check
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//manufacturer

   // the function that compares people
  public int compare(Object o1, Object o2){
    // must make
     // -1 if o1 "smaller than" o2
     // 0 if o1 "equal to" o2
     // +1 if o1 "greater than" 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;
  }//compare

    // test function
  public static void main(String[] arg){
    new sort2();
  }//hand

}//class

Esaminiamo questo programma. La funzione main crea un oggetto sort2. Il costruttore della classe sort2 è il seguente:

   // manufacturer
  public sort2() {
     // a table of people
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // sorting the people table
    Arrays.sort(amis,this);
     // check
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//manufacturer

L'array da ordinare è un array di oggetti Person. La classe Person è definita come privata all'interno della classe sort2. Il metodo statico sort della classe Arrays non sa come ordinare un array di oggetti Person, quindi siamo costretti qui a 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 implementa l'interfaccia Comparator. Nel costruttore di sort2, abbiamo scelto l'oggetto corrente this:

     // sorting the people table
    Arrays.sort(amis,this);

Questo ci impone di fare due cose:

  1. indicare che la classe sort2 implementa l'interfaccia Comparator
public class sort2 implements Comparator{
  1. scrivere la funzione di confronto nella classe sort2.

Ecco come:

   // the function that compares people
  public int compare(Object o1, Object o2){
    // must make
     // -1 if o1 "smaller than" o2
     // 0 if o1 "equal to" o2
     // +1 if o1 "greater than" 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;
  }//compare

Per confrontare due oggetti Person, qui usiamo l'età (avremmo potuto usare il nome).

I risultati dell'esecuzione sono i seguenti:

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

Avremmo potuto adottare un approccio diverso per l'implementazione dell'interfaccia Comparator:

import java.util.*;

public class sort2 {

  // an internal private class
  private class personne{
…….
  }; // class person

   // manufacturer
  public sort2() {
     // a table of people
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // sorting the people table
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//compare
      }//class
    );
     // check
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//manufacturer

   // the function that compares people
  public int compare1(Object o1, Object o2){
    // must make
     // -1 if o1 "smaller than" o2
     // 0 if o1 "equal to" o2
     // +1 if o1 "greater than" 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

  // hand
  public static void main(String[] arg){
    new sort2();
  }//hand
}//class

L'istruzione di ordinamento è diventata la seguente:

     // sorting the people table
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//compare
      }//class
    );

Il secondo parametro del metodo compare deve essere un oggetto che implementi l'interfaccia Comparator. In questo caso, creiamo un oggetto di questo tipo utilizzando new java.util.Comparator(), e il testo che segue {…} definisce la classe da cui viene creato l'oggetto. Si tratta di una classe anonima, poiché non ha un nome. In questa classe anonima, che deve implementare l'interfaccia Comparator, definiamo il metodo compare di tale interfaccia. Questo metodo chiama semplicemente il metodo compare1 della classe sort2. Ci ritroviamo quindi al caso 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 utilizzando il seguente esempio:

import java.util.*;

public class sort4 {

  // an internal private class
  private class personne{
      // attributes
    private String nom;
    private int age;

        // manufacturer
    public personne(String nom, int age){
      this.nom=nom; // person's name
      this.age=age; // his age
    }

     // retrieve name
    public String getNom(){
      return nom;
    }

     // recover age
    public int getAge(){
      return age;
    }
     // identity of the person
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // class person

   // manufacturer
  public sort4() {
     // a table of people
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};

     // comparators
    java.util.Comparator comparateur1=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//compare
      }//class
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//compare
      }//class
      ;          

     // sorting the people table
    Arrays.sort(amis,comparateur1);
    // check
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
    // research
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);
  }//manufacturer

   // the function that compares people
  public int compare1(Object o1, Object o2){
    // must make
     // -1 if o1 "smaller than" o2
     // 0 if o1 "equal to" o2
     // +1 if o1 "greater than" 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

   // the function that compares a person to a name
  public int compare2(Object o1, Object o2){
    // o1 is a person
     // o2 is a String, the name name2 of a person
     // must make
     // -1 if o1.nom "smaller than" name2
     // 0 if o1.nom "equal to" name2
     // +1 if o1.nom "greater than" name2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//compare2

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // search for a friend in the friends table
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // found?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//search

  // hand
  public static void main(String[] arg){
    new sort4();
  }//hand
}//class

Qui abbiamo proceduto in modo leggermente diverso rispetto agli esempi precedenti. I due oggetti Comparator richiesti dai metodi sort e binarySearch sono stati creati e assegnati alle variabili comparator1 e comparator2.

     // comparators
    java.util.Comparator comparateur1=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//compare
      }//class
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//compare
      }//class
      ;          

Nel costruttore sort4 viene eseguita due volte una ricerca binaria sull'array friends:

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

Il metodo search riceve tutti i parametri necessari per chiamare il metodo binarySearch:

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // search for a friend in the friends table
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // found?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//search

Il metodo binarySearch funziona con il comparatore comparator2, che a sua volta chiama il metodo compare2 della classe sort4. Il metodo restituisce la posizione del nome cercato nell'array se esiste, oppure un numero <0 in caso contrario. Il metodo compare2 viene utilizzato per confrontare un oggetto Person con un nome di tipo String.

   // the function that compares a person to a name
  public int compare2(Object o1, Object o2){
    // o1 is a person
     // o2 is a String, the name name2 of a person
     // must make
     // -1 if o1.nom "smaller than" name2
     // 0 if o1.nom "equal to" name2
     // +1 if o1.nom "greater than" name2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//compare2

A differenza del metodo sort, il metodo binarySearch non accetta due oggetti Person, bensì un oggetto Person e un oggetto String in quest'ordine. Il primo parametro è un elemento dell'array friends, mentre il secondo è il nome della persona che si sta cercando.

4.7. La classe Enumeration

Enumeration è un'interfaccia, non una classe. Dispone dei seguenti metodi:

public abstract boolean hasMoreElements()
restituisce true se l'enumerazione contiene ancora elementi
public abstract Object nextElement()
restituisce un riferimento all'elemento successivo nell'enumerazione

Come si usa un'enumerazione? Generalmente in questo modo:

    Enumeration e=                // an enumeration object is retrieved
    while(e.hasMoreElements()){
        // use e.nextElement() element
    }

Ecco un esempio:


// imported classes
import java.util.*;
 
public class test1{
 
// main program - static - class method
 
  public static void main(String arg[]){
 
// creation of objects that are instances of classes
    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());
 
// polymorphism
    personne p2=(personne)en;
    System.out.println("p2="+p2.toString());
    personne p3=(personne)et;
    System.out.println("p3="+p3.toString());
 
// a vector
    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());
    }
 
// an enumeration
    Enumeration E=V.elements();
    i=0;
    while(E.hasMoreElements()){
      p2=(personne) E.nextElement();
      System.out.println("V["+i+"]="+p2.toString());
      i++;
    }
   }// fine hand
}//end of class

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. Un dizionario può essere visto come un array a due colonne:

chiave
valore
chiave1
valore1
chiave2
valore2
..
...

Le chiavi sono univoche, ovvero 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 chiave, Object valore)
aggiunge la coppia (chiave, valore) al dizionario
public Object get(Object key)
recupera l'oggetto associato alla chiave key oppure restituisce null se la chiave key non esiste
public boolean containsKey(Object key)
true se la chiave esiste nel dizionario
public boolean contains(Object value)
true se il valore value esiste nel dizionario
public Enumeration keys()
restituisce le chiavi del dizionario come enumerazione
public Object remove(Object key)
rimuove la coppia (chiave, valore) dove chiave=chiave
public String toString()
identifica il dizionario

Ecco un esempio:


// imported classes
import java.util.*;
 
public class test1{
 
// main program - static - class method
 
  public static void main(String arg[]){
 
// creation of objects that are instances of classes
    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());
 
// polymorphism
    personne p2=(personne)en;
    System.out.println("p2="+p2.toString());
    personne p3=(personne)et;
    System.out.println("p3="+p3.toString());

// a dictionary
    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 hand
}//end of class

I risultati 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. File di testo

4.9.1. Scrittura

Per scrivere su un file, è necessario un flusso di scrittura. A questo scopo è possibile utilizzare la classe FileWriter. I seguenti costruttori sono comunemente utilizzati:

FileWriter(String fileName)
crea un file denominato fileName — è quindi possibile scriverci — qualsiasi file esistente con lo stesso nome viene sovrascritto
FileWriter(String fileName,
boolean append)
come sopra: qualsiasi file esistente con lo stesso nome può essere utilizzato aprendolo in modalità di aggiunta (append=true)

La classe FileWriter fornisce una serie di metodi per la scrittura su un file, metodi ereditati dalla classe Writer. Per scrivere su un file di testo, è preferibile utilizzare la classe PrintWriter, i cui costruttori comunemente utilizzati sono i seguenti:

PrintWriter(Writer out)
l'argomento è di tipo Writer, ovvero un flusso di scrittura (su un file, in rete, ecc.)
PrintWriter(Writer out, boolean autoflush)
Come sopra. Il secondo argomento controlla il buffering delle righe. Se impostato su false (impostazione predefinita), le righe scritte nel file passano attraverso un buffer in memoria. Quando il buffer è pieno, viene scritto nel file. Ciò migliora l'accesso al disco. Detto questo, questo comportamento a volte è indesiderabile, in particolare quando si scrive su una rete.

I metodi utili della classe PrintWriter sono i seguenti:

void print(Type T)
scrive i dati T (String, int, ….)
void println(Type T)
come sopra, terminando con un carattere di nuova 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:

// imports
import java.io.*;

public class ecrire{
  public static void main(String[] arg){
    // open file
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // write to file
    try{
      fic.println("Jean,Dupont,27");
      fic.println("Pauline,Garcia,24");
      fic.println("Gilles,Dumond,56");
    } catch (Exception e){
      Erreur(e,3);
    }
     // close file
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// fine hand

  private static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }//Error
}//class

Il file di output generato dal programma è il seguente:

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

4.9.2. Lettura

Per leggere il contenuto di un file, è necessario un flusso di lettura associato al file. A tal fine è possibile utilizzare la classe FileReader e il seguente costruttore:

FileReader(String filename)
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, ecc.

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 size)
Legge un numero di caratteri specificato 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:

// imported classes
import java.util.*;
import java.io.*;

public class lire{
  public static void main(String[] arg){
    personne p=null;
     // open file
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new FileReader("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // data
    String ligne=null;
    String[] champs=null;
    String prenom=null;
    String nom=null;
    int age=0;

    // error handling
    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));
      }// end while
    } catch (Exception e){
      Erreur(e,2);
    }

     // close file
    try{
      IN.close();
    } catch (Exception e){
      Erreur(e,3);
    }
  }// fine hand

   // Error
  public static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }

}// end of class

L'esecuzione del programma produce 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 dotare la classe Person di un metodo che ci consenta di salvare gli attributi di una persona su un file. Aggiungiamo il metodo saveAttributes alla definizione della classe Person:


    // ------------------------------
  // sauvegarde dans fichier texte
    // ------------------------------
  public void sauveAttributs(PrintWriter P){
    P.println(""+this);
  }

Prima di definire la classe Person, non dimenticare di importare il pacchetto java.io:

import java.io.*;

Il metodo saveAttributes accetta un unico parametro: lo stream PrintWriter su cui deve scrivere. Un programma di prova potrebbe essere simile al seguente:

// imports
import java.io.*;
// import nobody;

public class sauver{
  public static void main(String[] arg){
    // open file
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // write to 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);
    }
     // close file
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// fine hand

   // Error
  private static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }//Error
}//class

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. File binari

4.10.1. La classe RandomAccessFile

La classe RandomAccessFile consente di gestire i file binari, in particolare quelli con una struttura fissa come quelli in C/C++. Ecco alcuni metodi e costruttori utili:

RandomAccessFile(String filename, String mode)
costruttore - apre il file specificato nella modalità specificata. La modalità può essere una delle seguenti:
r: apertura in lettura
rw: apri per lettura e scrittura
void writeTTT(TTT value)
scrive il valore nel file. TTT rappresenta il tipo di valore. La rappresentazione in memoria del valore viene scritta così com'è nel file. Esistono quindi writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat, ecc. Per scrivere una stringa, utilizzare writeBytes(String string).
TTT readTTT()
Legge e restituisce un valore di tipo TTT. Esempi includono readBoolean, readByte, readInt, readDouble, readLong, readFloat, ecc. 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)
imposta il cursore del file sul byte pos

4.10.2. La classe Article

Tutti gli esempi seguenti utilizzeranno la seguente classe Article:

     // article structure
    private static class article{
         // we define the structure
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//item class

La classe Item Java sopra riportata è equivalente alla seguente struttura C

struct article{
    char code[4];
    char nom[20];
    double prix;
    int stockActuel;
    int stockMinimum;
}//structure

In questo modo, limiteremo il codice a 4 caratteri e il nome a 20.

4.10.3. Crea un record

Il seguente programma scrive un record in un file denominato "data":

// imported classes
import java.io.*;

public class test1{

// tests the writing of a structure (in the C sense) to a binary file

     // article structure
    private static class article{
         // we define the structure
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//item class

    public static void main(String arg[]){

         // we define the binary file in which the items will be stored
        RandomAccessFile fic=null;

         // we define an article
        article art=new article();
        art.code="a100";
        art.nom="velo";
        art.prix=1000.80;
        art.stockActuel=100;
        art.stockMinimum=10;

         // we define the file
        try{
                fic=new RandomAccessFile("data","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data",1);
        }//try-catch

        // we write
        try{
            ecrire(fic,art);
        } catch (IOException E){
                erreur("Erreur lors de l'écriture de l'enregistrement",2);
        }//try-catch

        // it's over
        try{
            fic.close();
        } catch (Exception E){
            erreur("Impossible de fermer le fichier data",2);
        }//try-catch
  }//hand

   // writing method
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // code
      fic.writeBytes(art.code);
       // name limited to 20 characters
      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);
       // the price
      fic.writeDouble(art.prix);
       // inventories
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// end write

   // ------------------------erreur
  public static void erreur(String msg, int exitCode){
          System.err.println(msg);
          System.exit(exitCode);
  }// end error
}// fin class

Il seguente programma ci permette di verificare che l'esecuzione sia andata a buon fine.

4.10.4. Leggi un record

// imported classes
import java.io.*;

public class test2{

// tests the writing of a structure (in the C sense) to a binary file

     // article structure
    private static class article{
         // we define the structure
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//item class

    public static void main(String arg[]){

         // we define the binary file in which the items will be stored
        RandomAccessFile fic=null;

       // open the file in read mode
      try{
          fic=new RandomAccessFile("data","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data",1);
      }//try-catch

       // we read the single article in the file
    article art=new article();
      try{
          lire(fic,art);
      } catch (IOException E){
          erreur("Erreur lors de la lecture de l'enregistrement",2);
      }//try-catch

       // the record played back is displayed
      affiche(art);

       // it's over
      try{
          fic.close();
      } catch (Exception E){
          erreur("Impossible de fermer le fichier data",2);
      }//try-catch
  }// fine hand

    // reading method
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // code reading
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // name
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // price
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// end write

     // ---------------------affiche
    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);
    }// end poster

     // ------------------------erreur
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// end error
}// end class

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    

Abbiamo recuperato con successo il record scritto dal programma di scrittura.

4.10.5. Conversione da testo a binario

Il programma seguente è un'estensione del programma di scrittura dei record. Ora scriviamo 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
// imported classes
import java.io.*;
import java.util.*;

public class test3{

// text file --> binary file

     // article structure
    private static class article{
         // we define the structure
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//item class

    public static void main(String arg[]){

         // we define the binary file in which the items will be stored
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin",1);
        }

         // data are taken from a text file
    BufferedReader dataTxt=null;
        try{
            dataTxt=new BufferedReader(new FileReader("data.txt"));
        } catch (IOException E){
            erreur("Impossible d'ouvrir le fichier data.txt",2);
        }

       // .txt file --> .bin file
      String ligne=null;
      String[] champs=null;
      int numLigne=0;
      String champ=null;
    article art=new article();    // item to be created
      try{
          while((ligne=dataTxt.readLine())!=null){
          // a + line
              numLigne++;
         // decomposition into fields
        champs=ligne.split(":");
         // 5 fields are required
              if(champs.length!=5)
                  erreur("Ligne "+numLigne+" erronée dans data.txt",3);
         //code
              art.code=champs[0];
              if(art.code.length()!=4) 
            erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
         // last name, first name
              art.nom=champs[1];
         // price
              try{
                  art.prix=Double.parseDouble(champs[2]);
              } catch (Exception E){
                  erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
              }
        // current stock
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }
        // current stock
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }             
               // we write the recording
              try{
                  ecrire(dataBin,art);
              } catch (IOException E){
                  erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
              }
               // move on to the next line
          }// end while
      } catch (IOException E){
          erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
      }        
        // it's over
      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 hand

   // writing method
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // code
      fic.writeBytes(art.code);
       // name limited to 20 characters
      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);
       // the price
      fic.writeDouble(art.prix);
       // inventories
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// end write

     // ------------------------erreur
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// end error

}// end class

Il seguente programma consente di verificare che questo abbia funzionato correttamente.

4.10.6. Conversione da binario a testo

Il programma seguente legge il contenuto del file binario data.bin creato in precedenza e ne scrive il contenuto nel file di testo data.text. Se tutto va bene, il file data.text dovrebbe essere identico al file originale data.txt.

// imported classes
import java.io.*;
import java.util.*;

public class test5{

// text file --> binary file

     // article structure
    private static class article{
         // we define the structure
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//item class

     // hand
    public static void main(String arg[]){

         // we define the binary file in which the items will be stored
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","r");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
        }

         // data is written to a text file
    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);
        }

      // .bin file --> .text file
    article art=new article();    // item to be created

       // we process the binary file
      int numRecord=0;
      long l=0;    // file size
      try{
          l=dataBin.length();
      } catch (IOException e){
          erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
      }
      long pos=0;    // current position in file
      try{
          pos=dataBin.getFilePointer();
      } catch (IOException e){
          erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
      }

     // as long as you haven't passed the end of the file
        while(pos<l){
            // read and use current recordings
            numRecord++;
            try{
                lire(dataBin,art);
            } catch (Exception e){
                erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
            }
            affiche(art);

             // write the corresponding text line in dataTxt
         dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);

             // shall we continue?
            try{
                pos=dataBin.getFilePointer();
            } catch (IOException e){
                erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
            }
        }// end while

       // it's over
      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 hand

     // reading method
    public static void lire(RandomAccessFile fic, article art) throws IOException{
      // code reading
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // name
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // price
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// end write

     // ---------------------affiche
    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);
    }// end poster

     // ------------------------erreur
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// end error
}// fin class

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 dimostra la possibilità di accedere direttamente ai record in 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.

// imported classes
import java.io.*;
import java.util.*;

public class test6{

// text file --> binary file

     // article structure
    private static class article{
         // we define the structure
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//item class

     // hand
    public static void main(String[] args){

       // check the arguments
      int nbArguments=args.length;
      String syntaxe="syntaxe : pg numéro_de_fiche";
      if(nbArguments!=1)
          erreur(syntaxe,20);
     // check plug no
      int numRecord=0;
      try{
          numRecord=Integer.parseInt(args[0]);
      } catch(Exception e){
          erreur(syntaxe+"\nNuméro de fiche incorrect",21);
      }

       // open the binary file for reading
    RandomAccessFile dataBin=null;
      try{
          dataBin=new RandomAccessFile("data.bin","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
      }

       // position yourself on the desired card
      try{
          dataBin.seek((numRecord-1)*40);
      } catch (Exception e){
          erreur("La fiche "+numRecord+" n'existe pas",23);
      }

       // we read it
    article art=new article();
      try{
          lire(dataBin,art);
      } catch (Exception e){
          erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
      }

       // we display it
      affiche(art);

       // it's over
      try{
          dataBin.close();
      } catch (Exception E){
          erreur("Impossible de fermer le fichier data.bin",2);
      }//try-catch

  }// fine hand

    // reading method
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // code reading
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // name
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // price
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// end write

     // ---------------------affiche
    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);
    }// end poster

     // ------------------------erreur
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// end error
}// fin class

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 in Java

Il pacchetto java.util.regex consente l'uso di espressioni regolari. Queste consentono di convalidare il formato di una stringa. Ad esempio, è possibile verificare che una stringa che rappresenta una data sia nel formato gg/mm/aa. Per farlo, si utilizza un modello e si confronta la stringa con quel modello. In questo esempio, g, m e a devono essere cifre. Il modello per un formato di data valido è quindi "\d\d/\d\d/\d\d", dove il simbolo \d rappresenta una cifra. I simboli che possono essere utilizzati in un modello sono i seguenti (documentazione Microsoft):

Carattere
Descrizione
\
Indica che il carattere seguente è un 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'input.
$
Corrisponde alla fine dell'input.
*
Corrisponde al carattere precedente zero o più volte. Pertanto, "zo*" corrisponde a "z" o "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.
(pattern)
Cerca il pattern e memorizza la corrispondenza. La sottostringa corrispondente può essere recuperata dalla collezione Matches risultante utilizzando Item [0]...[n]. Per trovare corrispondenze con caratteri all'interno di parentesi ( ), utilizzare "\(" o "\)".
x|y
Corrisponde a x o y. Ad esempio, "z|foot" corrisponde a "z" o "foot". "(z|f)oo" corrisponde a "zoo" o "foo".
{n}
n è un numero intero non negativo. Trova esattamente n occorrenze del carattere. Ad esempio, "o{2}" non trova "o" in "Bob", ma trova le prime due "o" in "fooooot".
{n,}
n è un numero intero non negativo. Trova almeno n occorrenze del carattere. Ad esempio, "o{2,}" non trova "o" in "Bob", ma trova 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. Trova da un minimo di n a un massimo di m occorrenze del carattere. Ad esempio, "o{1,3}" trova le prime tre "o" in "foooooot" e "o{0,1}" equivale a "o?".
[xyz]
Insieme di caratteri. Trova uno qualsiasi dei caratteri specificati. Ad esempio, "[abc]" trova la "a" in "plat".
[^xyz]
Insieme di caratteri negativo. Trova qualsiasi carattere non elencato. Ad esempio, "[^abc]" trova la "p" in "plat".
[a-z]
Intervallo di caratteri. Corrisponde a qualsiasi carattere compreso nell'intervallo specificato. Ad esempio, "[a-z]" corrisponde a qualsiasi carattere alfabetico minuscolo compreso tra "a" e "z".
[^m-z]
Intervallo di caratteri negativo. Trova qualsiasi carattere non compreso nell'intervallo specificato. Ad esempio, "[^m-z]" trova qualsiasi carattere non compreso tra "m" e "z".
\b
Corrisponde a un confine di parola, ovvero alla posizione tra una parola e uno spazio. Ad esempio, "er\b" corrisponde a "er" in "lever", ma non a "er" in "verb".
\B
Corrisponde a un confine che non rappresenta una parola. "en*t\B" corrisponde a "ent" in "bien entendu".
\d
Corrisponde a un carattere che rappresenta una cifra. Equivalente a [0-9].
\D
Corrisponde a un carattere che non è una cifra. Equivalente a [^0-9].
\f
Corrisponde a un carattere di interruzione di riga.
\n
Corrisponde a un carattere di nuova riga.
\r
Equivalente al carattere di ritorno a capo.
\s
Corrisponde a qualsiasi carattere di spaziatura, inclusi spazio, tabulazione, interruzione di pagina, ecc. Equivalente a "[ \f\n\r\t\v]".
\S
Corrisponde a qualsiasi carattere non spazio. Equivalente 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 rappresenta una parola, compreso il trattino basso. Equivalente a "[A-Za-z0-9_]".
\W
Corrisponde a qualsiasi carattere che non rappresenti una parola. Equivalente 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, "\11" e "\011" corrispondono entrambi a un carattere di tabulazione. "\0011" è equivalente a "\001" e "1". I valori di escape ottali non devono superare 256. In caso contrario, nell'espressione vengono prese in considerazione solo le prime due cifre. Consente l'uso dei codici ASCII nelle espressioni regolari.
\xn
Corrisponde a n, dove n è un valore di escape esadecimale. I valori di escape esadecimali devono essere composti esattamente da due cifre. Ad esempio, "\x41" corrisponde a "A". "\x041" è equivalente a "\x04" e "1". Consente l'uso dei codici ASCII nelle espressioni regolari.

Un elemento in un pattern può apparire una o più volte. Vediamo alcuni esempi che coinvolgono il simbolo \d, che rappresenta una singola 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

Ora immaginiamo un modello in grado di descrivere il formato previsto per una stringa:

stringa di destinazione
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, che può essere 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 "just"
\bjuste\b
  

È possibile specificare dove cercare il pattern nella stringa:

pattern
significato
^motivo
il pattern inizia la stringa
pattern$
il pattern termina la stringa
^pattern$
il pattern inizia e termina la stringa
pattern
il pattern viene cercato in qualsiasi punto della stringa, a partire dall'inizio.
stringa di ricerca
pattern
una stringa che termina con un punto esclamativo
!$
una stringa che termina con un punto
\.$
una stringa che inizia con la sequenza //
^//
una stringa costituita da una singola parola, facoltativamente seguita o preceduta da spazi
^\s*\w+\s*$
una stringa composta da due parole, facoltativamente preceduta o seguita da spazi
^\s*\w+\s*\w+\s*$
una stringa contenente la parola secret
\bsecret\b

I sottopattern di un pattern possono essere "estratti". Pertanto, non solo possiamo verificare che una stringa corrisponda a un particolare pattern, ma possiamo anche estrarre da quella stringa gli elementi corrispondenti ai sottopattern del pattern che sono stati racchiusi tra parentesi. Ad esempio, se stiamo analizzando una stringa contenente una data nel formato dd/mm/yy e vogliamo anche estrarre gli elementi dd, mm e yy da quella data, useremmo il modello (\d\d)/(\d\d)/(\d\d).

4.11.2. Verifica della corrispondenza di una stringa con un dato modello

La classe Pattern consente di verificare se una stringa corrisponde a un dato modello. Per farlo, utilizzare il metodo statico

boolean Matches(String modèle, String chaine)

con: pattern: il modello da verificare, string: la stringa da confrontare con il modello. Il risultato è il valore booleano true se string corrisponde a pattern, false in caso contrario.

Ecco un esempio:

import java.io.*;
import java.util.regex.*;

// regular expression management
public class regex1 {
  public static void main(String[] args){
    // a regular expression template
    String modèle1="^\\s*\\d+\\s*$";
    // compare a copy with the model
    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
  }//hand

  public static void affiche(String msg){
    System.out.println(msg);
  }//poster
}//class

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 raddoppiato a causa della specifica interpretazione di questo carattere da parte di Java. Scriviamo quindi: String pattern1="^\\s*\\d+\\s*$";

4.11.3. Trovare tutti gli elementi di una stringa che corrispondono a un modello

Consideriamo il pattern "\d+" e la stringa " 123 456 789 ". Il pattern si trova in tre punti diversi della stringa. Le classi Pattern e Matcher consentono di recuperare le diverse occorrenze di un pattern in una stringa. La classe Pattern è quella che gestisce le espressioni regolari. Un'espressione regolare utilizzata più di una volta deve essere "compilata". Ciò accelera le ricerche di pattern nelle stringhe. Il metodo statico compile svolge questo compito:

public static Pattern compile(String regex)

Prende la stringa del pattern come parametro e restituisce un oggetto Pattern. Per confrontare il pattern di un oggetto Pattern con una stringa, utilizziamo la classe Matcher. Questa classe consente il confronto di un pattern con una stringa. Da un oggetto Pattern, è possibile ottenere un oggetto Matcher utilizzando il metodo matcher:

public Matcher matcher(CharSequence input)

input è la stringa da confrontare con il modello.

Quindi, per confrontare il pattern "\d+" con la stringa " 123 456 789 ", è possibile creare un oggetto Matcher come segue:

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

Utilizzando l'oggetto résultats di cui sopra, possiamo recuperare le varie occorrenze del pattern nella stringa. Per farlo, utilizziamo i seguenti metodi della classe Matcher:

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

Il metodo find cerca nella stringa esplorata la prima occorrenza del pattern. Una seconda chiamata a find cercherà l'occorrenza successiva, e così via. Il metodo restituisce true se trova il pattern, false in caso contrario. La porzione della stringa corrispondente all'ultima occorrenza trovata da find si ottiene utilizzando il metodo group, mentre la sua posizione si ottiene utilizzando il metodo start. Quindi, continuando con l'esempio precedente, se vogliamo visualizzare tutte le occorrenze del pattern "\d+" nella stringa " 123 456 789 ", scriveremmo:

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 riportare l'oggetto Matcher all'inizio della stringa da confrontare con il pattern. In questo modo, il metodo find troverà nuovamente la prima occorrenza del pattern.

Ecco un esempio completo:

import java.io.*;
import java.util.regex.*;

// regular expression management
public class regex2 {
  public static void main(String[] args){
    // several occurrences of the model in the copy
    String modèle2="\\d+";
    Pattern regex2=Pattern.compile(modèle2);
    String exemplaire3="  123  456 789";
    // search for model occurrences in the copy
    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);
  }//poster
}//class

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 pattern. Pertanto, non solo possiamo verificare che una stringa corrisponda a un particolare pattern, ma possiamo anche estrarre da quella stringa gli elementi corrispondenti ai sottoinsiemi del pattern racchiusi tra parentesi. Ad esempio, se stiamo analizzando una stringa contenente una data nel formato dd/mm/yy e vogliamo anche estrarre gli elementi dd, mm e yy da quella data, useremmo il pattern (\d\d)/(\d\d)/(\d\d).

Esaminiamo il seguente esempio:

import java.io.*;
import java.util.regex.*;

// regular expression management
public class regex3 {
  public static void main(String[] args){
    // capture elements in the model
    String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
    Pattern regex3=Pattern.compile(modèle3);
    String exemplaire4="Il est 18:05:49";
    // model checking
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // the copy corresponds to the model
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // display groups
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//for
      }else{
        // the copy does not correspond to the model
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }
    }//Main

    public static void affiche(String msg){
      System.out.println(msg);
    }//poster
}//class

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 nuova funzionalità si trova nel seguente frammento di codice:

    // model checking
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // the copy corresponds to the model
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // display groups
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//for
      }else{
        // the copy does not correspond to the model
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }

La stringa example4 viene confrontata con il pattern regex3 utilizzando il metodo find. Viene quindi trovata una corrispondenza per il pattern regex3 nella stringa example4. Se il pattern include sottoinsiemi racchiusi tra parentesi, questi sono accessibili 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 sottogruppi trovati nel pattern, mentre group(i) restituisce il sottogruppo numero i. Questo si trova nella stringa nella posizione indicata da start(i). Quindi, 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 troverà la stringa 18:05:49 e creerà automaticamente i tre sottoinsiemi definiti dalle parentesi nel pattern, ovvero 18, 05 e 49.

4.11.5. Un programma di apprendimento

Trovare l'espressione regolare che ci permette di verificare se una stringa corrisponde a un determinato modello può a volte essere una vera sfida. Il seguente programma ti permette di esercitarti. Richiede un modello e una stringa e poi indica se la stringa corrisponde o meno al modello.

import java.io.*;
import java.util.regex.*;

// regular expression management
public class regex4 {
  public static void main(String[] args){

    // data
    String modèle=null,chaine=null;
    Pattern regex=null;
    BufferedReader IN=null;
    Matcher résultats=null;
    int nbOccurrences=0;

    // error management
    try{
      // the user is asked for models and samples to compare with this one
      while(true){
        // iNPUTS
        IN=new BufferedReader(new InputStreamReader(System.in));
        // the model is requested
        System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
        modèle=IN.readLine();
        // finished?
        if(modèle.trim().toLowerCase().equals("fin")) break;
        // we create the regular expression
        regex=Pattern.compile(modèle);
        // the user is asked for the specimens to be compared with the model
        while(true){
          System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
          chaine=IN.readLine();
          // finished?
          if(chaine.trim().toLowerCase().equals("fin")) break;
          // create the matcher object
          résultats=regex.matcher(chaine);
          // search for occurrences of the
          nbOccurrences=0;
          while(résultats.find()){
            // we have an occurrence
            nbOccurrences++;
            // we display it
            System.out.println("J'ai trouvé la correspondance ["+résultats.group()
            +"] en position "+résultats.start());
            // display of sub-elements
            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
            // next channel
          }//while(résultats.find())
          // was at least one occurrence found?
          if(nbOccurrences==0){
            System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
          }//if
          // next model
        }//while(true)
      }//while(true)
    }catch(Exception ex){
      // error
      System.err.println("Erreur : "+ex.getMessage());
      // end with error
      System.exit(1);
    }//try-catch
    // end
    System.exit(0);
  }//Main
}//class

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 delimitatrice 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 separatrice 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 di input viene suddivisa in campi, separati da un separatore che corrisponde al pattern dell'oggetto Pattern corrente. Per recuperare i campi da una riga in cui il separatore di campo è una virgola preceduta o seguita da un numero qualsiasi di spazi, scriveremmo:

    // a line
    String ligne="abc  ,, def  , ghi";
    // a model
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // decomposition of line into fields
    String[] champs=modèle.split(ligne);

È possibile ottenere lo stesso risultato utilizzando il metodo split della classe String:


public String[] split(String regex)

Ecco un programma di prova:

import java.io.*;
import java.util.regex.*;

// regular expression management
public class split1 {
  public static void main(String[] args){
    // a line
    String ligne="abc  ,, def  , ghi";
    // a model
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // decomposition of line into fields
    String[] champs=modèle.split(ligne);
    // display
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
    // another way of doing things
    champs=ligne.split("\\s*,\\s*");
    // display
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
  }//Main
}//class

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 come segue:

$ pg -o1 v1 v2 ... -o2 v3 v4 …

dove -oi rappresenta un'opzione e vi un valore associato a tale opzione. Vogliamo creare una classe di opzioni che analizzi la stringa di argomenti -o1 v1 v2 ... -o2 v3 v4 … al fine di costruire le seguenti entità:

ValidOptions
un 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
invalidOptions
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
optionsWithout
stringa (String) che elenca i valori vi non associati a un'opzione
errore
un numero intero pari a 0 se non ci sono errori nell'elenco degli argomenti, altrimenti:
1: sono presenti parametri di chiamata non validi
2: ci sono opzioni non valide
4: sono presenti valori non associati alle opzioni
Se sono presenti più tipi di errori, questi valori sono cumulativi.

Un oggetto opzioni può essere costruito in 4 modi diversi:

public options (String arguments, String optionsAcceptable)

argomenti
gli argomenti della riga di comando -o1 v1 v2 ... -o2 v3 v4 … da analizzare
optionsAcceptable
l'elenco delle opzioni accettabili

Esempio di chiamata: options opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");

Qui, entrambi gli argomenti sono stringhe. Accetteremo i casi in cui queste stringhe sono state suddivise in parole e inserite in un array di stringhe. Ciò richiede tre costruttori aggiuntivi:

public options (String[] arguments, String optionsAcceptables)
public options (String arguments, String[] optionsAcceptables)
public options (String[] arguments, String[] optionsAcceptables)

La classe options avrà la seguente interfaccia (getter):

public Hashtable getOptionsValides()

restituisce un riferimento all'array optionsValides costruito al momento della creazione dell'oggetto options

public Hashtable getOptionsInvalides()

restituisce un riferimento all'array invalidOptions costruito al momento della creazione dell'oggetto options

public String getOptionsSans()

Restituisce un riferimento alla stringa optionsWithout costruita al momento della creazione dell'oggetto options

public int getErreur()

Restituisce il valore dell'attributo error creato al momento della creazione dell'oggetto options

public String toString()

Se non ci sono errori, visualizza i valori degli attributi optionsValides, optionsInvalides e optionsSans; altrimenti, visualizza il numero dell'errore.

Ecco un programma di esempio:


import java.io.*;
//import options;
 
public class test1{
  
  
  public static void main (String[] arg){
    
    // inlet flow opening
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    // read constructor arguments 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));
    }// end while
  }//fine hand
 
 
public static void affiche(Exception e){
    System.err.println("Erreur : "+e);
  }
 
}//end of class

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

Vogliamo creare una classe stringtovector che ci 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 seguente costruttore:


    private void stringtovector(String S, String separateur, int[] tChampsVoulus,
            boolean strict){
 
        // crée un vecteur avec les champs de la chaîne S
        // celle-ci est constituée de champs séparés par separateur
        // si séparateur=null, la chaîne ne forme qu'un seul champ
        // seuls les champs dont les index sont dans le tableau tChampsVoulus 
        // sont désirés. Les index commencent à 1
        // si tChampsvoulus=null ou de taille nulle, on prend tous les champs
        // si strict=vrai, tous les champs désirés doivent être présents

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: mancano alcuni campi obbligatori nonostante strict=true

La classe avrà anche due metodi:

    public int getErreur()

che restituisce il valore dell'attributo privato error.

    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 dalla stringa.

Un programma di prova potrebbe essere simile a questo:


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, usa il metodo split della classe String.
  2. Inserisci i campi di S in un dizionario D indicizzato dal numero del campo
  3. Recupera dal dizionario D solo quei campi le cui chiavi (indici) sono presenti nell'array tChampsVoulus.

4.12.3. Esercizio 3

Vogliamo aggiungere il seguente costruttore alla classe stringtovector:


public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
        
        // crée un vecteur avec les champs de la chaîne S
        // celle-ci est constituée de champs séparés par separateur
        // si séparateur=null, la chaîne ne forme qu'un seul champ
        // seuls les champs dont les index sont dans sChampsVoulus sont désirés
        // les index commencent à 1
        // si sChampsvoulus=null ou "", on prend tous les champs
        // si strict=vrai, tous les champs désirés doivent être présents

L'elenco dei campi desiderati è quindi una stringa (String) anziché un array di interi (int[]). All'attributo privato error 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 tornare al caso del costruttore precedente trasferendo i campi dalla stringa sChampsVoulus in un array di interi. Per farlo, 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 determinare 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 se questa conversione non è possibile.

4.12.4. Esercizio 4

Vogliamo creare una classe filetovector che ci 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:


    // --------------------- constructeur 
    public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
        
        // creates a vector with the lines in the nomFichier text file
        // lines are made up of fields separated by separators
        // if separator=null, the line forms a single field
        // only fields whose indexes are in tChampsVoulus are desired
        // indexes start at 1
        // if tChampsvoulus=null or empty, all fields are taken
        // if strict=true, all desired fields must be present
        // if this is not the case, the line is not stored and its index
        // is placed in the vector lignesErronees
        // white lines are ignored
        // as well as lines beginning with tagCommentaire if tagCommentaire != null

La classe avrebbe i seguenti attributi privati:

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

L'attributo errore viene impostato dal costruttore precedente con i seguenti valori:

0: la creazione è andata a buon fine

1: impossibile aprire il file da elaborare

4: mancano alcuni campi obbligatori anche se 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 stringhe di caratteri. Una riga è considerata errata se non è in grado di fornire i campi richiesti quando strict=true.

La classe disporrà inoltre di due metodi:

    public int getErreur()

che restituisce il valore dell'attributo privato error.

    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 dal file e li sono i numeri delle righe errate.

Ecco un test di esempio:


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,# commentaire) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# commentaire) (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 utilizzando la classe stringtovector discussa in precedenza.

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

  3. Il metodo identite di filetovector può avvalersi del metodo stringtovector.identite() per visualizzare i propri elementi, nonché del metodo Vector.toString() per visualizzare il numero di eventuali righe errate.

4.12.5. Esercizio 5

Vogliamo aggiungere il seguente costruttore alla classe filetovector:


    public filetovector(String nomFichier, String separateur, String sChampsVoulus,
            boolean strict, String tagCommentaire){
        
        // creates a vector with the lines in the nomFichier text file
        // lines are made up of fields separated by separators
        // if separator=null, the line forms a single field
        // only fields whose indexes are in tChampsVoulus are desired
        // indexes start 1
        // if sChampsvoulus=null or empty, all fields are taken
        // if strict=true, all desired fields must be present
        // if this is not the case, the line is not stored and its index
        // is placed in the vector lignesErronees
        // white lines are ignored
        // as well as lines beginning with tagCommentaire if tagCommentaire != null

L'elenco degli indici per i campi obbligatori è ora contenuto in una stringa (String) anziché in un array di numeri interi.

L'attributo privato error può avere un valore aggiuntivo:

2: la stringa degli indici dei campi desiderati non è corretta

Ecco un test di esempio:


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());
  }
}

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,# commentaire) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# commentaire) (0,azerty,1,cvf,fff,qqqq) (0,s)]

Alcuni consigli

  1. Convertiamo la stringa sChampsVoulus in un array di interi tChampVoulus per riportarla al caso del costruttore precedente.