Skip to content

4. Поширені класи

У цьому розділі ми розглянемо низку поширених класів Java. Вони мають численні атрибути, методи та конструктори. Щоразу ми розглядаємо лише невелику частину цих класів. Детальну інформацію про них можна знайти в довідці Java, яку ми зараз представимо.

4.1. Документація

Якщо ви встановили JDK від Sun у папку <jdk>, документація доступна в папці <jdk>\docs:

Image

Іноді трапляється, що є файл jdk, але без документації. Її можна знайти на сайті Sun http://www.sun.com. У папці docs міститься файл index.html, який є відправною точкою для довідки щодо JDK:

Image

Image

Посилання API & Language, наведене вище, надає доступ до класів Java. Посилання Demos/Tutorials є особливо корисним для ознайомлення з прикладами програм на Java. Перейдемо за посиланням API & Language:

Image

Перейдемо за посиланням «Java 2 Platform» API:

Image

Ця сторінка є справжньою відправною точкою документації щодо класів. Можна створити ярлик на ній, щоб мати швидкий доступ. URL знаходиться за адресою <jdk>\docs\api\index.html. Тут містяться посилання на сотні класів Java з JDK. На початку основною складністю є розуміння того, що саме роблять ці різні класи. Тому спочатку ця довідка буде корисною лише в тому випадку, якщо ви знаєте назву класу, про який хочете отримати інформацію. Також можна орієнтуватися на назви класів, які зазвичай вказують на їхню роль.

Візьмемо приклад і пошукаємо інформацію про клас Vector, який реалізує динамічний масив. Достатньо знайти у списку класів у лівій панелі посилання на клас Vector:

Image

і натиснути на посилання, щоб переглянути визначення класу:

Image

Там можна знайти

  • ієрархію, до якої належить цей клас, у даному випадку java.util.Vector
  • перелік полів (атрибутів) класу
  • перелік конструкторів
  • перелік методів

Далі ми наводимо різні класи. Просимо читача систематично перевіряти повне визначення використовуваних класів.

4.2. Тестові класи

У наведених нижче прикладах іноді використовуються класи personne, enseignant. Нагадаємо їхні визначення.

public class personne{
    // прізвище, ім’я, вік
  private String prenom;
  private String nom;
  private int age;

  // конструктор 1
  public personne(String P, String N, int age){
    this.prenom=P;
    this.nom=N;
    this.age=age;
  }

   // конструктор 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+")";
  }

  // додаткові елементи
  public String getPrenom(){
    return prenom;
  }
  public String getNom(){
    return nom;
  }
  public int getAge(){
    return age;
  }

  //модифікатори
  public void setPrenom(String P){
    this.prenom=P;
  }
  public void setNom(String N){
    this.nom=N;
  }
  public void setAge(int age){
    this.age=age;
  }
}

Клас enseignant походить від класу personne і визначається наступним чином:

class enseignant extends personne{
// атрибути
  private int section;

    // конструктор
  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+")";    
  }
}

Ми також будемо використовувати клас etudiant, похідний від класу personne і визначений наступним чином:


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. Клас String

Клас String представляє символьні рядки. Нехай nom — це змінна, що містить символьний рядок:

String nom;

nom — це посилання на об’єкт, який ще не ініціалізовано. Його можна ініціалізувати двома способами:

nom="cheval" або nom=new String("cheval")

Обидва методи є еквівалентними. Якщо пізніше записати nom="poisson", то nom буде посилатися на новий об’єкт. Старий об’єкт String("cheval") буде втрачено, а пам’ять, яку він займав, буде звільнено.

Клас String має багато атрибутів і методів. Ось деякі з них:

public char charAt(int i)
повертає символ i з рядка, де перший символ має індекс 0. Таким чином, String("cheval").charAt(3) дорівнює 'v'
public int compareTo(chaine2)
chaine1.compareTo(chaine2) порівнює chaine1 з chaine2 і повертає 0, якщо chaine1 = chaine2, 1 — якщо chaine1 > chaine2, -1 — якщо chaine1 < chaine2
public boolean equals(Object anObject)
chaine1.equals(рядок2) повертає true, якщо рядок1 = рядок2, інакше — false
public String toLowerCase()
chaine1.toLowerCase() перетворює рядок1 на малі літери
public String toUpperCase()
chaine1.toUpperCase() перетворює рядок1 на великі літери
public String trim()
chaine1.trim() видаляє пробіли на початку та в кінці рядка1
public String substring(int beginIndex, int endIndex)
String("chapeau").subString(2,4) повертає рядок "ape"
public char[] toCharArray()
дозволяє помістити символи рядка в масив символів
int length()
кількість символів у рядку
int indexOf(String chaine2)
повертає індекс першої позиції chaine2 у поточному рядку або -1, якщо chaine2 відсутній
int indexOf(String chaine2, int startIndex)
повертає позицію першого входження chaine2 у поточний рядок або -1, якщо chaine2 відсутнє. Пошук починається з символу № startIndex.
int lastIndexOf(String chaine2)
повертає останню позицію chaine2 у поточному рядку або -1, якщо chaine2 відсутній
boolean startsWith(String chaine2)
повертає «true», якщо поточний рядок починається з chaine2
boolean endsWith(String chaine2)
повертає «true», якщо поточний рядок закінчується на chaine2
boolean matches(String regex)
повертає true, якщо поточний рядок відповідає регулярному виразу regex.
String[] split(String regex)
Поточний рядок складається з полів, розділених символьним ланцюжком, що моделюється регулярним виразом regex. Метод split дозволяє отримати поля у вигляді масиву.
String replace(char oldChar, char newChar)
замінює в поточному рядку символ oldChar на символ newChar.

Ось приклад програми:

// імпорти
import java.io.*;

public class string1{
    // демонстраційний клас
    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]+"]");
        }//для
        affiche("(\"  abc  \").trim()=["+"  abc  ".trim()+"]");
    }//Main

  // виводить
    public static void affiche(String msg){
        // виводить повідомлення
        System.out.println(msg);
    }//відображає
}//клас

та отримані результати:

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. Клас Vector

Вектор — це динамічний масив, елементи якого є посиланнями на об’єкти. Отже, це масив об’єктів, розмір якого може змінюватися з часом, що неможливо у випадку зі статичними масивами, з якими ми мали справу досі. Ось деякі поля, конструктори та методи цього класу:

public Vector()
створює порожній вектор
public final int size()
кількість елементів у векторі
public final void addElement(Object obj)
додає об’єкт, на який вказує obj, до вектора
public final Object elementAt(int index)
посилання на об’єкт № index у масиві — індекси починаються з 0
public final Enumeration elements()
набір елементів вектора у вигляді переліку
public final Object firstElement()
посилання на перший елемент масиву
public final Object lastElement()
посилання на останній елемент вектора
public final boolean isEmpty()
повертає true, якщо масив порожній
public final void removeElementAt(int index)
видаляє елемент з індексом index
public final void removeAllElements()
очищає масив від усіх елементів
public final String toString()
повертає ідентифікаційний рядок масиву

Ось тестова програма:


// імпортовані класи
import java.util.*;

public class test1{

// головна програма main — static — метод класу

  public static void main(String arg[]){

// створення об’єктів — екземплярів класів
    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());

// поліморфізм
    personne p2=(personne)en;
    System.out.println("p2="+p2.toString());
    personne p3=(personne)et;
    System.out.println("p3="+p3.toString());

// вектор
    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());
    }
} // кінець main
}// кінець класу

Скомпілюємо цю програму:

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

Запустимо файл 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)

Надалі ми більше не будемо повторювати процес компіляції та виконання тестових програм. Достатньо просто повторити те, що було зроблено вище.

4.5. Клас ArrayList

Клас ArrayList аналогічний класу Vector. Він істотно відрізняється від нього лише в тому випадку, коли його одночасно використовують кілька потоків виконання. Методи синхронізації потоків для доступу до Vector або ArrayList відрізняються. У всіх інших випадках можна використовувати будь-який із них. Ось деякі поля, конструктори та методи цього класу:

ArrayList()
створює порожній масив
int size()
кількість елементів масиву
void add(Object obj)
додає об'єкт, на який вказує obj, до масиву
void add(int index, Object obj)
додає об’єкт, на який вказує obj, до масиву на позицію index
Object get(int index)
посилання на об’єкт № index у масиві — індекси починаються з 0
boolean isEmpty()
повертає true, якщо масив порожній
void remove(int index)
видаляє елемент з індексом index
void clear()
очищає масив від усіх елементів
Object[] toArray()
перетворює динамічний масив на звичайний масив
String toString()
повертає ідентифікаційний рядок масиву

Ось тестова програма:

// імпортовані класи
import java.util.*;

public class test1{

// головна програма main — static — метод класу

  public static void main(String arg[]){

// створення об’єктів-інстанцій класів
    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);

// поліморфізм
    personne p2=(personne)en;
    System.out.println("p2="+p2);
    personne p3=(personne)et;
    System.out.println("p3="+p3);

// вектор
    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);
    }
} // кінець main
}// кінець класу

Отримані результати такі самі, як і раніше.

4.6. Клас Arrays

Клас java.util.Arrays надає доступ до статичних методів, що дозволяють виконувати різні операції з масивами, зокрема сортування та пошук елементів. Ось декілька таких методів:

static void sort(масив)
сортує tableau, використовуючи для цього неявний порядок типів даних масиву — чисел або рядків.
static void sort (Object[] масив, Comparator C)
сортує масив tableau, використовуючи для порівняння елементів функцію порівняння C
static int binarySearch(масив, елемент)
повертає позицію élément у масиві tableau або значення <0 в іншому випадку. Масив має бути попередньо відсортований.
static int binarySearch(Object[] масив, Object елемент, Comparator C)
те саме, але використовує функцію порівняння C для порівняння двох елементів масиву.

Ось перший приклад:

import java.util.*;

public class sort2 implements Comparator{

   // внутрішній приватний клас
  private class personne{
    private String nom;
    private int age;
    public personne(String nom, int age){
      this.nom=nom; // ім'я особи
      this.age=age; // його вік
    }
     // отримати вік
    public int getAge(){
      return age;
    }
     // ідентифікатор особи
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // клас «особа»

   // виробник
  public sort2() {
     // масив осіб
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // сортування масиву осіб
    Arrays.sort(amis,this);
     // перевірка
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//конструктор

   // функція, що порівнює осіб
  public int compare(Object o1, Object o2){
    // повинна повертати
     // -1, якщо o1 «менше за» o2
     // 0, якщо o1 «дорівнює» o2
     // +1, якщо o1 «більше за» 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;
  }//порівнює

    // тестова функція
  public static void main(String[] arg){
    new sort2();
  }//main

}//клас

Розглянемо цю програму. Функція main створює об’єкт sort2. Конструктор класу sort2 має такий вигляд:

   // виробник
  public sort2() {
     // таблиця осіб
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // сортування списку осіб
    Arrays.sort(amis,this);
     // перевірка
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//розробник

Масив, який потрібно впорядкувати, — це масив об’єктів personne. Клас personne визначено як приватний (private) усередині класу sort2. Статичний метод sort класу Arrays не знає, як сортувати масив об’єктів personne, тому тут доводиться використовувати форму void sort(Object[] obj, Comparator C). Comparator — це інтерфейс, що визначає лише один метод:

    int compare(Object o1, Object o2)

і який повинен повертати 0: якщо o1=o2, -1: якщо o1 < o2, +1: якщо o1 > o2. У прототипі void sort(Object[] obj, Comparator C) другий аргумент C має бути об’єктом, що реалізує інтерфейс Comparator. У конструкторі sort2 було обрано поточний об’єкт this:

     // сортування масиву осіб
    Arrays.sort(amis,this);

Це змушує нас зробити дві речі:

  1. вказати, що клас sort2 реалізує інтерфейс Comparator
public class sort2 implements Comparator{
  1. написати функцію compare у класі sort2.

Вона має такий вигляд:

   // функція, що порівнює осіб
  public int compare(Object o1, Object o2){
    // повинна повертати
     // -1, якщо o1 «менше за» o2
     // 0, якщо o1 «дорівнює» o2
     // +1, якщо o1 «більше за» 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;
  }//порівнює

Щоб порівняти два об’єкти personne, тут використовується вік (можна було б використовувати ім’я).

Результати виконання такі:

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

Можна було б по-іншому реалізувати інтерфейс Comparator:

import java.util.*;

public class sort2 {

  // внутрішній приватний клас
  private class personne{
…….
  }; // клас «особа»

   // конструктор
  public sort2() {
     // масив осіб
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // сортування масиву осіб
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//порівняння
      }//клас
    );
     // перевірка
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }// конструктор

   // функція, що порівнює осіб
  public int compare1(Object o1, Object o2){
    // повинна повертати
     // -1, якщо o1 «менше за» o2
     // 0, якщо o1 «дорівнює» o2
     // +1, якщо o1 «більше за» 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

  // main
  public static void main(String[] arg){
    new sort2();
  }//main
}//клас

Інструкція сортування стала такою:

     // сортування масиву осіб
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//порівняння
      }//клас
    );

Другий параметр методу sort повинен бути об’єктом, що реалізує інтерфейс Comparator. Тут ми створюємо такий об’єкт за допомогою методу new java.util.Comparator(), а текст, що йде після {…}, визначає клас, об’єкт якого ми створюємо. Такий клас називають анонімним, оскільки він не має імені. У цьому анонімному класі, який повинен реалізовувати інтерфейс Comparator, ми визначаємо метод compare цього інтерфейсу. Цей метод просто викликає метод compare1 класу sort2. Таким чином, ми повертаємося до попереднього випадку.

Клас sort2 більше не реалізує інтерфейс Comparator. Тому його оголошення виглядає так:

public class sort2 {

Тепер ми перевіримо метод binarySearch класу Arrays на наступному прикладі:

import java.util.*;

public class sort4 {

  // внутрішній приватний клас
  private class personne{
      // атрибути
    private String nom;
    private int age;

        // конструктор
    public personne(String nom, int age){
      this.nom=nom; // ім'я особи
      this.age=age; // її вік
    }

     // отримати ім'я
    public String getNom(){
      return nom;
    }

     // отримати вік
    public int getAge(){
      return age;
    }
     // ідентифікатор особи
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // клас «особа»

   // конструктор
  public sort4() {
     // масив осіб
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};

     // компаратори
    java.util.Comparator comparateur1=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//порівнює
      }//клас
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//порівнює
      }//сортує
      ;          

     // сортування таблиці осіб
    Arrays.sort(amis,comparateur1);
    // перевірка
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
    // пошук
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);
  }//конструктор

   // функція, що порівнює осіб
  public int compare1(Object o1, Object o2){
    // повинна повертати
     // -1, якщо o1 «менше за» o2
     // 0, якщо o1 «дорівнює» o2
     // +1, якщо o1 «більше за» 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

   // функція, яка порівнює особу з ім’ям
  public int compare2(Object o1, Object o2){
    // o1 — це особа
     // o2 — це рядок, ім'я nom2 людини
     // повинна повернути
     // -1, якщо o1.nom «менше за» nom2
     // 0, якщо o1.nom «дорівнює» nom2
     // +1, якщо o1.nom «більше за» nom2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//порівняння2

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // пошук друга в масиві друзів
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // знайдено?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//шукає

  // рука
  public static void main(String[] arg){
    new sort4();
  }//рука
}//клас

Тут ми вчинили трохи інакше, ніж у попередніх прикладах. Два об’єкти Comparator, необхідні для методів sort та binarySearch, було створено та присвоєно змінним comparateur1 та comparateur2.

     // компаратори
    java.util.Comparator comparateur1=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//порівнює
      }//клас
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//порівняння
      }//клас
      ;          

У конструкторі sort4 двічі виконується дихотомічний пошук за таблицею amis:

     // пошук
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);

Метод cherche отримує всі параметри, необхідні для виклику методу binarySearch:

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // пошук друга в списку друзів
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // знайдено?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//шукає

Метод binarySearch працює з компаратором comparateur2, який, у свою чергу, викликає метод compare2 класу sort4. Метод rend повертає позицію шуканого імені в масиві, якщо воно існує, або число <0 в іншому випадку. Метод compare2 слугує для порівняння об’єкта типу personne з іменем типу String.

   // функція, яка порівнює особу з ім'ям
  public int compare2(Object o1, Object o2){
    // o1 — це особа
     // o2 — це рядок, ім'я nom2 людини
     // повинна повернути
     // -1, якщо o1.nom «менше, ніж» nom2
     // 0, якщо o1.nom «дорівнює» ім'я2
     // +1, якщо o1.nom «більше за» ім'я2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//порівняння2

На відміну від методу sort, метод binarySearch не приймає два об’єкти personne, а один об’єкт personne та один об’єкт String у такому порядку. Перший параметр — це елемент масиву amis, другий — ім’я особи, яку шукають.

4.7. Клас Enumeration

Enumeration є інтерфейсом, а не класом. Він має такі методи:

public abstract boolean hasMoreElements()
повертає true, якщо перелік ще містить елементи
public abstract Object nextElement()
повертає посилання на наступний елемент переліку

Як використовувати перелік? Зазвичай так:

    Enumeration e=…                // отримуємо об’єкт переліку
    while(e.hasMoreElements()){
        // використовуємо елемент e.nextElement()
    }

Ось приклад:


// імпортовані класи
import java.util.*;

public class test1{

// головна програма main — статичний метод класу

  public static void main(String arg[]){

// створення об’єктів — екземплярів класів
    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());

// поліморфізм
    personne p2=(personne)en;
    System.out.println("p2="+p2.toString());
    personne p3=(personne)et;
    System.out.println("p3="+p3.toString());

// вектор
    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());
    }

// перелік
    Enumeration E=V.elements();
    i=0;
    while(E.hasMoreElements()){
      p2=(personne) E.nextElement();
      System.out.println("V["+i+"]="+p2.toString());
      i++;
    }
   }// кінець main
}//кінець класу

Отримуємо такі результати:

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. Клас Hashtable

Клас Hashtable дозволяє реалізувати словник. Словник можна розглядати як таблицю з двома стовпцями:

ключ
значення
ключ1
значення1
ключ2
значення2
..
...

Ключі є унікальними, c.a.d. Тобто не може бути двох однакових ключів. Основні методи та властивості класу Hashtable такі:

public Hashtable()
конструктор — створює порожній словник
public int size()
кількість елементів у словнику — кожен елемент є парою (ключ, значення)
public Object put(Object key, Object value)
додає пару (ключ, значення) до словника
public Object get(Object key)
повертає об’єкт, пов’язаний із ключем key або null, якщо ключ key не існує
public boolean containsKey(Object key)
повертає true, якщо ключ key існує у словнику
public boolean contains(Object value)
true, якщо значення value існує у словнику
public Enumeration keys()
повертає ключі словника у вигляді переліку
public Object remove(Object key)
видаляє пару (ключ, значення), де ключ = key
public String toString()
ідентифікує словник

Ось приклад:


// імпортовані класи
import java.util.*;

public class test1{

// головна програма main — static — метод класу

  public static void main(String arg[]){

// створення об’єктів-інстанцій класів
    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());

// поліморфізм
    personne p2=(personne)en;
    System.out.println("p2="+p2.toString());
    personne p3=(personne)et;
    System.out.println("p3="+p3.toString());

// словник
    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++;
    }
  }//кінець main
}//кінець класу

Отримано такі результати:

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. Текстові файли

4.9.1. Запис

Щоб записати дані у файл, потрібно мати потік запису. Для цього можна використовувати клас FileWriter. Найчастіше використовуються такі конструктори:

FileWriter(String fileName)
створює файл з іменем fileName — після цього в нього можна записувати дані — існуючий файл з таким самим іменем буде перезаписано
FileWriter(String fileName,
boolean append)
те саме — існуючий файл з такою ж назвою можна використовувати, відкривши його в режимі додавання (append=true)

Клас FileWriter надає низку методів для запису в файл, успадкованих від класу Writer. Для запису в текстовий файл краще використовувати клас PrintWriter, найпоширеніші конструктори якого такі:

PrintWriter(Writer out)
аргумент має тип Writer, c.a.d — потік запису (у файл, у мережу тощо)
PrintWriter(Writer out, boolean autoflush)
те саме. Другий аргумент керує буферизацією рядків. Коли він має значення false (за замовчуванням), рядки, що записуються у файл, проходять через буфер у пам’яті. Коли буфер заповнюється, його вміст записується у файл. Це покращує доступ до диска. Однак іноді така поведінка є небажаною, зокрема під час запису в мережу.

Корисні методи класу PrintWriter такі:

void print(Type T)
записує дані типу T (String, int, …)
void println(Type T)
те саме, але з додаванням символу кінця рядка
void flush()
очищає буфер, якщо не перебуваємо в режимі автоочищення
void close()
закриває потік запису

Ось програма, яка записує кілька рядків у текстовий файл:

// імпорт
import java.io.*;

public class ecrire{
  public static void main(String[] arg){
    // відкриття файлу
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // запис у файл
    try{
      fic.println("Jean,Dupont,27");
      fic.println("Pauline,Garcia,24");
      fic.println("Gilles,Dumond,56");
    } catch (Exception e){
      Erreur(e,3);
    }
     // закриття файлу
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// кінець функції main

  private static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }//Помилка
}//клас

Файл out, отриманий у результаті виконання, має такий вигляд:

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

4.9.2. Читання

Щоб прочитати вміст файлу, потрібно мати потік читання, пов’язаний із цим файлом. Для цього можна використати клас FileReader та такий конструктор:

FileReader(String ім'яФайлу)
відкриває потік читання з вказаного файлу. Генерує виняток, якщо операція завершилася невдало.

Клас FileReader має низку методів для читання з файлу, успадкованих від класу Reader. Для читання рядків тексту з текстового файлу краще використовувати клас BufferedReader із таким конструктором:

BufferedReader(Reader in)
відкриває буферований потік читання з вхідного потоку типу in. Цей потік типу Reader може надходити з клавіатури, файлу, мережі тощо.

Корисні методи класу BufferedReader такі:

int read()
зчитує символ
String readLine()
зчитує рядок тексту
int read(char[] buffer, int offset, int taille)
зчитує taille символів з файлу та розміщує їх у масиві buffer, починаючи з позиції offset.
void close()
закриває потік читання

Ось програма, яка зчитує вміст раніше створеного файлу:

// імпортовані класи
import java.util.*;
import java.io.*;

public class lire{
  public static void main(String[] arg){
    personne p=null;
     // відкриття файлу
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new FileReader("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // дані
    String ligne=null;
    String[] champs=null;
    String prenom=null;
    String nom=null;
    int age=0;

    // обробка можливих помилок
    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));
      }// кінець циклу while
    } catch (Exception e){
      Erreur(e,2);
    }

     // закриття файлу
    try{
      IN.close();
    } catch (Exception e){
      Erreur(e,3);
    }
  }// кінець main

   // Помилка
  public static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }

}// кінець класу

Виконання програми дає такі результати:

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

4.9.3. Збереження об’єкта «особа»

Ми застосовуємо те, що щойно розглянули, щоб надати класу personne метод, який дозволяє зберегти атрибути особи у файлі. Додаємо метод sauveAttributs до визначення класу personne:


    // ------------------------------
  // збереження у текстовому файлі
    // ------------------------------
  public void sauveAttributs(PrintWriter P){
    P.println(""+this);
  }

Перед визначенням класу personne не забудьте імпортувати пакет java.io:

import java.io.*;

Метод sauveAttributs отримує як єдиний параметр потік PrintWriter, у який він повинен записувати дані. Тестова програма може виглядати так:

// імпорт
import java.io.*;
// імпорт особи;

public class sauver{
  public static void main(String[] arg){
    // відкриття файлу
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // запис у файл
    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);
    }
     // закриття файлу
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// кінець main

   // Помилка
  private static void Erreur(Exception e, int code){
    System.err.println("Erreur : "+e);
    System.exit(code);
  }//Помилка
}//клас

Скомпілюємо та запустимо цю програму:

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. Бінарні файли

4.10.1. Клас RandomAccessFile

Клас RandomAccessFile дозволяє керувати бінарними файлами, зокрема тими, що мають фіксовану структуру, як це відомо в мовах C/C++. Ось кілька корисних методів та конструкторів:

RandomAccessFile(String ім'я_файлу, String режим)
конструктор — відкриває вказаний файл у вказаному режимі. Режим може приймати такі значення:
r: відкриття для читання
rw: відкриття для читання та запису
void writeTTT(TTT значення)
записує значення у файл. TTT позначає тип значення. Пам'ятне представлення значення записується у файл без змін. Таким чином, у файлі містяться writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat,... Для запису рядка використовується writeBytes(String рядок).
TTT readTTT()
зчитує та повертає значення типу TTT. Таким чином, існують readBoolean, readByte, readInt, readDouble, readLong, readFloat,... Метод read() зчитує один байт.
long length()
розмір файлу в байтах
long getFilePointer()
поточна позиція покажчика файлу
void seek(long pos)
встановлює курсор файлу на байт pos

4.10.2. Клас статті

У всіх наведених нижче прикладах буде використовуватися наступний клас article:

     // структура статті
    private static class article{
         // визначаємо структуру
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//клас статті

Наведений вище клас Java article буде еквівалентом наступної структури article на мові C

struct article{
    char code[4];
    char nom[20];
    double prix;
    int stockActuel;
    int stockMinimum;
}//структура

Таким чином, ми обмежимо код до 4 символів, а ім'я — до 20.

4.10.3. Запис даних

Наступна програма записує статтю у файл під назвою «data»:

// імпортовані класи
import java.io.*;

public class test1{

// перевіряє запис структури (у значенні мови C) у двійковий файл

     // структура статті
    private static class article{
         // визначаємо структуру
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//клас «article»

    public static void main(String arg[]){

         // визначаємо бінарний файл, у якому будуть зберігатися статті
        RandomAccessFile fic=null;

         // визначається стаття
        article art=new article();
        art.code="a100";
        art.nom="velo";
        art.prix=1000.80;
        art.stockActuel=100;
        art.stockMinimum=10;

         // визначається файл
        try{
                fic=new RandomAccessFile("data","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data",1);
        }//try-catch

        // записується
        try{
            ecrire(fic,art);
        } catch (IOException E){
                erreur("Erreur lors de l'écriture de l'enregistrement",2);
        }//try-catch

        // все закінчено
        try{
            fic.close();
        } catch (Exception E){
            erreur("Impossible de fermer le fichier data",2);
        }//try-catch
  }//main

   // метод запису
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // код
      fic.writeBytes(art.code);
       // ім'я обмежене до 20 символів
      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);
       // ціна
      fic.writeDouble(art.prix);
       // запаси
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// завершення запису

   // ------------------------помилка
  public static void erreur(String msg, int exitCode){
          System.err.println(msg);
          System.exit(exitCode);
  }// кінець помилки
}// кінець класу

Саме ця програма дозволяє нам перевірити, чи виконання відбулося правильно.

4.10.4. Зчитати запис

// імпортовані класи
import java.io.*;

public class test2{

// перевіряє запис структури (у розумінні мови C) у бінарний файл

     // структура article
    private static class article{
         // визначаємо структуру
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//клас «article»

    public static void main(String arg[]){

         // визначаємо бінарний файл, у якому будуть зберігатися статті
        RandomAccessFile fic=null;

       // відкриваємо файл для читання
      try{
          fic=new RandomAccessFile("data","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data",1);
      }//try-catch

       // зчитуємо єдиний елемент із файлу
    article art=new article();
      try{
          lire(fic,art);
      } catch (IOException E){
          erreur("Erreur lors de la lecture de l'enregistrement",2);
      }//try-catch

       // виводимо прочитаний запис
      affiche(art);

       // все закінчено
      try{
          fic.close();
      } catch (Exception E){
          erreur("Impossible de fermer le fichier data",2);
      }//try-catch
  }// кінець ручного введення

    // метод зчитування
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // зчитування коду
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // назва
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // ціна
        art.prix=fic.readDouble();
    // запаси
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// завершення запису

     // ---------------------відображення
    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);
    }// кінець відображення

     // ------------------------помилка
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// кінець помилки
}// кінець класу

Результати виконання такі:

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

Запис, який був записаний програмою запису, відновлюється правильно.

4.10.5. Перетворення тексту в двійковий формат

Наступна програма є розширенням програми запису одного запису. Тепер ми записуємо кілька записів у двійковий файл під назвою data.bin. Дані беруться з наступного файлу 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
// імпортовані класи
import java.io.*;
import java.util.*;

public class test3{

// текстовий файл --> двійковий файл

     // структура статті
    private static class article{
         // визначення структури
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//клас статті

    public static void main(String arg[]){

         // визначаємо бінарний файл, у якому будуть зберігатися статті
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin",1);
        }

         // дані беруться з текстового файлу
    BufferedReader dataTxt=null;
        try{
            dataTxt=new BufferedReader(new FileReader("data.txt"));
        } catch (IOException E){
            erreur("Impossible d'ouvrir le fichier data.txt",2);
        }

       // файл .txt --> файл .bin
      String ligne=null;
      String[] champs=null;
      int numLigne=0;
      String champ=null;
    article art=new article();    // товар, який потрібно створити
      try{
          while((ligne=dataTxt.readLine())!=null){
          // рядок із знаком «+»
              numLigne++;
         // розбиття на поля
        champs=ligne.split(":");
         // потрібно 5 полів
              if(champs.length!=5)
                  erreur("Ligne "+numLigne+" erronée dans data.txt",3);
         //код
              art.code=champs[0];
              if(art.code.length()!=4) 
            erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
         // прізвище, ім'я
              art.nom=champs[1];
         // ціна
              try{
                  art.prix=Double.parseDouble(champs[2]);
              } catch (Exception E){
                  erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
              }
        // поточний запас
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }
        // поточний запас
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }             
               // записуємо запис
              try{
                  ecrire(dataBin,art);
              } catch (IOException E){
                  erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
              }
               // перехід до наступного рядка
          }// кінець циклу while
      } catch (IOException E){
          erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
      }        
        // завершено
      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);
      }
  }// кінець main

   // метод запису
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // код
      fic.writeBytes(art.code);
       // ім'я обмежене до 20 символів
      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);
       // ціна
      fic.writeDouble(art.prix);
       // запаси
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// завершення запису

     // ------------------------помилка
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// кінець помилки

}// кінець класу

За допомогою наступної програми можна перевірити, чи програма працювала правильно.

4.10.6. Перетворення з двійкового формату в текстовий

Наведена нижче програма зчитує вміст раніше створеного бінарного файлу data.bin і записує його вміст у текстовий файл data.text. Якщо все пройшло успішно, файл data.text має бути ідентичним вихідному файлу data.txt.

// імпортовані класи
import java.io.*;
import java.util.*;

public class test5{

// текстовий файл --> бінарний файл

     // структура статті
    private static class article{
         // визначення структури
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//клас статті

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

         // визначаємо бінарний файл, у якому будуть зберігатися статті
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","r");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
        }

         // дані записуються у текстовий файл
    PrintWriter dataTxt=null;
        try{
            dataTxt=new PrintWriter(new FileWriter("data.text"));
        } catch (IOException E){
            erreur("Impossible d'ouvrir le fichier data.text en écriture",2);
        }

      // файл .bin --> файл .text
    article art=new article();    // стаття, яку потрібно створити

       // зчитується бінарний файл
      int numRecord=0;
      long l=0;    // розмір файлу
      try{
          l=dataBin.length();
      } catch (IOException e){
          erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
      }
      long pos=0;    // поточна позиція у файлі
      try{
          pos=dataBin.getFilePointer();
      } catch (IOException e){
          erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
      }

     // доки не досягнуто кінця файлу
        while(pos<l){
            // прочитати поточний запис і обробити його
            numRecord++;
            try{
                lire(dataBin,art);
            } catch (Exception e){
                erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
            }
            affiche(art);

             // запис відповідного рядка тексту в dataTxt
         dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);

             // продовжуємо?
            try{
                pos=dataBin.getFilePointer();
            } catch (IOException e){
                erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
            }
        }// кінець циклу while

       // все закінчено
      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);
      }

  }// кінець main

     // метод читання
    public static void lire(RandomAccessFile fic, article art) throws IOException{
      // читання коду
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // ім'я
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // ціна
        art.prix=fic.readDouble();
    // запаси
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// завершення запису

     // ---------------------відображення
    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);
    }// кінець відображення

     // ------------------------помилка
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// кінець помилки
}// кінець класу

Ось приклад виконання:

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. Прямий доступ до записів

Ця остання програма демонструє можливість прямого доступу до записів бінарного файлу. Вона відображає запис із файлу data.bin, номер якого передається їй як параметр, причому перший запис має номер 1.

// імпортовані класи
import java.io.*;
import java.util.*;

public class test6{

// текстовий файл --> двійковий файл

     // структура статті
    private static class article{
         // визначення структури
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//клас статті

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

       // перевірка аргументів
      int nbArguments=args.length;
      String syntaxe="syntaxe : pg numéro_de_fiche";
      if(nbArguments!=1)
          erreur(syntaxe,20);
     // перевірка номера картки
      int numRecord=0;
      try{
          numRecord=Integer.parseInt(args[0]);
      } catch(Exception e){
          erreur(syntaxe+"\nNuméro de fiche incorrect",21);
      }

       // відкриття бінарного файлу для читання
    RandomAccessFile dataBin=null;
      try{
          dataBin=new RandomAccessFile("data.bin","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
      }

       // перехід до потрібної запису
      try{
          dataBin.seek((numRecord-1)*40);
      } catch (Exception e){
          erreur("La fiche "+numRecord+" n'existe pas",23);
      }

       // читання запису
    article art=new article();
      try{
          lire(dataBin,art);
      } catch (Exception e){
          erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
      }

       // виводимо її на екран
      affiche(art);

       // все закінчено
      try{
          dataBin.close();
      } catch (Exception E){
          erreur("Impossible de fermer le fichier data.bin",2);
      }//try-catch

  }// кінець main

    // метод читання
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // читання коду
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // ім'я
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // ціна
        art.prix=fic.readDouble();
    // запаси
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// завершення запису

     // ---------------------відображення
    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);
    }// кінець відображення

     // ------------------------помилка
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// кінець помилки
}// кінець класу

Ось приклади виконання:

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. Використання регулярних виразів

4.11.1. Пакет java.util.regex

Пакет java.util.regex дозволяє використовувати регулярні вирази. Вони дають змогу перевіряти формат символьного рядка. Таким чином можна перевірити, чи рядок, що позначає дату, має формат дд/мм/рр. Для цього використовується шаблон, з яким порівнюється рядок. У цьому прикладі символи j, m та a мають бути цифрами. Шаблон дійсного формату дати має вигляд «\d\d/\d\d/\d\d», де символ \d позначає цифру. У шаблоні можна використовувати такі символи (документація Microsoft):

Символ
Опис
\
Позначає наступний символ як спеціальний або літеральний. Наприклад, «n» відповідає символу «n». «\n» відповідає символу нового рядка. Послідовність «\\» відповідає «\», а «\(" відповідає «(».
^
Відповідає початку введення.
$
Відповідає кінцю введення.
*
Відповідає попередньому символу нуль або більше разів. Таким чином, «zo*» відповідає «z» або «zoo».
+
Відповідає попередньому символу один або кілька разів. Таким чином, «zo+» відповідає «zoo», але не «z».
?
Відповідає попередньому символу нуль або один раз. Наприклад, «a?ve?» відповідає «ve» у слові «lever».
.
Відповідає будь-якому окремому символу, крім символу нового рядка.
(шаблон)
Здійснює пошук modèle і запам’ятовує збіг. Відповідний підрядок можна витягти з отриманої колекції Matches за допомогою Item [0]...[n]. Щоб знайти збіги з символами в дужках ( ), використовуйте "\(" або "\)".
x|y
Відповідає або x, або y. Наприклад, «z|foot» відповідає «z» або «foot». «(z|f)oo» відповідає «zoo» або «foo».
{n}
n — це невід’ємне ціле число. Відповідає точно n, помноженому на символ. Наприклад, «o{2}» не відповідає «o» у слові «Bob», а відповідає двом першим «o» у слові «fooooot».
{n,}
n — це невід’ємне ціле число. Відповідає щонайменше n-кратному кількості символів. Наприклад, «o{2,}» не відповідає «o» у слові «Bob», а відповідає всім «o» у слові «fooooot». «o{1,}» еквівалентно «o+», а «o{0,}» — «o*».
{n,m}
m та n — це невід’ємні цілі числа. Відповідає не менше ніж n і не більше ніж m повторень символу. Наприклад, «o{1,3}» відповідає трьом першим «o» у слові «foooooot», а «o{0,1}» дорівнює «o?».
[xyz]
Набір символів. Відповідає одному з вказаних символів. Наприклад, «[abc]» відповідає «a» у слові «plat».
[^xyz]
Негативний набір символів. Відповідає будь-якому символу, що не вказано. Наприклад, «[^abc]» відповідає «p» у слові «plat».
[a-z]
Діапазон символів. Відповідає будь-якому символу у вказаному діапазоні. Наприклад, «[a-z]» відповідає будь-якій маленькій літері від «a» до «z».
[^m-z]
Негативний діапазон символів. Відповідає будь-якому символу, якого немає у вказаній послідовності. Наприклад, «[^m-z]» відповідає будь-якому символу, якого немає між «m» і «z».
\b
Відповідає межі, що позначає слово, іншими словами, позиції між словом і пробілом. Наприклад, «er\b» відповідає «er» у слові «lever», але не «er» у слові «verbe».
\B
Відповідає межі, що не позначає слово. «en*t\B» відповідає «ent» у слові «bien entendu».
\d
Відповідає символу, що позначає цифру. Еквівалентно [0-9].
\D
Відповідає символу, що не є цифрою. Еквівалентно [^0-9].
\f
Відповідає символу переходу на новий рядок.
\n
Відповідає символу нового рядка.
\r
Відповідає символу повернення каретки.
\s
Відповідає будь-якому пробілу, включаючи пробіл, табуляцію, перехід на нову сторінку тощо. Еквівалентно "[ \f\n\r\t\v]".
\S
Відповідає будь-якому символу, що не є пробілом. Еквівалентно «[^ \f\n\r\t\v]».
\t
Відповідає символу табуляції.
\v
Відповідає символу вертикального табуляції.
\w
Відповідає будь-якому символу, що позначає слово, включаючи підкреслення. Еквівалентно «[A-Za-z0-9_]».
\W
Відповідає будь-якому символу, що не позначає слово. Еквівалентно «[^A-Za-z0-9_]».
\num
Відповідає num, де num — додатне ціле число. Посилається на збережені відповідності. Наприклад, «(.)\1» відповідає двом послідовним однаковим символам.
\n
Відповідає n, де n — це вісімкове значення екранування. Вісімкові значення екранування повинні складатися з 1, 2 або 3 цифр. Наприклад, «\11» і «\011» обидва відповідають символу табуляції. "\0011" еквівалентно "\001" & "1". Восьмиричні екрануючі значення не повинні перевищувати 256. Якщо це сталося, у виразі враховуватимуться лише перші дві цифри. Дозволяє використовувати коди ASCII у регулярних виразах.
\xn
Відповідає n, де n — це шістнадцяткове значення екранування. Шістнадцяткові екрануючі значення обов’язково мають складатися з двох цифр. Наприклад, «\x41» відповідає «A». «\x041» дорівнює «\x04» та «1». Дозволяє використовувати коди ASCII у регулярних виразах.

Елемент у шаблоні може зустрічатися один або кілька разів. Розглянемо кілька прикладів із символом \d, який позначає 1 цифру:

шаблон
значення
\d
цифра
\d?
0 або 1 цифра
\d*
0 або більше цифр
\d+
1 або більше цифр
\d{2}
2 цифри
\d{3,}
щонайменше 3 цифри
\d{5,7}
від 5 до 7 цифр

Тепер уявімо собі шаблон, здатний описати очікуваний формат рядка:

пошуковий рядок
модель
дата у форматі дд/мм/рр
\d{2}/\d{2}/\d{2}
час у форматі hh:mm:ss
\d{2}:\d{2}:\d{2}
ціле число без знака
\d+
послідовність пробілів, яка може бути порожньою
\s*
ціле число без знака, перед яким або після якого можуть бути пробіли
\s*\d+\s*
ціле число, яке може мати знак і перед яким або після якого можуть бути пробіли
\s*[+|-]?\s*\d+\s*
число з дійсної області без знака, перед яким або після якого можуть бути пробіли
\s*\d+(.\d*)?\s*
дійсне число, яке може мати знак і мати пробіли перед або після нього
\s*[+|]?\s*\d+(.\d*)?\s*
рядок, що містить слово «just»
\bjuste\b
  

Можна вказати, де саме в рядку шукати шаблон:

шаблон
значення
^шаблон
модель починає рядок
шаблон$
шаблон завершує рядок
^шаблон$
шаблон починає і закінчує рядок
шаблон
шаблон шукається у всьому рядку, починаючи з його початку.
шуканий рядок
шаблон
рядок, що закінчується знаком оклику
!$
рядок, що закінчується крапкою
\.$
рядок, що починається з послідовності //
^//
рядок, що складається лише з одного слова, за яким можуть йти або перед яким можуть стояти пробіли
^\s*\w+\s*$
рядок, що містить два слова, за якими або перед якими можуть бути пробіли
^\s*\w+\s*\w+\s*$
рядок, що містить слово secret
\bsecret\b

Підмножини шаблону можна «витягувати». Таким чином, можна не тільки перевірити, чи рядок відповідає певному шаблону, але й витягнути з цього рядка елементи, що відповідають підмножинам шаблону, які були взяті в дужки. Отже, якщо ми аналізуємо рядок, що містить дату дд/мм/рр, і хочемо додатково виокремити елементи дд, мм, рр з цієї дати, ми використаємо шаблон (\d\d)/(\d\d)/(\d\d).

4.11.2. Перевірка відповідності рядка заданому шаблону

Клас Pattern дозволяє перевірити, чи відповідає рядок заданому шаблону. Для цього використовується статичний метод

boolean Matches(String modèle, String chaine)

де: modèle — шаблон для перевірки, chaine — рядок, який потрібно порівняти з шаблоном. Результатом є логічне значення true, якщо рядок відповідає шаблону, і false — у протилежному випадку.

Ось приклад:

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

// обробка регулярних виразів
public class regex1 {
  public static void main(String[] args){
    // шаблон регулярного виразу
    String modèle1="^\\s*\\d+\\s*$";
    // порівняння зразка з шаблоном
    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);
  }//вивести
}//клас

та результати виконання:

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

Слід зауважити, що в шаблоні "^\s*\d+\s*$" символ \ має бути подвоєний через особливе тлумачення цього символу в Java. Отже, записуємо: String шаблон1="^\\s*\\d+\\s*$";

4.11.3. Пошук усіх елементів рядка, що відповідають шаблону

Розглянемо шаблон "\d+" та рядок " 123 456 789 ". Шаблон зустрічається у трьох різних місцях рядка. Класи Pattern та Matcher дозволяють витягувати різні входження шаблону в рядку. Клас Pattern — це клас, що керує регулярними виразами. Регулярний вираз, який використовується більше ніж один раз, потрібно «скомпілювати». Це прискорює пошук шаблону в рядках. Цю роботу виконує статичний метод compile:

public static Pattern compile(String regex)

Він приймає як параметр рядок шаблону та повертає об’єкт Pattern. Для порівняння шаблону об’єкта Pattern із рядком символів використовується клас Matcher. Він дозволяє порівняти шаблон із рядком символів. На основі об’єкта Pattern можна отримати об’єкт типу Matcher за допомогою методу matcher:

public Matcher matcher(CharSequence input)

input — це рядок, який потрібно порівняти з шаблоном.

Отже, щоб порівняти шаблон "\d+" із рядком " 123 456 789 ", можна створити об’єкт Matcher таким чином:

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

На основі попереднього об’єкта résultats можна буде витягти різні входження шаблону в рядку. Для цього використовуються методи suivantes класу Matcher:

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

Метод find шукає в обстежуваному рядку перше входження шаблону. Другий виклик методу find шукатиме наступне входження. І так далі. Метод повертає true, якщо знаходить шаблон, і false — у протилежному випадку. Частина рядка, що відповідає останньому вживанню, знайденому методом find, отримується за допомогою методу group, а його позиція — за допомогою методу start. Отже, якщо продовжити попередній приклад і ми хочемо вивести всі входження шаблону «\d+» у рядку « 123 456 789 », то напишемо:

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

Метод reset дозволяє скинути об’єкт Matcher до початку рядка, що порівнюється з шаблоном. Таким чином, метод find знову знайде перше входження шаблону.

Ось повний приклад:

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

// обробка регулярних виразів
public class regex2 {
  public static void main(String[] args){
    // кілька входжень шаблону в екземплярі
    String modèle2="\\d+";
    Pattern regex2=Pattern.compile(modèle2);
    String exemplaire3="  123  456 789";
    // пошук входжень шаблону в екземплярі
    Matcher matcher2=regex2.matcher(exemplaire3);
    while(matcher2.find()){
      affiche("séquence " + matcher2.group() + " trouvée en position " + matcher2.start());
    }//while
  }//Main

  public static void affiche(String msg){
    System.out.println(msg);
  }//відображає
}//класифікує

Результати виконання:

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. Витягнення частин шаблону

Підмножини шаблону можна «витягувати». Таким чином, можна не тільки перевірити, чи відповідає рядок певному шаблону, але й витягнути з цього рядка елементи, що відповідають підмножинам шаблону, які були взяті в дужки. Отже, якщо ми аналізуємо рядок, що містить дату дд/мм/рр, і хочемо додатково виокремити елементи дд, мм, рр з цієї дати, ми використаємо шаблон (\d\d)/(\d\d)/(\d\d).

Розглянемо такий приклад:

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

// обробка регулярних виразів
public class regex3 {
  public static void main(String[] args){
    // витяг елементів із шаблону
    String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
    Pattern regex3=Pattern.compile(modèle3);
    String exemplaire4="Il est 18:05:49";
    // перевірка шаблону
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // екземпляр відповідає шаблону
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // відображення груп
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//for
      }else{
        // екземпляр не відповідає шаблону
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }
    }//Головна

    public static void affiche(String msg){
      System.out.println(msg);
    }//відображає
}//класифікує

Виконання цієї програми дає такі результати:

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

Нововведення міститься в наступному фрагменті коду:

    // перевірка зразка
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // екземпляр відповідає зразку
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // відображаються групи
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//для
      }else{
        // екземпляр не відповідає шаблону
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }

Рядок exemplaire4 порівнюється з шаблоном regex3 за допомогою методу find. У результаті в рядку exemplaire4 знайдено входження шаблону regex3. Якщо шаблон містить підмножини, взяті в дужки, до них можна отримати доступ за допомогою різних методів класу Matcher:


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

Метод groupCount повертає кількість підмножин, знайдених у шаблоні, а group(i) — підмножину № i. Ця підмножина знайдена в рядку у позиції, заданій методом start(i). Отже, у прикладі:

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

При першому виклику методу find буде знайдено рядок 18:05:49 і автоматично створено три підмножини, визначені дужками шаблону, відповідно 18, 05 та 49.

4.11.5. Програма для навчання

Знайти регулярний вираз, який дозволяє перевірити, чи відповідає рядок певному шаблону, іноді є справжнім викликом. Наступна програма дозволяє потренуватися. Вона запитує шаблон і рядок, а потім вказує, чи відповідає рядок шаблону.

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

// обробка регулярних виразів
public class regex4 {
  public static void main(String[] args){

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

    // обробка помилок
    try{
      // користувачеві пропонується вказати шаблони та екземпляри, які слід порівняти з цим
      while(true){
        // вхідний потік
        IN=new BufferedReader(new InputStreamReader(System.in));
        // запитується шаблон
        System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
        modèle=IN.readLine();
        // завершено?
        if(modèle.trim().toLowerCase().equals("fin")) break;
        // створюється регулярний вираз
        regex=Pattern.compile(modèle);
        // запитуємо у користувача екземпляри для порівняння з шаблоном
        while(true){
          System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
          chaine=IN.readLine();
          // завершено?
          if(chaine.trim().toLowerCase().equals("fin")) break;
          // створюємо об’єкт matcher
          résultats=regex.matcher(chaine);
          // шукаємо входження шаблону
          nbOccurrences=0;
          while(résultats.find()){
            // знайдено збіг
            nbOccurrences++;
            // виводимо його
            System.out.println("J'ai trouvé la correspondance ["+résultats.group()
            +"] en position "+résultats.start());
            // відображення піделементів
            if(résultats.groupCount()!=1){
              for(int j=1;j<=résultats.groupCount();j++){
                System.out.println("\tsous-élément ["+résultats.group(j)+"] en position "+
                  résultats.start(j));
              }//for j
            }//if
            // наступний рядок
          }//while(résultats.find())
          // чи знайдено хоча б одне входження?
          if(nbOccurrences==0){
            System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
          }//if
          // наступний шаблон
        }//while(true)
      }//while(true)
    }catch(Exception ex){
      // помилка
      System.err.println("Erreur : "+ex.getMessage());
      // завершення з помилкою
      System.exit(1);
    }//try-catch
    // кінець
    System.exit(0);
  }//Main
}//клас

Ось приклад виконання:

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. Метод split класу Pattern

Розглянемо рядок, що складається з полів, розділених розділювальним рядком, який описується за допомогою регулярного виразу. Наприклад, якщо поля розділені символом , якому передує або слідує довільна кількість пробілів, регулярний вираз, що моделює розділювальний рядок полів, буде "\s*,\s*". Метод split класу Pattern дозволяє нам отримати поля у вигляді масиву:


public String[] split(CharSequence input)

Рядок input розбивається на поля, які розділяються роздільником, що відповідає шаблону поточного об’єкта Pattern. Щоб витягти поля з рядка, роздільником полів у якому є кома, перед якою або після якої стоїть довільна кількість пробілів, слід написати:

    // один рядок
    String ligne="abc  ,, def  , ghi";
    // шаблон
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // розбиття рядка на поля
    String[] champs=modèle.split(ligne);

Такий самий результат можна отримати за допомогою методу split класу String:


public String[] split(String regex)

Ось тестова програма:

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

// обробка регулярних виразів
public class split1 {
  public static void main(String[] args){
    // один рядок
    String ligne="abc  ,, def  , ghi";
    // шаблон
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // розбиття рядка на поля
    String[] champs=modèle.split(ligne);
    // відображення
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
    // інший спосіб
    champs=ligne.split("\\s*,\\s*");
    // відображення
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
  }//Головна
}//клас

Результати виконання:

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

4.12. Вправи

4.12.1. Вправа 1

У системі Unix програми часто запускаються таким чином:

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

де -oi позначає опцію, а vi — значення, пов’язане з цією опцією. Потрібно створити клас options, який би дозволяв аналізувати рядок аргументів -o1 v1 v2 ... -o2 v3 v4 … для побудови таких об’єктів:

optionsValides
словник (Hashtable), ключами якого є дійсні опції oi. Значенням, пов’язаним із ключем oi, є вектор (Vector), елементами якого є значення v1 v2 …, пов’язані з опцією -oi
optionsInvalides
словник (Hashtable), ключами якого є недійсні опції oi. Значенням, пов’язаним із ключем oi, є вектор (Vector), елементами якого є значення v1, v2 …, пов’язані з опцією -oi
optionsSans
рядок (String), що містить список значень vi, не пов’язаних з жодною опцією
erreur
ціле число, що дорівнює 0, якщо в рядку аргументів немає помилок, інакше — інше значення:
1: є недійсні параметри виклику
2: є недійсні опції
4: є значення, не пов'язані з опціями
Якщо є кілька типів помилок, ці значення сумуються.

Об’єкт options можна створити чотирма різними способами:

public options (String arguments, String optionsAcceptables)

arguments
рядок аргументів -o1 v1 v2 ... -o2 v3 v4 …, що підлягає аналізу
optionsAcceptables
список допустимих опцій oi

Приклад виклику: options opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");

Тут обидва аргументи є символьними рядками. Допускаються випадки, коли ці рядки розбиті на слова, що містяться в масиві символьних рядків. Для цього потрібні ще три конструктори:

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

Клас options матиме такий інтерфейс (методи доступу):

public Hashtable getOptionsValides()

повертає посилання на масив optionsValides, створений під час створення об’єкта «options»

public Hashtable getOptionsInvalides()

повертає посилання на масив optionsInvalides, створений під час створення об’єкта options

public String getOptionsSans()

повертає посилання на рядок optionsSans, сформований під час створення об’єкта options

public int getErreur()

повертає значення атрибуту «помилка», сформованого під час створення об’єкта «options»

public String toString()

якщо помилки немає, виводить значення атрибутів optionsValides, optionsInvalides, optionsSans, а в іншому випадку — номер помилки.

Ось приклад програми:


import java.io.*;
//параметри імпорту;

public class test1{
  
  
  public static void main (String[] arg){
    
    // відкриття вхідного потоку
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    // читання аргументів конструктора 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));
    }// кінець циклу while
  }//кінець main


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

}//кінець класу

Деякі результати:

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. Вправа 2

Потрібно створити клас stringtovector, який дозволить перенести вміст об’єкта String в об’єкт Vector. Цей клас буде похідним від класу Vector:

class stringtovector extends Vector

і матиме такий конструктор:


    private void stringtovector(String S, String separateur, int[] tChampsVoulus,
            boolean strict){
        
        // створює вектор із полями рядка S
        // який складається з полів, розділених роздільником
        // якщо роздільник=null, рядок утворює лише одне поле
        // потрібні лише ті поля, індекси яких містяться в масиві tChampsVoulus 
        // є потрібними. Індекси починаються з 1
        // якщо tChampsvoulus=null або має нульовий розмір, беруться всі поля
        // якщо strict=true, то всі необхідні поля мають бути присутніми

Клас матиме такий приватний атрибут:

        private int erreur;

Цей атрибут встановлюється попереднім конструктором із такими значеннями:

0: створення пройшло успішно

4: деякі необхідні поля відсутні, хоча strict=true

Клас також матиме два методи:

    public int getErreur()

, який повертає значення приватного атрибута erreur.

    public String identite(){

який виводить значення об’єкта у вигляді (помилка, елемент 1, елемент 2, …) де елементи i — це елементи вектора, побудованого на основі рядка.

Тестова програма може виглядати так:


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

Результати:

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

Кілька порад:

  1. Щоб розбити рядок S на поля, використовуйте метод split класу String.
  2. Помістіть поля з S у словник D, індексований за номером поля
  3. Витягнути зі словника D лише ті поля, ключі (індекси) яких містяться в масиві tChampsVoulus.

4.12.3. Вправа 3

Потрібно додати до класу **stringtovector** наступний конструктор:


public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
        
        // створює вектор із полями рядка S
        // який складається з полів, розділених роздільником
        // якщо «separator» = null, рядок утворює лише одне поле
        // потрібні лише ті поля, індекси яких містяться в sChampsVoulus
        // індекси починаються з 1
        // якщо sChampsvoulus = null або "", беруться всі поля
        // якщо strict=true, то всі необхідні поля мають бути присутніми

Отже, список потрібних полів міститься у рядку (String), а не у масиві цілих чисел (int[]). Приватному атрибуту класу erreur можна присвоїти нове значення:

2: ланцюжок індексів потрібних полів є неправильним

Ось приклад програми:


import java.io.*;
//імпортувати 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());
  }
}

Деякі результати:

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

Кілька порад:

  1. Потрібно повернутися до випадку попереднього конструктора, перенісши поля рядка sChampsVoulus у масив цілих чисел. Для цього слід розбити sChampsVoulus на поля за допомогою об’єкта StringTokenizer, атрибут countTokens якого вкаже кількість отриманих полів. Після цього можна створити масив цілих чисел потрібного розміру та заповнити його отриманими полями.
  2. Щоб дізнатися, чи є поле цілим числом, скористайтеся методом Integer.parseInt для перетворення поля в ціле число та обробіть виняток, який буде згенеровано, якщо таке перетворення виявиться неможливим.

4.12.4. Вправа 4

Потрібно створити клас filetovector, який дозволить перенести вміст текстового файлу в об’єкт Vector. Цей клас буде похідним від класу Vector:

class filetovector extends Vector

і матиме такий конструктор:


    // --------------------- конструктор 
    public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
        
        // створює вектор із рядками текстового файлу nomFichier
        // рядки складаються з полів, розділених роздільником
        // якщо роздільник=null, рядок утворює лише одне поле
        // потрібні лише ті поля, індекси яких містяться у tChampsVoulus
        // індекси починаються з 1
        // якщо tChampsvoulus = null або порожній, беруться всі поля
        // якщо strict=true, то всі необхідні поля мають бути присутніми
        // якщо це не так, рядок не зберігається, а його індекс
        // поміщається у вектор lignesErronees
        // порожні рядки ігноруються
        //, а також рядки, що починаються з tagCommentaire, якщо tagCommentaire != null

Клас матиме такі приватні атрибути:

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

Атрибут erreur встановлюється попереднім конструктором із такими значеннями:

0: створення пройшло успішно

1: файл, який потрібно обробити, не вдалося відкрити

4: деякі необхідні поля відсутні, хоча strict=true

8: під час обробки файлу сталася помилка вводу-виводу

Атрибут lignesErronees є вектором, елементи якого — номери рядків з помилками у вигляді символьних рядків. Рядок вважається помилковим, якщо він не може надати необхідні поля при значенні strict=true.

Клас також матиме два методи:

    public int getErreur()

яка повертає значення приватного атрибута erreur.

    public String identite(){

який виводить значення об’єкта у вигляді (помилка, елемент 1, елемент 2 …,(l1,l2,…)), де елементи i — це елементи вектора, побудованого на основі файлу, а li — номери рядків, що містять помилки.

Ось приклад тесту:


import java.io.*;
//імпортувати 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());
  }
}

Результати виконання:

[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,# коментар) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# коментар) (0,azerty,1,cvf,fff,qqqq) (0,s)]

Кілька порад

  1. Текстовий файл обробляється по рядках. Рядок розбивається на поля за допомогою класу stringtovector, який ми розглядали раніше.

  2. Отже, елементи вектора, сформованого на основі текстового файлу, є об’єктами типу stringtovector.

  3. Метод identite класу filetovector може використовувати метод stringtovector.identite() для виведення своїх елементів, а також метод Vector.toString() для виведення номерів можливих помилкових рядків.

4.12.5. Вправа 5

Потрібно додати до класу filetovector наступний конструктор:


    public filetovector(String nomFichier, String separateur, String sChampsVoulus,
            boolean strict, String tagCommentaire){
        
        // створює вектор із рядків текстового файлу nomFichier
        // рядки складаються з полів, розділених роздільником
        // якщо роздільник=null, рядок утворює лише одне поле
        // потрібні лише ті поля, індекси яких містяться у tChampsVoulus
        // індекси починаються з   1
        // якщо sChampsvoulus=null або порожній, беруться всі поля
        // якщо strict=true, то всі необхідні поля мають бути присутніми
        // якщо це не так, рядок не зберігається, а його індекс
        // поміщається у вектор lignesErronees
        // порожні рядки ігноруються
        //, а також рядки, що починаються з tagCommentaire, якщо tagCommentaire != null

Список індексів потрібних полів тепер міститься у рядку символів (String), а не у масиві цілих чисел.

Приватний атрибут erreur може мати додаткове значення:

2: рядок з індексами потрібних полів є некоректним

Ось приклад тесту:


import java.io.*;
//імпортувати 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());
  }
}

Результати:

[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,# коментар) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# коментар) (0,azerty,1,cvf,fff,qqqq) (0,s)]

Кілька порад

  1. Перетворимо рядок sChampsVoulus на масив цілих чисел tChampVoulus, щоб повернутися до випадку попереднього конструктора.