Skip to content

4. Powszechnie używane klasy

W niniejszym rozdziale przedstawiamy kilka powszechnie używanych klas języka Java. Klasy te posiadają wiele atrybutów, metod i konstruktorów. Za każdym razem przedstawiamy jedynie niewielką część tych klas. Szczegółowe informacje na ich temat można znaleźć w pomocy języka Java, którą teraz przedstawiamy.

4.1. Dokumentacja

Jeśli zainstalowali Państwo pakiet JDK firmy Sun w folderze <jdk>, dokumentacja jest dostępna w folderze <jdk>\docs:

Image

Czasami mamy plik jdk, ale bez dokumentacji. Można ją znaleźć na stronie firmy Sun http://www.sun.com. W folderze docs znajduje się plik index.html, który stanowi punkt wyjścia dla pomocy dotyczącej pliku JDK:

Image

Image

Powyższy link „API & Language” zapewnia dostęp do klas Java. Link „Demos/Tutorials” jest szczególnie przydatny, aby uzyskać przykłady programów w języku Java. Przejdźmy do linku „API & Language”:

Image

Kliknijmy link „Java 2 Platform” API:

Image

Ta strona stanowi prawdziwy punkt wyjścia dla dokumentacji dotyczącej klas. Można utworzyć skrót do tej strony, aby mieć do niej szybki dostęp. Plik URL znajduje się w katalogu <jdk>\docs\api\index.html. Znajdują się tam linki do setek klas Java z biblioteki JDK. Na początku największą trudnością jest ustalenie, do czego służą te różne klasy. Na początku ta pomoc ma więc sens tylko wtedy, gdy znamy nazwę klasy, o której chcemy uzyskać informacje. Można również kierować się nazwami klas, które zazwyczaj wskazują na ich przeznaczenie.

Weźmy przykład i poszukajmy informacji o klasie Vector, która implementuje tablicę dynamiczną. Wystarczy znaleźć na liście klas w lewym panelu link do klasy Vector:

Image

i kliknąć ten link, aby wyświetlić definicję klasy:

Image

Znajduje się tam

  • hierarchię, w której znajduje się ta klasa, w tym przypadku java.util.Vector
  • listę pól (atrybutów) klasy
  • listę konstruktorów
  • listę metod

W dalszej części przedstawiamy różne klasy. Zachęcamy czytelnika do systematycznego sprawdzania pełnych definicji używanych klas.

4.2. Klasy testowe

W poniższych przykładach wykorzystujemy czasami klasy personne oraz enseignant. Przypominamy tutaj ich definicje.

public class personne{
    // nazwisko, imię, wiek
  private String prenom;
  private String nom;
  private int age;

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

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

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

  //modyfikatory
  public void setPrenom(String P){
    this.prenom=P;
  }
  public void setNom(String N){
    this.nom=N;
  }
  public void setAge(int age){
    this.age=age;
  }
}

Klasa enseignant wywodzi się z klasy personne i jest zdefiniowana w następujący sposób:

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

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

Wykorzystamy również klasę etudiant, wywodzącą się z klasy personne i zdefiniowaną w następujący sposób:


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. Klasa String

Klasa String reprezentuje ciągi znaków. Niech nom będzie zmienną typu ciąg znaków:

String nazwa;

nom jest odwołaniem do obiektu, który nie został jeszcze zainicjowany. Można go zainicjować na dwa sposoby:

nom="koń" lub nom=new String("koń")

Obie metody są równoważne. Jeśli później zapiszemy nom=&quot;poisson&quot;, to nom będzie odnosić się do nowego obiektu. Poprzedni obiekt *String(&quot;cheval&quot;)* zostanie utracony, a zajmowane przez niego miejsce w pamięci zostanie zwolnione.

Klasa String posiada wiele atrybutów i metod. Oto kilka z nich:

public char charAt(int i)
zwraca znak i z ciągu znaków, przy czym pierwszy znak ma indeks 0. Zatem String("cheval").charAt(3) jest równe 'v'
public int compareTo(ciąg2)
chaine1.compareTo(ciąg2) porównuje ciąg1 z ciągiem2 i zwraca 0, jeśli ciąg1 = ciąg2, 1, jeśli ciąg1 > ciąg2, oraz -1, jeśli ciąg1 < ciąg2
public boolean equals(Object anObject)
chaine1.equals(ciąg2) zwraca wartość true, jeśli ciąg1 = ciąg2, w przeciwnym razie zwraca wartość false
public String toLowerCase()
chaine1.toLowerCase() zamienia ciąg1 na małe litery
public String toUpperCase()
chaine1.toUpperCase() zamienia ciąg1 na wielkie litery
public String trim()
chaine1.trim() usuwa spacje na początku i na końcu ciągu1
public String substring(int beginIndex, int endIndex)
String("chapeau").subString(2,4) zwraca ciąg znaków „ape”
public char[] toCharArray()
umożliwia umieszczenie znaków ciągu w tablicy znaków
int length()
liczba znaków w ciągu
int indexOf(String chaine2)
zwraca pozycję, na której w bieżącym ciągu znaków znajduje się chaine2, lub -1, jeśli chaine2 nie występuje
int indexOf(String chaine2, int startIndex)
zwraca pozycję, na której w bieżącym ciągu znaków znajduje się chaine2, lub -1, jeśli chaine2 nie występuje. Wyszukiwanie rozpoczyna się od znaku o numerze startIndex.
int lastIndexOf(String chaine2)
zwraca ostatnią pozycję ciągu „chaine2” w bieżącym ciągu lub -1, jeśli ciąg „chaine2” nie występuje
boolean startsWith(String chaine2)
zwraca wartość true, jeśli bieżący ciąg znaków zaczyna się od chaine2
boolean endsWith(String chaine2)
zwraca wartość true, jeśli bieżący ciąg kończy się na chaine2
boolean matches(String regex)
zwraca wartość true, jeśli bieżący ciąg znaków pasuje do wyrażenia regularnego regex.
String[] split(String regex)
Bieżący ciąg składa się z pól oddzielonych ciągiem znaków opisanym wyrażeniem regularnym regex. Metoda split pozwala pobrać pola do tablicy.
String replace(char oldChar, char newChar)
zastępuje w bieżącym ciągu znaków znak oldChar znakiem newChar.

Oto przykładowy program:

// importy
import java.io.*;

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

  // wyświetla
    public static void affiche(String msg){
        // wyświetla komunikat
        System.out.println(msg);
    }//wyświetla
}//klasa

oraz uzyskane wyniki:

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. Klasa Vector

Wektor to dynamiczna tablica, której elementami są odwołania do obiektów. Jest to zatem tablica obiektów, której rozmiar może się zmieniać w czasie, co nie jest możliwe w przypadku statycznych tablic, z którymi mieliśmy do tej pory do czynienia. Oto niektóre pola, konstruktory i metody tej klasy:

public Vector()
tworzy pusty wektor
public final int size()
liczba elementów wektora
public final void addElement(Object obj)
dodaje obiekt, do którego odwołuje się obj, do wektora
public final Object elementAt(int index)
odwołanie do obiektu o numerze index w tablicy – indeksy zaczynają się od 0
public final Enumeration elements()
zbiór elementów wektora w postaci wyliczenia
public final Object firstElement()
odwołanie do pierwszego elementu wektora
public final Object lastElement()
odwołanie do ostatniego elementu wektora
public final boolean isEmpty()
zwraca wartość true, jeśli wektor jest pusty
public final void removeElementAt(int index)
usuwa element o indeksie index
public final void removeAllElements()
usuwają wszystkie elementy z wektora
public final String toString()
zwraca ciąg znaków identyfikujący wektor

Oto program testowy:


// zaimportowane klasy
import java.util.*;

public class test1{

// program główny main – static – metoda klasy

  public static void main(String arg[]){

// tworzenie obiektów – instancji klas
    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());

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

// wektor
    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());
    }
} // koniec funkcji main
}// koniec klasy

Skompilujmy ten program:

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

Uruchommy plik 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)

W dalszej części nie będziemy już powtarzać procesu kompilacji i uruchamiania programów testowych. Wystarczy powtórzyć czynności opisane powyżej.

4.5. Klasa ArrayList

Klasa ArrayList jest analogiczna do klasy Vector. Różni się od niej zasadniczo tylko wtedy, gdy jest używana jednocześnie przez wiele wątków wykonawczych. Metody synchronizacji wątków w celu uzyskania dostępu do obiektu Vector lub ArrayList różnią się. Poza tym przypadkiem można stosować dowolną z tych klas. Oto niektóre pola, konstruktory i metody tej klasy:

ArrayList()
tworzy pustą listę
int size()
liczba elementów tablicy
void add(Object obj)
dodaje obiekt, do którego odwołuje się obj, do tablicy
void add(int index, Object obj)
dodaje obiekt, do którego odwołuje się obj, do tablicy w pozycji index
Object get(int index)
odwołanie do obiektu nr index z tablicy – indeksy zaczynają się od 0
boolean isEmpty()
zwraca wartość true, jeśli tablica jest pusta
void remove(int index)
usuwa element o indeksie index
void clear()
czyści tablicę ze wszystkich elementów
Object[] toArray()
przekształca tablicę dynamiczną w tablicę klasyczną
String toString()
zwraca ciąg znaków identyfikujący tablicę

Oto program testowy:

// importowane klasy
import java.util.*;

public class test1{

// program główny main – static – metoda klasy

  public static void main(String arg[]){

// tworzenie obiektów – instancji klas
    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);

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

// wektor
    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);
    }
} // koniec funkcji main
}// koniec klasy

Uzyskane wyniki są takie same jak poprzednio.

4.6. Klasa Arrays

Klasa java.util.Arrays udostępnia metody statyczne umożliwiające wykonywanie różnych operacji na tablicach, w szczególności sortowanie i wyszukiwanie elementów. Oto kilka z tych metod:

static void sort(tablica)
sortuje tableau, wykorzystując do tego domyślną kolejność typu danych tablicy, czyli liczb lub ciągów znaków.
static void sort (Object[] tablica, Comparator C)
sortuje tablicę tableau, wykorzystując do porównywania elementów funkcję porównawczą C
static int binarySearch(tablica, element)
zwraca pozycję elementu o indeksie élément w tablicy tableau lub wartość <0 w przeciwnym razie. Tablica musi być wcześniej posortowana.
static int binarySearch(Object[] tablica, Object element, Comparator C)
to samo, ale wykorzystuje funkcję porównawczą C do porównania dwóch elementów tablicy.

Oto pierwszy przykład:

import java.util.*;

public class sort2 implements Comparator{

   // klasa wewnętrzna prywatna
  private class personne{
    private String nom;
    private int age;
    public personne(String nom, int age){
      this.nom=nom; // imię i nazwisko osoby
      this.age=age; // jej wiek
    }
     // pobierz wiek
    public int getAge(){
      return age;
    }
     // tożsamość osoby
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // klasa osoby

   // twórca
  public sort2() {
     // tablica osób
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // sortowanie tablicy osób
    Arrays.sort(amis,this);
     // weryfikacja
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//konstruktor

   // funkcja porównująca osoby
  public int compare(Object o1, Object o2){
    // musi zwrócić
     // -1, jeśli o1 jest „mniejsze niż” o2
     // 0, jeśli o1 „jest równe” o2
     // +1, jeśli o1 jest „większe niż” 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;
  }//porównuje

    // funkcja testowa
  public static void main(String[] arg){
    new sort2();
  }//ręka

}//klasa

Przyjrzyjmy się temu programowi. Funkcja main tworzy obiekt sort2. Konstruktor klasy sort2 wygląda następująco:

   // producent
  public sort2() {
     // tabela osób
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // sortowanie listy osób
    Arrays.sort(amis,this);
     // weryfikacja
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//twórca

Tablą, którą należy posortować, jest tabela obiektów personne. Klasa personne jest zdefiniowana jako prywatna (private) wewnątrz klasy sort2. Metoda statyczna sort klasy Arrays nie wie, jak posortować tablicę obiektów personne, dlatego w tym przypadku konieczne jest użycie formy void sort(Object[] obj, Comparator C). Comparator jest interfejsem definiującym tylko jedną metodę:

    int compare(Object o1, Object o2)

i która musi zwracać 0: jeśli o1=o2, -1: jeśli o1 < o2, +1: jeśli o1 > o2. W prototypie void sort(Object[] obj, Comparator C) drugi argument C musi być obiektem implementującym interfejs Comparator. W konstruktorze sort2 wybrano bieżący obiekt this:

     // sortowanie tabeli osób
    Arrays.sort(amis,this);

Wymaga to od nas wykonania dwóch czynności:

  1. wskazać, że klasa sort2 implementuje interfejs Comparator
public class sort2 implements Comparator{
  1. napisać funkcję compare w klasie sort2.

Wygląda ona następująco:

   // funkcja porównująca osoby
  public int compare(Object o1, Object o2){
    // musi zwrócić
     // -1, jeśli o1 jest „mniejsze niż” o2
     // 0, jeśli o1 „jest równe” o2
     // +1, jeśli o1 jest „większe niż” 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;
  }//porównuje

Aby porównać dwa obiekty personne, wykorzystujemy tutaj wiek (można by było użyć nazwy).

Wyniki wykonania są następujące:

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

Można było postąpić inaczej, aby zaimplementować interfejs Comparator:

import java.util.*;

public class sort2 {

  // wewnętrzna klasa prywatna
  private class personne{
…….
  }; // klasa osoba

   // konstruktor
  public sort2() {
     // tablica osób
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // sortowanie tablicy osób
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//porównanie
      }//klasa
    );
     // weryfikacja
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//generator

   // funkcja porównująca osoby
  public int compare1(Object o1, Object o2){
    // musi zwrócić
     // -1, jeśli o1 jest „mniejsze niż” o2
     // 0, jeśli o1 „jest równe” o2
     // +1, jeśli o1 jest „większe niż” 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;
  }//porównanie1

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

Instrukcja sortowania przybrała następującą postać:

     // sortowanie tablicy osób
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//porównanie
      }//klasa
    );

Drugi parametr metody sort musi być obiektem implementującym interfejs Comparator. W tym miejscu tworzymy taki obiekt za pomocą metody new java.util.Comparator(), a tekst następujący po {…} definiuje klasę, której obiekt tworzymy. Nazywamy to klasą anonimową, ponieważ nie ma ona nazwy. W tej anonimowej klasie, która musi implementować interfejs Comparator, definiujemy metodę compare tego interfejsu. Metoda ta ogranicza się do wywołania metody compare1 klasy sort2. W ten sposób powracamy do poprzedniego przypadku.

Klasa sort2 nie implementuje już interfejsu Comparator. W związku z tym jej deklaracja przyjmuje następującą postać:

public class sort2 {

Teraz przetestujemy metodę binarySearch klasy Arrays na poniższym przykładzie:

import java.util.*;

public class sort4 {

  // klasa wewnętrzna prywatna
  private class personne{
      // atrybuty
    private String nom;
    private int age;

        // konstruktor
    public personne(String nom, int age){
      this.nom=nom; // imię i nazwisko osoby
      this.age=age; // jej wiek
    }

     // pobierz imię
    public String getNom(){
      return nom;
    }

     // pobierz wiek
    public int getAge(){
      return age;
    }
     // tożsamość osoby
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // klasa osoby

   // konstruktor
  public sort4() {
     // tabela osób
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};

     // komparatory
    java.util.Comparator comparateur1=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//porównuje
      }//klasa
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//porównuje
      }//klasyfikuje
      ;          

     // sortowanie tabeli osób
    Arrays.sort(amis,comparateur1);
    // weryfikacja
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
    // wyszukiwanie
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);
  }//generator

   // funkcja porównująca osoby
  public int compare1(Object o1, Object o2){
    // musi zwrócić
     // -1, jeśli o1 jest „mniejsze niż” o2
     // 0, jeśli o1 „jest równe” o2
     // +1, jeśli o1 jest „większe niż” 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

   // funkcja porównująca osobę z imieniem
  public int compare2(Object o1, Object o2){
    // o1 jest osobą
     // o2 jest ciągiem znaków, imieniem nom2 danej osoby
     // musi zwrócić
     // -1, jeśli o1.nom jest „mniejsze niż” nom2
     // 0, jeśli o1.nom jest „równe” imieniu2
     // +1, jeśli o1.nom jest „większe niż” nazwa2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//porównanie2

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // szuka znajomego w tablicy znajomych
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // znaleziono?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//szuka

  // ręka
  public static void main(String[] arg){
    new sort4();
  }//ręka
}//klasa

W tym przypadku postąpiliśmy nieco inaczej niż w poprzednich przykładach. Dwa obiekty Comparator, niezbędne dla metod sort i binarySearch, zostały utworzone i przypisane do zmiennych comparateur1 oraz comparateur2.

     // porównywarki
    java.util.Comparator comparateur1=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//porównuje
      }//klasa
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//porównuje
      }//klasa
      ;          

W kreatorze sort4 dwukrotnie przeprowadzane jest wyszukiwanie dychotomiczne w tabeli amis:

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

Metoda cherche otrzymuje wszystkie parametry potrzebne do wywołania metody binarySearch:

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // wyszukiwanie znajomego w tabeli znajomych
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // znaleziono?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//szuka

Metoda binarySearch współpracuje z komparatorem comparateur2, który z kolei wywołuje metodę compare2 klasy sort4. Metoda rend zwraca pozycję szukanego nazwy w tablicy, jeśli istnieje, lub liczbę <0 w przeciwnym razie. Metoda compare2 służy do porównywania obiektu typu personne z nazwą typu String.

   // funkcja porównująca osobę z imieniem
  public int compare2(Object o1, Object o2){
    // o1 to osoba
     // o2 to ciąg znaków, imię „nom2” danej osoby
     // musi zwrócić
     // -1, jeśli o1.nom jest „mniejsze niż” nazwa2
     // 0, jeśli o1.nom „jest równe” nazwa2
     // +1, jeśli o1.nom „większe niż” nazwa2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//porównanie2

W przeciwieństwie do metody sort, metoda binarySearch nie przyjmuje dwóch obiektów personne, lecz jeden obiekt personne i jeden obiekt String w tej kolejności. Pierwszy parametr jest elementem tablicy amis, a drugi to imię i nazwisko poszukiwanej osoby.

4.7. Klasa Enumeration

Enumeration jest interfejsem, a nie klasą. Posiada następujące metody:

public abstract boolean hasMoreElements()
zwraca wartość true, jeśli wyliczenie zawiera jeszcze elementy
public abstract Object nextElement()
zwraca odwołanie do następnego elementu wyliczenia

Jak korzystać z wyliczenia? Zazwyczaj w następujący sposób:

    Enumeration e=…                // pobieramy obiekt typu wyliczeniowego
    while(e.hasMoreElements()){
        // wykorzystanie elementu e.nextElement()
    }

Oto przykład:


// zaimportowane klasy
import java.util.*;

public class test1{

// program główny main – metoda statyczna klasy

  public static void main(String arg[]){

// tworzenie obiektów – instancji klas
    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());

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

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

// wyliczenie
    Enumeration E=V.elements();
    i=0;
    while(E.hasMoreElements()){
      p2=(personne) E.nextElement();
      System.out.println("V["+i+"]="+p2.toString());
      i++;
    }
   }// koniec funkcji main
}//koniec klasy

Otrzymujemy następujące wyniki:

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. Klasa Hashtable

Klasa Hashtable umożliwia implementację słownika. Słownik można postrzegać jako tablicę o dwóch kolumnach:

klucz
wartość
klucz1
wartość1
klucz2
wartość2
..
...

Klucze są unikalne, np. c.a.d, co oznacza, że nie mogą istnieć dwa identyczne klucze. Główne metody i właściwości klasy Hashtable są następujące:

public Hashtable()
konstruktor – tworzy pusty słownik
public int size()
liczba elementów w słowniku – przy czym elementem jest para (klucz, wartość)
public Object put(Object key, Object value)
dodaje parę (key, value) do słownika
public Object get(Object key)
pobiera obiekt powiązany z kluczem key lub null, jeśli klucz key nie istnieje
public boolean containsKey(Object key)
zwraca wartość true, jeśli klucz key istnieje w słowniku
public boolean contains(Object value)
prawda, jeśli wartość value występuje w słowniku
public Enumeration keys()
zwraca klucze słownika w postaci wyliczenia
public Object remove(Object key)
usuwa parę (klucz, wartość), gdzie klucz = key
public String toString()
identyfikuje słownik

Oto przykład:


// importowane klasy
import java.util.*;

public class test1{

// program główny main – static – metoda klasy

  public static void main(String arg[]){

// tworzenie obiektów – instancji klas
    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());

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

// słownik
    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++;
    }
  }//koniec funkcji main
}//koniec klasy

Otrzymane wyniki są następujące:

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. Pliki tekstowe

4.9.1. Zapis

Aby zapisać dane do pliku, należy dysponować strumieniem zapisu. W tym celu można wykorzystać klasę FileWriter. Najczęściej stosowane konstruktory to:

FileWriter(String fileName)
tworzy plik o nazwie fileName – można następnie zapisywać w nim dane – ewentualny plik o tej samej nazwie zostanie nadpisany
FileWriter(String fileName,
boolean append)
to samo – istniejący plik o tej samej nazwie można wykorzystać, otwierając go w trybie dołączania (append=true)

Klasa FileWriter udostępnia szereg metod do zapisu w pliku, odziedziczonych po klasie Writer. Do zapisu w pliku tekstowym zaleca się użycie klasy PrintWriter, której najczęściej stosowane konstruktory to:

PrintWriter(Writer out)
argumentem jest obiekt typu Writer lub c.a.d – strumień zapisu (do pliku, w sieci itp.)
PrintWriter(Writer out, boolean autoflush)
to samo. Drugi argument zarządza buforowaniem wierszy. Gdy ma wartość fałsz (wartość domyślna), wiersze zapisywane do pliku przechodzą przez bufor w pamięci. Gdy bufor jest pełny, zawartość jest zapisywana do pliku. Poprawia to dostęp do dysku. Jednak czasami takie zachowanie jest niepożądane, zwłaszcza podczas zapisu w sieci.

Przydatne metody klasy PrintWriter to:

void print(Type T)
zapisuje dane typu T (String, int, …)
void println(Type T)
to samo, z dodatkiem znaku końca linii
void flush()
opróżnia bufor, jeśli nie jesteśmy w trybie automatycznego opróżniania
void close()
zamyka strumień zapisu

Oto program, który zapisuje kilka wierszy do pliku tekstowego:

// importy
import java.io.*;

public class ecrire{
  public static void main(String[] arg){
    // otwarcie pliku
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // zapis do pliku
    try{
      fic.println("Jean,Dupont,27");
      fic.println("Pauline,Garcia,24");
      fic.println("Gilles,Dumond,56");
    } catch (Exception e){
      Erreur(e,3);
    }
     // zamknięcie pliku
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// koniec funkcji main

  private static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }//Błąd
}//klasa

Plik out uzyskany w wyniku wykonania wygląda następująco:

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

4.9.2. Odczyt

Aby odczytać zawartość pliku, należy dysponować strumieniem odczytu powiązanym z tym plikiem. W tym celu można wykorzystać klasę FileReader oraz następujący konstruktor:

FileReader(String nazwaPliku)
otwiera strumień odczytu z podanego pliku. W przypadku niepowodzenia operacji zgłasza wyjątek.

Klasa FileReader posiada szereg metod służących do odczytu z pliku, które odziedziczyła po klasie Reader. Aby odczytać wiersze tekstu z pliku tekstowego, zaleca się użycie klasy BufferedReader wraz z następującym konstruktorem:

BufferedReader(Reader in)
otwiera buforowany strumień odczytu z strumienia wejściowego typu in. Strumień ten, typu Reader, może pochodzić z klawiatury, pliku, sieci itp.

Przydatne metody klasy BufferedReader to:

int read()
odczytuje znak
String readLine()
odczytuje wiersz tekstu
int read(char[] buffer, int offset, int taille)
odczytuje taille znaków z pliku i umieszcza je w tablicy buffer, zaczynając od pozycji offset.
void close()
zamyka strumień odczytu

Oto program, który odczytuje zawartość utworzonego wcześniej pliku:

// zaimportowane klasy
import java.util.*;
import java.io.*;

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

    // obsługa ewentualnych błędów
    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));
      }// koniec pętli while
    } catch (Exception e){
      Erreur(e,2);
    }

     // zamknięcie pliku
    try{
      IN.close();
    } catch (Exception e){
      Erreur(e,3);
    }
  }// koniec funkcji main

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

}// koniec klasy

Po uruchomieniu programu otrzymujemy następujące wyniki:

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

4.9.3. Zapisywanie obiektu osoby

Wykorzystujemy to, co właśnie omówiliśmy, aby zapewnić klasie personne metodę umożliwiającą zapisanie atrybutów osoby w pliku. Dodajemy metodę sauveAttributs do definicji klasy personne:


    // ------------------------------
  // zapis do pliku tekstowego
    // ------------------------------
  public void sauveAttributs(PrintWriter P){
    P.println(""+this);
  }

Przed zdefiniowaniem klasy personne należy pamiętać o zaimportowaniu pakietu java.io:

import java.io.*;

Metoda sauveAttributs przyjmuje jako jedyny parametr strumień PrintWriter, do którego ma zapisywać dane. Program testowy mógłby wyglądać następująco:

// importy
import java.io.*;
// import osoby;

public class sauver{
  public static void main(String[] arg){
    // otwarcie pliku
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // zapis do pliku
    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);
    }
     // zamknięcie pliku
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// koniec funkcji main

   // Błąd
  private static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }//Błąd
}//klasa

Skompilujmy i uruchommy ten program:

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. Pliki binarne

4.10.1. Klasa RandomAccessFile

Klasa RandomAccessFile umożliwia zarządzanie plikami binarnymi, w szczególności tymi o stałej strukturze, znanymi z języków C/C++. Oto kilka przydatnych metod i konstruktorów:

RandomAccessFile(String nazwaPliku, String tryb)
konstruktor – otwiera wskazany plik w określonym trybie. Tryb ten przyjmuje następujące wartości:
r: otwarcie do odczytu
rw: otwarcie do odczytu i zapisu
void writeTTT(TTT wartość)
zapisuje wartość do pliku. TTT określa typ wartości. Reprezentacja pamięciowa wartości jest zapisywana w pliku w niezmienionej postaci. W ten sposób występują: writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat, ... Aby zapisać ciąg znaków, używa się writeBytes(String chaine).
TTT readTTT()
odczytuje i zwraca wartość typu TTT. Znajdujemy zatem readBoolean, readByte, readInt, readDouble, readLong, readFloat, ... Metoda read() odczytuje jeden bajt.
long length()
rozmiar pliku w bajtach
long getFilePointer()
aktualna pozycja wskaźnika pliku
void seek(long pos)
ustawia kursor pliku na bajt o pozycji pos

4.10.2. Klasa artykułu

Wszystkie poniższe przykłady będą wykorzystywać następującą klasę article:

     // struktura artykułu
    private static class article{
         // definiujemy strukturę
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//klasa artykułu

Powyższa klasa Java article będzie odpowiadać następującej strukturze article w języku C

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

W ten sposób ograniczymy długość kodu do 4 znaków, a nazwy do 20.

4.10.3. Zapisanie rekordu

Poniższy program zapisuje wpis do pliku o nazwie „data”:

// klasy importowane
import java.io.*;

public class test1{

// testuje zapis struktury (w rozumieniu języka C) w pliku binarnym

     // struktura artykułu
    private static class article{
         // definiujemy strukturę
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//klasa article

    public static void main(String arg[]){

         // definiujemy plik binarny, w którym będą przechowywane artykuły
        RandomAccessFile fic=null;

         // definiuje się artykuł
        article art=new article();
        art.code="a100";
        art.nom="velo";
        art.prix=1000.80;
        art.stockActuel=100;
        art.stockMinimum=10;

         // definiuje się plik
        try{
                fic=new RandomAccessFile("data","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data",1);
        }//try-catch

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

        // koniec
        try{
            fic.close();
        } catch (Exception E){
            erreur("Impossible de fermer le fichier data",2);
        }//try-catch
  }//main

   // metoda zapisu
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // kod
      fic.writeBytes(art.code);
       // nazwa ograniczona do 20 znaków
      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);
       // cena
      fic.writeDouble(art.prix);
       // stan magazynowy
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// koniec zapisu

   // ------------------------błąd
  public static void erreur(String msg, int exitCode){
          System.err.println(msg);
          System.exit(exitCode);
  }// koniec błędu
}// koniec klasy

Poniższy program pozwala nam sprawdzić, czy operacja została poprawnie wykonana.

4.10.4. Odczytanie rekordu

// zaimportowane klasy
import java.io.*;

public class test2{

// testuje zapis struktury (w rozumieniu języka C) w pliku binarnym

     // struktura article
    private static class article{
         // definiujemy strukturę
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//klasa „article”

    public static void main(String arg[]){

         // definiujemy plik binarny, w którym będą przechowywane artykuły
        RandomAccessFile fic=null;

       // otwieramy plik do odczytu
      try{
          fic=new RandomAccessFile("data","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data",1);
      }//try-catch

       // odczytujemy pojedynczy artykuł z pliku
    article art=new article();
      try{
          lire(fic,art);
      } catch (IOException E){
          erreur("Erreur lors de la lecture de l'enregistrement",2);
      }//try-catch

       // wyświetlamy odczytany rekord
      affiche(art);

       // koniec
      try{
          fic.close();
      } catch (Exception E){
          erreur("Impossible de fermer le fichier data",2);
      }//try-catch
  }// koniec ręcznego wprowadzania

    // metoda odczytu
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // odczyt kodu
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nazwa
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // cena
        art.prix=fic.readDouble();
    // stan magazynowy
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// koniec zapisu

     // ---------------------wyświetla
    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);
    }// koniec wyświetlania

     // ------------------------błąd
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// koniec błędu
}// koniec klasy

Wyniki wykonania są następujące:

E:\data\serge\JAVA\random>java test2
code : a100
nom : velo
prix : 1000.8
Stock actuel : 100
Stock minimum : 10    

Pobieramy poprawnie rekord, który został zapisany przez program zapisujący.

4.10.5. Konwersja tekstu na format binarny

Poniższy program stanowi rozszerzenie programu do zapisywania pojedynczego rekordu. Teraz zapisujemy kilka rekordów do pliku binarnego o nazwie data.bin. Dane pochodzą z następującego pliku 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
// zaimportowane klasy
import java.io.*;
import java.util.*;

public class test3{

// plik tekstowy --> plik binarny

     // struktura artykułu
    private static class article{
         // definiujemy strukturę
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//klasa artykułu

    public static void main(String arg[]){

         // definiuje się plik binarny, w którym będą przechowywane artykuły
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin",1);
        }

         // dane  pobierane z pliku tekstowego
    BufferedReader dataTxt=null;
        try{
            dataTxt=new BufferedReader(new FileReader("data.txt"));
        } catch (IOException E){
            erreur("Impossible d'ouvrir le fichier data.txt",2);
        }

       // plik .txt --> plik .bin
      String ligne=null;
      String[] champs=null;
      int numLigne=0;
      String champ=null;
    article art=new article();    // artykuł do utworzenia
      try{
          while((ligne=dataTxt.readLine())!=null){
          // wiersz z znakiem +
              numLigne++;
         // podział na pola
        champs=ligne.split(":");
         // potrzeba 5 pól
              if(champs.length!=5)
                  erreur("Ligne "+numLigne+" erronée dans data.txt",3);
         //kod
              art.code=champs[0];
              if(art.code.length()!=4) 
            erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
         // nazwisko, imię
              art.nom=champs[1];
         // cena
              try{
                  art.prix=Double.parseDouble(champs[2]);
              } catch (Exception E){
                  erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
              }
        // aktualny stan magazynowy
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }
        // aktualny stan magazynowy
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }             
               // zapisujemy rekord
              try{
                  ecrire(dataBin,art);
              } catch (IOException E){
                  erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
              }
               // przechodzimy do następnego wiersza
          }// koniec pętli while
      } catch (IOException E){
          erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
      }        
        // koniec
      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);
      }
  }// koniec funkcji main

   // metoda zapisu
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // kod
      fic.writeBytes(art.code);
       // nazwa ograniczona do 20 znaków
      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);
       // cena
      fic.writeDouble(art.prix);
       // stany magazynowe
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// koniec zapisu

     // ------------------------błąd
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// koniec błędu

}// koniec klasy

Poniższy program pozwala sprawdzić, czy poprzedni program zadziałał poprawnie.

4.10.6. Konwersja z formatu binarnego na tekst

Poniższy program odczytuje zawartość utworzonego wcześniej pliku binarnego data.bin i zapisuje ją w pliku tekstowym data.text. Jeśli wszystko przebiegnie pomyślnie, plik data.text powinien być identyczny z oryginalnym plikiem data.txt.

// zaimportowane klasy
import java.io.*;
import java.util.*;

public class test5{

// plik tekstowy --> plik binarny

     // struktura artykułu
    private static class article{
         // definiujemy strukturę
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//klasa artykułu

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

         // definiujemy plik binarny, w którym będą przechowywane artykuły
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","r");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
        }

         // dane są zapisywane w pliku tekstowym
    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);
        }

      // plik .bin --> plik .text
    article art=new article();    // artykuł do utworzenia

       // przetwarzamy plik binarny
      int numRecord=0;
      long l=0;    // rozmiar pliku
      try{
          l=dataBin.length();
      } catch (IOException e){
          erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
      }
      long pos=0;    // bieżąca pozycja w pliku
      try{
          pos=dataBin.getFilePointer();
      } catch (IOException e){
          erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
      }

     // dopóki nie przekroczono końca pliku
        while(pos<l){
            // odczyt bieżącego rekordu i przetworzenie go
            numRecord++;
            try{
                lire(dataBin,art);
            } catch (Exception e){
                erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
            }
            affiche(art);

             // zapis odpowiedniego wiersza tekstu w dataTxt
         dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);

             // kontynuować?
            try{
                pos=dataBin.getFilePointer();
            } catch (IOException e){
                erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
            }
        }// koniec pętli while

       // koniec
      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);
      }

  }// koniec funkcji main

     // metoda odczytu
    public static void lire(RandomAccessFile fic, article art) throws IOException{
      // odczyt kodu
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nazwa
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // cena
        art.prix=fic.readDouble();
    // zapas
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// koniec zapisu

     // ---------------------wyświetla
    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);
    }// koniec wyświetlania

     // ------------------------błąd
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// koniec błędu
}// koniec klasy

Oto przykładowe wyniki działania:

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. Bezpośredni dostęp do rekordów

Ten ostatni program ilustruje możliwość bezpośredniego dostępu do rekordów w pliku binarnym. Wyświetla on rekord z pliku data.bin, którego numer jest przekazywany jako parametr, przy czym pierwszy rekord ma numer 1.

// zaimportowane klasy
import java.io.*;
import java.util.*;

public class test6{

// plik tekstowy --> plik binarny

     // struktura artykułu
    private static class article{
         // definiujemy strukturę
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//klasa artykułu

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

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

       // otwieramy plik binarny w trybie odczytu
    RandomAccessFile dataBin=null;
      try{
          dataBin=new RandomAccessFile("data.bin","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
      }

       // przechodzimy do żądanego rekordu
      try{
          dataBin.seek((numRecord-1)*40);
      } catch (Exception e){
          erreur("La fiche "+numRecord+" n'existe pas",23);
      }

       // odczytuje się ją
    article art=new article();
      try{
          lire(dataBin,art);
      } catch (Exception e){
          erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
      }

       // wyświetla się
      affiche(art);

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

  }// koniec funkcji main

    // metoda odczytu
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // odczyt kodu
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nazwa
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // cena
        art.prix=fic.readDouble();
    // zapas
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// koniec zapisu

     // ---------------------wyświetla
    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);
    }// koniec wyświetlania

     // ------------------------błąd
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// koniec błędu
}// koniec klasy

Oto przykłady wyników:

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. Korzystanie z wyrażeń regularnych

4.11.1. Pakiet java.util.regex

Pakiet java.util.regex umożliwia stosowanie wyrażeń regularnych. Pozwalają one na sprawdzanie formatu ciągu znaków. Dzięki temu można zweryfikować, czy ciąg znaków reprezentujący datę ma format dd/mm/rr. W tym celu stosuje się wzorzec i porównuje się ciąg znaków z tym wzorcem. W tym przykładzie znaki j, m i a muszą być cyframi. Szablon prawidłowego formatu daty to zatem „\d\d/\d\d/\d\d”, gdzie symbol \d oznacza cyfrę. Symbole, których można używać w szablonie, są następujące (dokumentacja Microsoft):

Znak
Opis
\
Oznacza następujący znak jako znak specjalny lub literał. Na przykład „n” odpowiada znakowi „n”. „\n” odpowiada znakowi nowego wiersza. Sekwencja „\\” odpowiada znakowi „\”, natomiast „\(" odpowiada znakowi „(”.
^
Odpowiada początkowi wprowadzania tekstu.
$
Odpowiada końcowi wpisu.
*
Odpowiada poprzedniemu znakowi zero lub więcej razy. Tak więc „zo*” odpowiada „z” lub „zoo”.
+
Odpowiada poprzedniemu znakowi jeden lub więcej razy. Na przykład „zo+” odpowiada „zoo”, ale nie odpowiada „z”.
?
Odpowiada poprzedniemu znakowi zero lub jeden raz. Na przykład „a?ve?” odpowiada „ve” w słowie „lever”.
.
Odpowiada dowolnemu pojedynczemu znakowi, z wyjątkiem znaku nowej linii.
(wzór)
Wyszukuje ciąg modèle i zapisuje dopasowanie. Odpowiedni podciąg można wyodrębnić z uzyskanej kolekcji Matches za pomocą elementu [0]...[n]. Aby znaleźć dopasowania zawierające znaki w nawiasach ( ), należy użyć znaków „\(" lub „\)”.
x|y
Odpowiada albo x, albo y. Na przykład „z|foot" correspond à "z" ou à "foot". "(z|f)oo” odpowiada „zoo” lub „foo”.
{n}
n jest liczbą całkowitą nieujemną. Odpowiada dokładnie n razy danemu znakowi. Na przykład „o{2}” nie odpowiada „o” w „Bob”, ale dwóm pierwszym „o” w „fooooot”.
{n,}
n jest liczbą całkowitą nieujemną. Odpowiada co najmniej n-krotności danego znaku. Na przykład „o{2,}” nie odpowiada literze „o” w słowie „Bob”, ale wszystkim literom „o” w słowie „fooooot”. „o{1,}” jest równoważne „o+”, a „o{0,}” jest równoważne „o*”.
{n,m}
m i n są liczbami całkowitymi nieujemnymi. Odpowiada co najmniej n i co najwyżej m wystąpieniom znaku. Na przykład „o{1,3}” odpowiada trzem pierwszym „o” w słowie „foooooot”, a „o{0,1}” odpowiada „o?”.
[xyz]
Zbiór znaków. Odpowiada jednemu z podanych znaków. Na przykład „[abc]” odpowiada znakowi „a” w słowie „plat”.
[^xyz]
Zbiór znaków ujemnych. Odpowiada każdemu znakowi, który nie został wymieniony. Na przykład „[^abc]” odpowiada literze „p” w słowie „plat”.
[a-z]
Zakres znaków. Odpowiada dowolnemu znakowi z podanego zakresu. Na przykład „[a-z]” odpowiada dowolnemu małej literze alfabetu od „a” do „z”.
[^m-z]
Zakres znaków ujemny. Odpowiada dowolnemu znakowi, który nie występuje w podanej serii. Na przykład „[^m-z]” odpowiada dowolnemu znakowi, który nie występuje między „m” a „z”.
\b
Odpowiada granicy wyznaczającej słowo, czyli pozycji między słowem a spacją. Na przykład „er\b” odpowiada „er” w słowie „lever”, ale nie odpowiada „er” w słowie „verbe”.
\B
Odpowiada granicy, która nie reprezentuje słowa. „en*t\B” odpowiada „ent” w słowie „bien entendu”.
\d
Odpowiada znakowi reprezentującemu cyfrę. Odpowiada [0-9].
\D
Odpowiada znakowi, który nie jest cyfrą. Odpowiada ciągowi [^0-9].
\f
Odpowiada znakowi końca strony.
\n
Odpowiada znakowi nowego wiersza.
\r
Odpowiada znakowi powrotu karetki.
\s
Odpowiada dowolnemu znakowi spacji, w tym spacji, tabulacji, znakowi końca strony itp. Odpowiada ciągowi znaków „[ \f\n\r\t\v]”.
\S
Odpowiada dowolnemu znakowi niebędącemu spacją. Odpowiada „[^ \f\n\r\t\v]”.
\t
Odpowiada znakowi tabulacji.
\v
Odpowiada znakowi pionowego tabulacji.
\w
Odpowiada dowolnemu znakowi reprezentującemu słowo, w tym znakowi podkreślenia. Odpowiada ciągowi znaków „[A-Za-z0-9_]”.
\W
Odpowiada dowolnemu znakowi, który nie stanowi słowa. Odpowiada ciągowi znaków „[^A-Za-z0-9_]”.
\num
Odpowiada num, gdzie num jest dodatnią liczbą całkowitą. Odnosi się do zapisanych reguł dopasowania. Na przykład „(.)\1” odpowiada dwóm identycznym, następującym po sobie znakom.
\n
Odpowiada n, gdzie n jest ośmiokrotną wartością escape. Ośmiokrotne wartości escape muszą składać się z 1, 2 lub 3 cyfr. Na przykład zarówno „\11”, jak i „\011” odpowiadają znakowi tabulacji. „\0011” jest równoważne „\001” i „1”. Oktalowe wartości escape nie mogą przekraczać 256. W przeciwnym razie w wyrażeniu brane pod uwagę będą tylko dwie pierwsze cyfry. Umożliwia używanie kodów ASCII w wyrażeniach regularnych.
\xn
Odpowiada n, gdzie n jest szesnastkową wartością escape. Wartości ucieczkowe w systemie szesnastkowym muszą obowiązkowo składać się z dwóch cyfr. Na przykład „\x41” odpowiada literze „A”. „\x041” jest równoważne „\x04” i „1”. Umożliwia stosowanie kodów ASCII w wyrażeniach regularnych.

Element w szablonie może występować w jednej lub wielu instancjach. Rozważmy kilka przykładów dotyczących symbolu \d, który reprezentuje jedną cyfrę:

szablon
znaczenie
\d
cyfra
\d?
0 lub 1 cyfra
\d*
0 lub więcej cyfr
\d+
1 lub więcej cyfr
\d{2}
2 cyfry
\d{3,}
co najmniej 3 cyfry
\d{5,7}
od 5 do 7 cyfr

Wyobraźmy sobie teraz wzorzec, który może opisać oczekiwany format ciągu znaków:

poszukiwany ciąg znaków
model
data w formacie dd/mm/rr
\d{2}/\d{2}/\d{2}
godzina w formacie hh:mm:ss
\d{2}:\d{2}:\d{2}
liczba całkowita bez znaku
\d+
sekwencja spacji, która może być pusta
\s*
liczba całkowita bez znaku, przed którą lub po której mogą występować spacje
\s*\d+\s*
liczba całkowita, która może być ze znakiem i może być poprzedzona lub następować po spacjach
\s*[+|-]?\s*\d+\s*
liczba rzeczywista bez znaku, przed którą lub po której mogą występować spacje
\s*\d+(.\d*)?\s*
liczba rzeczywista, która może być ze znakiem i może być poprzedzona lub następowana przez spacje
\s*[+|]?\s*\d+(.\d*)?\s*
ciąg znaków zawierający słowo „juste”
\bjuste\b
  

Można określić, w którym miejscu ciągu ma być wyszukiwany wzorzec:

wzór
znaczenie
^wzorzec
wzorzec rozpoczyna ciąg
wzór$
szablon kończy ciąg
^wzorzec$
szablon rozpoczyna i kończy ciąg
wzorzec
wzorzec jest wyszukiwany w całym ciągu, zaczynając od jego początku.
wyszukiwany ciąg
wzorzec
ciąg kończący się wykrzyknikiem
!$
ciąg kończący się kropką
\.$
ciąg znaków rozpoczynający się sekwencją //
^//
ciąg zawierający tylko jedno słowo, po którym mogą następować lub przed którym mogą występować spacje
^\s*\w+\s*$
ciąg zawierający dwa słowa, po których mogą następować lub przed którymi mogą występować spacje
^\s*\w+\s*\w+\s*$
ciąg zawierający słowo „secret”
\bsecret\b

Podzbiory wzorca można „wyodrębnić”. Dzięki temu nie tylko można sprawdzić, czy ciąg znaków odpowiada konkretnemu wzorcowi, ale także wyodrębnić z tego ciągu elementy odpowiadające podzbiorom wzorca, które zostały ujęte w nawiasy. Jeśli więc analizujemy ciąg zawierający datę dd/mm/rr i chcemy dodatkowo wyodrębnić elementy dd, mm, rr z tej daty, użyjemy wzorca (\d\d)/(\d\d)/(\d\d).

4.11.2. Sprawdzanie, czy ciąg znaków odpowiada podanemu wzorcowi

Klasa Pattern umożliwia sprawdzenie, czy ciąg znaków odpowiada podanemu wzorcowi. W tym celu stosuje się metodę statyczną

boolean Matches(String modèle, String chaine)

gdzie: modèle – wzorzec do sprawdzenia, chaine – ciąg znaków do porównania ze wzorcem. Wynikiem jest wartość logiczna true, jeśli ciąg znaków odpowiada wzorcowi, a false w przeciwnym razie.

Oto przykład:

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

// obsługa wyrażeń regularnych
public class regex1 {
  public static void main(String[] args){
    // wyrażenie regularne jako wzorzec
    String modèle1="^\\s*\\d+\\s*$";
    // porównanie wzorca z przykładem
    String exemplaire1="  123  ";
    if (Pattern.matches(modèle1,exemplaire1)){
      affiche("["+exemplaire1 + "] correspond au modèle ["+modèle1+"]");
    }else{
      affiche("["+exemplaire1 + "] ne correspond pas au modèle ["+modèle1+"]");
    }//if
    String exemplaire2="  123a  ";
    if (Pattern.matches(modèle1,exemplaire2)){
      affiche("["+exemplaire2 + "] correspond au modèle ["+modèle1+"]");
    }else{
      affiche("["+exemplaire2 + "] ne correspond pas au modèle ["+modèle1+"]");
    }//if
  }//main

  public static void affiche(String msg){
    System.out.println(msg);
  }//wyświetla
}//klasa

oraz wyniki wykonania:

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

Należy zauważyć, że w wzorcu „^\s*\d+\s*$” znak \ musi być podwójny ze względu na specyficzną interpretację tego znaku przez język Java. Piszemy zatem: String wzorzec1 = „^\\s*\\d+\\s*$”;

4.11.3. Wyszukiwanie wszystkich elementów ciągu znaków pasujących do wzorca

Rozważmy wzorzec „\d+” oraz ciąg znaków „ 123 456 789 ”. Wzorzec ten występuje w trzech różnych miejscach ciągu. Klasy Pattern i Matcher umożliwiają pobranie poszczególnych wystąpień wzorca w ciągu znaków. Klasa Pattern jest klasą obsługującą wyrażenia regularne. Wyrażenie regularne używane więcej niż raz wymaga „kompilacji”. Przyspiesza to wyszukiwanie wzorca w ciągach znaków. Zadanie to wykonuje statyczna metoda compile:

public static Pattern compile(String regex)

Jako parametr przyjmuje ona ciąg znaków zawierający wzorzec i zwraca obiekt typu Pattern. Aby porównać wzorzec obiektu typu Pattern z ciągiem znaków, wykorzystuje się klasę Matcher. Klasa ta umożliwia porównanie wzorca z ciągiem znaków. Na podstawie obiektu typu Pattern można uzyskać obiekt typu Matcher za pomocą metody matcher:

public Matcher matcher(CharSequence input)

input to ciąg znaków, który ma zostać porównany z wzorcem.

Aby więc porównać wzorzec „\d+” z ciągiem „ 123 456 789 ”, można utworzyć obiekt Matcher w następujący sposób:

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

Na podstawie poprzedniego obiektu résultats będziemy mogli pobrać różne wystąpienia wzorca w ciągu znaków. W tym celu wykorzystujemy metody suivantes klasy Matcher:

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

Metoda find wyszukuje w analizowanym ciągu pierwsze wystąpienie wzorca. Drugie wywołanie metody find wyszuka kolejne wystąpienie. I tak dalej. Metoda zwraca wartość true, jeśli znajdzie wzorzec, a w przeciwnym razie – false. Fragment ciągu odpowiadający ostatniemu wystąpieniu znalezionemu przez metodę find uzyskuje się za pomocą metody group, a jego pozycję – za pomocą metody start. Tak więc, kontynuując poprzedni przykład i chcąc wyświetlić wszystkie wystąpienia wzorca „\d+” w ciągu „ 123 456 789 ”, należy napisać:

while(résultats.find()){
    System.out.println("séquence " + résultats.group() + " trouvée en position " + résultats.start());
    }//while

Metoda reset pozwala zresetować obiekt Matcher do początku ciągu porównywanego z wzorcem. Dzięki temu metoda find ponownie znajdzie pierwsze wystąpienie wzorca.

Oto pełny przykład:

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

// obsługa wyrażeń regularnych
public class regex2 {
  public static void main(String[] args){
    // wiele wystąpień wzorca w egzemplarzu
    String modèle2="\\d+";
    Pattern regex2=Pattern.compile(modèle2);
    String exemplaire3="  123  456 789";
    // wyszukiwanie wystąpień wzorca w egzemplarzu
    Matcher matcher2=regex2.matcher(exemplaire3);
    while(matcher2.find()){
      affiche("séquence " + matcher2.group() + " trouvée en position " + matcher2.start());
    }//while
  }//Główna

  public static void affiche(String msg){
    System.out.println(msg);
  }//wyświetla
}//klasa

Wyniki wykonania:

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. Pobieranie części wzorca

Podzbiory wzorca można „wyodrębnić”. Dzięki temu nie tylko można sprawdzić, czy ciąg znaków odpowiada konkretnemu wzorcowi, ale także wyodrębnić z tego ciągu elementy odpowiadające podzbiorom wzorca, które zostały ujęte w nawiasy. Jeśli więc analizujemy ciąg znaków zawierający datę dd/mm/rr i chcemy dodatkowo wyodrębnić elementy dd, mm, rr z tej daty, użyjemy wzorca (\d\d)/(\d\d)/(\d\d).

Rozważmy następujący przykład:

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

// obsługa wyrażeń regularnych
public class regex3 {
  public static void main(String[] args){
    // pobieranie elementów z szablonu
    String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
    Pattern regex3=Pattern.compile(modèle3);
    String exemplaire4="Il est 18:05:49";
    // weryfikacja szablonu
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // egzemplarz odpowiada szablonowi
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // wyświetlanie grup
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//for
      }else{
        // egzemplarz nie odpowiada szablonowi
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }
    }//Główna

    public static void affiche(String msg){
      System.out.println(msg);
    }//wyświetla
}//klasyfikuje

Uruchomienie tego programu daje następujące wyniki:

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

Nowość znajduje się w następującym fragmencie kodu:

    // weryfikacja wzorca
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // egzemplarz odpowiada wzorcowi
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // wyświetla się lista grup
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//dla
      }else{
        // egzemplarz nie odpowiada szablonowi
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }

Ciąg znaków exemplaire4 jest porównywany z wzorcem regex3 przy użyciu metody find. W ciągu znaków exemplaire4 znaleziono wówczas wystąpienie wzorca regex3. Jeśli wzorzec zawiera podzbiory ujęte w nawiasy, są one dostępne dzięki różnym metodom klasy Matcher:


public int groupCount()
public String group(int group)
public int start(int group)

Metoda groupCount zwraca liczbę podzbiorów znalezionych w modelu, a group(i) – podzbiór o numerze i. Znajduje się on w ciągu znaków w pozycji określonej przez start(i). Tak więc w przykładzie:

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

Pierwsze wywołanie metody find znajdzie ciąg znaków 18:05:49 i automatycznie utworzy trzy podzbiory określone przez nawiasy w szablonie, odpowiednio 18, 05 i 49.

4.11.5. Program do nauki

Znalezienie wyrażenia regularnego, które pozwoli nam sprawdzić, czy dany ciąg znaków pasuje do określonego wzorca, bywa prawdziwym wyzwaniem. Poniższy program pozwala poćwiczyć tę umiejętność. Wymaga on podania wzorca i ciągu znaków, a następnie wskazuje, czy ciąg ten pasuje do wzorca, czy nie.

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

// obsługa wyrażeń regularnych
public class regex4 {
  public static void main(String[] args){

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

    // obsługa błędów
    try{
      // użytkownik jest proszony o podanie wzorców i egzemplarzy, które mają zostać porównane z danym egzemplarzem
      while(true){
        // strumień danych wejściowych
        IN=new BufferedReader(new InputStreamReader(System.in));
        // wyświetla się prośba o podanie szablonu
        System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
        modèle=IN.readLine();
        // zakończone?
        if(modèle.trim().toLowerCase().equals("fin")) break;
        // tworzy się wyrażenie regularne
        regex=Pattern.compile(modèle);
        // pyta się użytkownika o egzemplarze do porównania z wzorcem
        while(true){
          System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
          chaine=IN.readLine();
          // zakończone?
          if(chaine.trim().toLowerCase().equals("fin")) break;
          // tworzymy obiekt dopasowujący
          résultats=regex.matcher(chaine);
          // wyszukuje się wystąpienia wzorca
          nbOccurrences=0;
          while(résultats.find()){
            // znaleziono wystąpienie
            nbOccurrences++;
            // wyświetla się
            System.out.println("J'ai trouvé la correspondance ["+résultats.group()
            +"] en position "+résultats.start());
            // wyświetlanie podelementów
            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));
              }//dla j
            }//if
            // następny ciąg
          }//while(résultats.find())
          // czy znaleziono co najmniej jedno wystąpienie?
          if(nbOccurrences==0){
            System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
          }//if
          // następny wzorzec
        }//while(true)
      }//while(true)
    }catch(Exception ex){
      // błąd
      System.err.println("Erreur : "+ex.getMessage());
      // koniec z błędem
      System.exit(1);
    }//try-catch
    // koniec
    System.exit(0);
  }//Main
}//klasa

Oto przykładowe wyniki:

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. Metoda split klasy Pattern

Rozważmy ciąg znaków składający się z pól oddzielonych ciągiem rozdzielającym wyrażonym za pomocą wyrażenia regularnego. Na przykład, jeśli pola są oddzielone znakiem , poprzedzonym lub następującym po dowolnej liczbie spacji, wyrażenie regularne modelujące ciąg rozdzielający pola brzmiałoby „\s*,\s*”. Metoda split klasy Pattern pozwala nam pobrać pola do tablicy:


public String[] split(CharSequence input)

Ciąg input jest rozkładany na pola, które są oddzielone separatorem odpowiadającym wzorcowi bieżącego obiektu Pattern. Aby pobrać pola z wiersza, w którym separatorem pól jest przecinek poprzedzony lub następujący po dowolnej liczbie spacji, należy wpisać:

    // jedna linia
    String ligne="abc  ,, def  , ghi";
    // szablon
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // podział wiersza na pola
    String[] champs=modèle.split(ligne);

Ten sam wynik można uzyskać za pomocą metody split z klasy String:


public String[] split(String regex)

Oto program testowy:

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

// obsługa wyrażeń regularnych
public class split1 {
  public static void main(String[] args){
    // jedna linia
    String ligne="abc  ,, def  , ghi";
    // szablon
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // podział wiersza na pola
    String[] champs=modèle.split(ligne);
    // wyświetlanie
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
    // inny sposób wykonania
    champs=ligne.split("\\s*,\\s*");
    // wyświetlanie
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
  }//Strona główna
}//klasa

Wyniki wykonania:

champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]

4.12. Ćwiczenia

4.12.1. Ćwiczenie 1

W systemie Unix programy są często wywoływane w następujący sposób:

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

gdzie -oi oznacza opcję, a vi – wartość powiązaną z tą opcją. Chcemy utworzyć klasę options, która umożliwiłaby analizę ciągu argumentów -o1 v1 v2 ... -o2 v3 v4 … w celu skonstruowania następujących elementów:

optionsValides
słownik (Hashtable), którego klucze stanowią prawidłowe opcje oi. Wartością powiązaną z kluczem oi jest wektor (Vector), którego elementami są wartości v1 v2 … powiązane z opcją -oi
optionsInvalides
słownik (Hashtable), którego kluczami są nieprawidłowe opcje oi. Wartością powiązaną z kluczem oi jest wektor (Vector), którego elementami są wartości v1, v2 … powiązane z opcją -oi
optionsSans
ciąg znaków (String) zawierający listę wartości vi niepowiązanych z żadną opcją
erreur
liczba całkowita równa 0, jeśli w wierszu argumentów nie ma błędów, w przeciwnym razie:
1: występują nieprawidłowe parametry wywołania
2: występują nieprawidłowe opcje
4: występują wartości niepowiązane z opcjami
Jeśli występuje kilka rodzajów błędów, wartości te sumują się.

Obiekt opcji można utworzyć na 4 różne sposoby:

public options (String arguments, String optionsAcceptables)

arguments
wiersz argumentów -o1 v1 v2 ... -o2 v3 v4 … do analizy
optionsAcceptables
lista dopuszczalnych opcji oi

Przykład wywołania: options opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");

W tym przypadku oba argumenty są ciągami znaków. Dopuszczalne są przypadki, w których ciągi te zostały podzielone na słowa umieszczone w tablicy ciągów znaków. Wymaga to trzech dodatkowych konstruktorów:

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

Klasa options będzie miała następujący interfejs (metody dostępowe):

public Hashtable getOptionsValides()

zwraca odwołanie do tablicy optionsValides utworzonej podczas tworzenia obiektu opcji

public Hashtable getOptionsInvalides()

zwraca odwołanie do tablicy optionsInvalides utworzonej podczas tworzenia obiektu opcji

public String getOptionsSans()

zwraca odwołanie do ciągu znaków optionsSans utworzonego podczas tworzenia obiektu opcji

public int getErreur()

zwraca wartość atrybutu „błąd” utworzoną podczas tworzenia obiektu opcji

public String toString()

jeśli nie ma błędu, wyświetla wartości atrybutów optionsValides, optionsInvalides, optionsSans, w przeciwnym razie wyświetla numer błędu.

Oto przykładowy program:


import java.io.*;
//opcje importu;

public class test1{
  
  
  public static void main (String[] arg){
    
    // otwarcie strumienia wejściowego
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    // odczyt argumentów konstruktora 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));
    }// koniec pętli while
  }//koniec funkcji main


public static void affiche(Exception e){
    System.err.println("Erreur : "+e);
  }

}//koniec klasy

Niektóre wyniki:

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. Ćwiczenie 2

Chcemy utworzyć klasę **stringtovector**, umożliwiającą przeniesienie zawartości obiektu *String do obiektu *Vector. Klasa ta będzie pochodną klasy *Vector*:

class stringtovector extends Vector

i miałby następujący kod źródłowy:


    private void stringtovector(String S, String separateur, int[] tChampsVoulus,
            boolean strict){
        
        // tworzy wektor zawierający pola ciągu S
        // który składa się z pól oddzielonych separatorem
        // jeśli separator = null, ciąg znaków tworzy tylko jedno pole
        // pożądane są tylko te pola, których indeksy znajdują się w tablicy tChampsVoulus 
        // są pożądane. Indeksy zaczynają się od 1
        // jeśli tChampsvoulus = null lub ma zerową długość, pobierane są wszystkie pola
        // jeśli strict=prawda, wszystkie wymagane pola muszą być obecne

Klasa miałaby następujący atrybut prywatny:

        private int erreur;

Atrybut ten jest ustalany przez poprzedni konstruktor z następującymi wartościami:

0: tworzenie przebiegło pomyślnie

4: brakuje niektórych wymaganych pól, mimo że strict=true

Klasa będzie miała również dwie metody:

    public int getErreur()

, która zwraca wartość prywatnego atrybutu erreur.

    public String identite(){

która wyświetla wartość obiektu w postaci (błąd, element 1, element 2, …) gdzie elementy i są elementami wektora utworzonego na podstawie ciągu znaków.

Program testowy mógłby wyglądać następująco:


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

Wyniki:

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

Kilka wskazówek:

  1. Aby podzielić ciąg znaków S na pola, należy użyć metody split z klasy String.
  2. Umieścić pola z S w słowniku D indeksowanym według numeru pola
  3. Pobierz ze słownika D tylko te pola, których klucz (indeks) znajduje się w tablicy tChampsVoulus.

4.12.3. Ćwiczenie 3

Chcemy dodać do klasy stringtovector następujący konstruktor:


public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
        
        // tworzy wektor zawierający pola z ciągu znaków S
        // który składa się z pól oddzielonych separatorem
        // jeśli separator=null, ciąg znaków tworzy tylko jedno pole
        // pożądane są tylko te pola, których indeksy znajdują się w sChampsVoulus
        // indeksy zaczynają się od 1
        // jeśli sChampsvoulus = null lub „”, pobierane są wszystkie pola
        // jeśli strict=true, wszystkie wymagane pola muszą być obecne

Lista żądanych pól znajduje się zatem w ciągu znaków (String), a nie w tablicy liczb całkowitych (int[]). Prywatnemu atrybutowi klasy o nazwie erreur można przypisać nową wartość:

2: ciąg indeksów wybranych pól jest nieprawidłowy

Oto przykładowy program:


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

Niektóre wyniki:

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

Kilka wskazówek:

  1. Należy powrócić do przypadku poprzedniego konstruktora, przenosząc pola ciągu znaków sChampsVoulus do tablicy liczb całkowitych. W tym celu należy podzielić ciąg sChampsVoulus na pola za pomocą obiektu StringTokenizer, którego atrybut countTokens poda liczbę uzyskanych pól. Następnie można utworzyć tablicę liczb całkowitych o odpowiednim rozmiarze i wypełnić ją uzyskanymi polami.
  2. Aby sprawdzić, czy pole jest liczbą całkowitą, należy użyć metody Integer.parseInt w celu przekształcenia pola w liczbę całkowitą oraz obsłużyć wyjątek, który zostanie wygenerowany, gdy konwersja ta będzie niemożliwa.

4.12.4. Ćwiczenie 4

Chcemy utworzyć klasę **filetovector,* umożliwiającą przeniesienie zawartości pliku tekstowego do obiektu *Vector. Klasa ta będzie pochodną klasy *Vector*:

class filetovector extends Vector

i posiadałaby następujący konstruktor:


    // --------------------- konstruktor 
    public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
        
        // tworzy wektor zawierający wiersze z pliku tekstowego nomFichier
        // wiersze składają się z pól oddzielonych separatorem
        // jeśli separator=null, wiersz składa się tylko z jednego pola
        // pożądane są tylko te pola, których indeksy znajdują się w tChampsVoulus
        // indeksy zaczynają się od 1
        // jeśli tChampsvoulus = null lub jest puste, pobierane są wszystkie pola
        // jeśli strict=prawda, wszystkie wymagane pola muszą być obecne
        // jeśli tak nie jest, wiersz nie jest zapisywany, a jego indeks
        // jest umieszczany wektorze lignesErronees
        // puste wiersze są ignorowane
        // podobnie jak wiersze zaczynające się od tagCommentaire, jeśli tagCommentaire != null

Klasa miałaby następujące atrybuty prywatne:

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

Atrybut erreur jest ustawiany przez poprzedni konstruktor z następującymi wartościami:

0: operacja przebiegła pomyślnie

1: nie udało się otworzyć pliku, który ma zostać przetworzony

4: brakuje niektórych wymaganych pól, mimo że strict=true

8: podczas przetwarzania pliku wystąpił błąd wejścia/wyjścia

Atrybut lignesErronees jest wektorem, którego elementami są numery błędnych wierszy w postaci ciągu znaków. Wiersz jest błędny, jeśli nie może dostarczyć wymaganych pól, mimo że strict=true.

Klasa będzie posiadała również dwie metody:

    public int getErreur()

która zwraca wartość atrybutu prywatnego erreur.

    public String identite(){

który wyświetla wartość obiektu w postaci (błąd, element 1, element 2 …, (l1, l2, …)), gdzie elementy i są elementami wektora utworzonego na podstawie pliku, a li to numery wierszy zawierających błędy.

Oto przykładowy test:


import java.io.*;
//import filetovector;

public class test2{
  public static void main(String arg[]){
    int[] T1={1,3};
    System.out.println(new filetovector("data.txt",":",T1,false,"#").identite());
    System.out.println(new filetovector("data.txt",":",T1,true,"#").identite());
    System.out.println(new filetovector("data.txt","",T1,false,"#").identite());
    System.out.println(new filetovector("data.txt",null,T1,false,"#").identite());
    int[] T2=null;
    System.out.println(new filetovector("data.txt",":",T2,false,"#").identite());
    System.out.println(new filetovector("data.txt",":",T2,false,"").identite());
    int[] T3=new int[0];
    System.out.println(new filetovector("data.txt",":",T3,false,null).identite());
  }
}

Wyniki wykonania:

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

Kilka wskazówek

  1. Plik tekstowy jest przetwarzany wiersz po wierszu. Wiersz jest dzielony na pola za pomocą omówionej wcześniej klasy **stringtovector**.

  2. Elementy wektora utworzonego na podstawie pliku tekstowego są zatem obiektami typu stringtovector.

  3. Metoda identite klasy filetovector może wykorzystywać metodę stringtovector.identite() do wyświetlania swoich elementów, a także metodę Vector.toString() do wyświetlania numerów ewentualnych błędnych wierszy.

4.12.5. Ćwiczenie 5

Chcemy dodać do klasy **filetovector** następujący konstruktor:


    public filetovector(String nomFichier, String separateur, String sChampsVoulus,
            boolean strict, String tagCommentaire){
        
        // tworzy wektor zawierający wiersze z pliku tekstowego nomFichier
        // wiersze składają się z pól oddzielonych separatorem
        // jeśli separator = null, wiersz stanowi jedno pole
        // pożądane są tylko te pola, których indeksy znajdują się w tChampsVoulus
        // indeksy zaczynają się od   1
        // jeśli sChampsvoulus = null lub jest puste, pobierane są wszystkie pola
        // jeśli strict=prawda, wszystkie wymagane pola muszą być obecne
        // jeśli tak nie jest, wiersz nie jest zapisywany, a jego indeks
        // jest umieszczany wektorze lignesErronees
        // puste wiersze są ignorowane
        // podobnie jak wiersze zaczynające się od tagCommentaire, jeśli tagCommentaire != null

Lista indeksów żądanych pól znajduje się teraz w ciągu znaków (String) zamiast w tablicy liczb całkowitych.

Prywatny atrybut erreur może przyjmować dodatkową wartość:

2: ciąg indeksów żądanych pól jest nieprawidłowy

Oto przykładowy test:


import java.io.*;
//import filetovector;

public class test1{
  public static void main(String arg[]){
    System.out.println(new filetovector("data.txt",":","1 3",false,"#").identite());
    System.out.println(new filetovector("data.txt",":","1 3",true,"#").identite());
    System.out.println(new filetovector("data.txt","","1 3",false,"#").identite());
    System.out.println(new filetovector("data.txt",null," 1 3",false,"#").identite());
    String S2=null;
    System.out.println(new filetovector("data.txt",":",S2,false,"#").identite());
    System.out.println(new filetovector("data.txt",":",S2,false,"").identite());
    String S3="";
    System.out.println(new filetovector("data.txt",":",S3,false,null).identite());
  }
}

Wyniki:

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

Kilka wskazówek

  1. Przekształcimy ciąg znaków sChampsVoulus w tablicę liczb całkowitych tChampVoulus, aby powrócić do przypadku poprzedniego konstruktora.