4. Veelgebruikte klassen
In dit hoofdstuk bespreken we een aantal veelgebruikte Java-klassen. Deze beschikken over talrijke attributen, methoden en constructors. Telkens behandelen we slechts een klein deel van de klassen. Meer informatie hierover is te vinden in de Java-help, die we nu zullen bespreken.
4.1. De documentatie
Als u JDK van Sun in de map <jdk> hebt geïnstalleerd, is de documentatie beschikbaar in de map <jdk>\docs:

Soms beschikt men over een jdk, maar zonder documentatie. Deze is te vinden op de website van Sun http://www.sun.com. In de map docs bevindt zich een bestand index.html, dat het uitgangspunt vormt voor de helpfunctie van JDK:


De bovenstaande link API & Language geeft toegang tot de Java-klassen. De link Demos/Tutorials is bijzonder nuttig voor het vinden van voorbeelden van Java-programma’s. Laten we de link API & Language volgen:

Laten we de link 'Java 2 Platform' (API) volgen:

Deze pagina vormt het echte startpunt van de documentatie over de klassen. U kunt er een snelkoppeling naar maken om er snel toegang toe te hebben. De URL bevindt zich in <jdk>\docs\api\index.html. Hier vindt men links naar de honderden Java-klassen van het JDK. Wanneer men begint, is de grootste moeilijkheid om te weten wat deze verschillende klassen doen. In eerste instantie is deze hulp dus alleen nuttig als men de naam kent van de klasse waarover men informatie wil. Men kan zich ook laten leiden door de namen van de klassen, die doorgaans de functie van de klasse aangeven.
Laten we een voorbeeld nemen en informatie zoeken over de klasse Vector, die een dynamische array implementeert. Je hoeft alleen maar in de lijst met klassen in het linkervenster de link naar de klasse Vector te zoeken:

en op de link te klikken om de definitie van de klasse te bekijken:

Daar vind je
- de hiërarchie waarin de klasse zich bevindt, in dit geval java.util.Vector
- de lijst met velden (attributen) van de klasse
- de lijst met constructors
- de lijst met methoden
Vervolgens stellen we verschillende klassen voor. We raden de lezer aan om systematisch de volledige definitie van de gebruikte klassen te controleren.
4.2. De testklassen
In de volgende voorbeelden wordt soms gebruikgemaakt van de klassen personne en enseignant. Hier geven we hun definitie nogmaals weer.
public class personne{
// achternaam, voornaam, leeftijd
private String prenom;
private String nom;
private int age;
// bouwer 1
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// constructor 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+")";
}
// toebehoren
public String getPrenom(){
return prenom;
}
public String getNom(){
return nom;
}
public int getAge(){
return age;
}
//wijzigers
public void setPrenom(String P){
this.prenom=P;
}
public void setNom(String N){
this.nom=N;
}
public void setAge(int age){
this.age=age;
}
}
De klasse enseignant is afgeleid van de klasse personne en is als volgt gedefinieerd:
class enseignant extends personne{
// attributen
private int section;
// constructor
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+")";
}
}
We zullen ook een klasse etudiant gebruiken, die is afgeleid van de klasse personne en als volgt is gedefinieerd:
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. De klasse String
De klasse String vertegenwoordigt tekenreeksen. Stel dat nom een tekenreeksvariabele is:
String naam;
nom is een verwijzing naar een nog niet geïnitialiseerd object. Dit kan op twee manieren worden geïnitialiseerd:
naam="paard" of naam=new String("paard")
Beide methoden zijn gelijkwaardig. Als we later naam="vis" schrijven, verwijst *nom dan naar een nieuw object. Het oude object *String("cheval") gaat verloren en de geheugenruimte die het in beslag nam, wordt vrijgegeven.
De klasse String beschikt over talrijke attributen en methoden. Hier volgen er enkele:
public char charAt(int i) | geeft het teken i uit de tekenreeks terug, waarbij het eerste teken de index 0 heeft. Dus String("paard").charAt(3) is gelijk aan 'v' |
public int compareTo(string2) | chaine1.compareTo(string2) vergelijkt string1 met string2 en retourneert 0 als string1 = string2, 1 als string1 > string2 en -1 als string1 < string2 |
public boolean equals(Object anObject) | chaine1.equals(string2) geeft ‘waar’ terug als string1 = string2, anders ‘onwaar’ |
public String toLowerCase() | chaine1.toLowerCase() zet string1 in kleine letters |
public String toUpperCase() | chaine1.toUpperCase() zet string1 in hoofdletters |
public String trim() | chaine1.trim() verwijdert de spaties aan het begin en einde van string1 |
public String substring(int beginIndex, int endIndex) | String("chapeau").subString(2,4) geeft de tekenreeks "ape" weer |
public char[] toCharArray() | maakt het mogelijk om de tekens van de tekenreeks in een tekenarray op te slaan |
int length() | aantal tekens in de tekenreeks |
int indexOf(String string2) | geeft de eerste positie van chaine2 in de huidige tekenreeks terug, of -1 als chaine2 niet aanwezig is |
int indexOf(String string2, int startIndex) | geeft de eerste positie van chaine2 in de huidige tekenreeks terug, of -1 als chaine2 niet aanwezig is. Het zoeken begint vanaf het teken met nummer startIndex. |
int lastIndexOf(String string2) | geeft de laatste positie van chaine2 in de huidige tekenreeks terug, of -1 als chaine2 niet voorkomt |
boolean startsWith(String string2) | geeft ‘waar’ terug als de huidige tekenreeks begint met chaine2 |
boolean endsWith(String string2) | geeft ‘waar’ terug als de huidige tekenreeks eindigt op chaine2 |
boolean matches(String regex) | geeft 'waar' terug als de huidige tekenreeks overeenkomt met de reguliere expressie regex. |
String[] split(String regex) | De huidige tekenreeks bestaat uit velden die worden gescheiden door een tekenreeks die wordt gemodelleerd door de reguliere expressie regex. Met de methode split kunnen de velden in een array worden opgehaald. |
String replace(char oldChar, char newChar) | vervangt in de huidige tekenreeks het teken oldChar door het teken newChar. |
Hier volgt een voorbeeldprogramma:
// imports
import java.io.*;
public class string1{
// een voorbeeldklasse
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]+"]");
}//voor
affiche("(\" abc \").trim()=["+" abc ".trim()+"]");
}//Main
// toont
public static void affiche(String msg){
// geeft bericht weer
System.out.println(msg);
}//weergeeft
}//klasse
en de verkregen resultaten:
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. De klasse Vector
Een vector is een dynamische array waarvan de elementen verwijzingen naar objecten zijn. Het is dus een array van objecten waarvan de grootte in de loop van de tijd kan variëren, wat niet mogelijk is met de statische arrays die we tot nu toe hebben gezien. Hieronder volgen enkele velden, constructors en methoden van deze klasse:
public Vector() | maakt een lege vector aan |
public final int size() | aantal elementen van de vector |
public final void addElement(Object obj) | voegt het object waarnaar obj verwijst toe aan de vector |
public final Object elementAt(int index) | verwijzing naar object nr. index in de vector – de indices beginnen bij 0 |
public final Enumeration elements() | de verzameling elementen van de vector in de vorm van een enumeratie |
public final Object firstElement() | verwijzing naar het eerste element van de vector |
public final Object lastElement() | referentie van het laatste element van de vector |
public final boolean isEmpty() | geeft 'true' terug als de vector leeg is |
public final void removeElementAt(int index) | verwijdert het element met index index |
public final void removeAllElements() | maakt de vector leeg |
public final String toString() | geeft een identificatiestring van de vector terug |
Hier is een testprogramma:
// de geïmporteerde klassen
import java.util.*;
public class test1{
// het hoofdprogramma main - static - klassemethode
public static void main(String arg[]){
// het aanmaken van objecten (instanties van klassen)
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());
// polymorfisme
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// een vector
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
for(int i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
} // einde main
}// einde klasse
Laten we dit programma compileren:
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
Laten we het bestand test1.class uitvoeren:
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)
Vanaf nu zullen we het proces van het compileren en uitvoeren van de testprogramma's niet meer herhalen. Het volstaat om te herhalen wat hierboven is gedaan.
4.5. De klasse ArrayList
De klasse ArrayList is vergelijkbaar met de klasse Vector. Het enige wezenlijke verschil is dat deze klasse door meerdere uitvoeringsthreads tegelijk wordt gebruikt. De methoden voor het synchroniseren van threads bij toegang tot een Vector of een ArrayList verschillen. Buiten dit geval kan men de ene of de andere naar keuze gebruiken. Hieronder volgen enkele velden, constructors of methoden van deze klasse:
ArrayList() | maakt een lege array aan |
int size() | aantal elementen in de array |
void add(Object obj) | voegt het object waarnaar wordt verwezen door obj toe aan de array |
void add(int index, Object obj) | voeg het object waarnaar obj verwijst toe aan de array op positie index |
Object get(int index) | referentie van object nr. index in de array – de indices beginnen bij 0 |
boolean isEmpty() | geeft ‘waar’ terug als de array leeg is |
void remove(int index) | verwijdert het element met index index |
void clear() | maakt de array leeg |
Object[] toArray() | zet de dynamische array om in een klassieke array |
String toString() | geeft een identificatiestring van de array terug |
Hier is een testprogramma:
// de geïmporteerde klassen
import java.util.*;
public class test1{
// het hoofdprogramma main - static - klassemethode
public static void main(String arg[]){
// het aanmaken van objecten (instanties van klassen)
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);
// polymorfisme
personne p2=(personne)en;
System.out.println("p2="+p2);
personne p3=(personne)et;
System.out.println("p3="+p3);
// een vector
ArrayList personnes=new ArrayList();
personnes.add(p);personnes.add(en);personnes.add(et);
System.out.println("Nombre de personnes = "+personnes.size());
for(int i=0;i<personnes.size();i++){
p2=(personne) personnes.get(i);
System.out.println("personnes["+i+"]="+p2);
}
} // einde main
}// einde klasse
De verkregen resultaten zijn dezelfde als eerder.
4.6. De klasse Arrays
De klasse java.util.Arrays biedt toegang tot statische methoden waarmee verschillende bewerkingen op arrays kunnen worden uitgevoerd, met name het sorteren en zoeken naar elementen. Hier volgen enkele van deze methoden:
static void sort(array) | sorteert tableau, waarbij gebruik wordt gemaakt van de impliciete volgorde van het gegevenstype van de array, getallen of tekenreeksen. |
static void sort (Object[] array, Comparator C) | sorteert tableau en gebruikt daarbij de vergelijkingsfunctie C om de elementen te vergelijken |
static int binarySearch(array, element) | geeft de positie van élément in tableau terug, of anders een waarde <0. De array moet vooraf gesorteerd zijn. |
static int binarySearch(Object[] array, Object element, Comparator C) | Hetzelfde, maar gebruikt de vergelijkingsfunctie C om twee elementen van de array te vergelijken. |
Hier volgt een eerste voorbeeld:
import java.util.*;
public class sort2 implements Comparator{
// een interne privéklasse
private class personne{
private String nom;
private int age;
public personne(String nom, int age){
this.nom=nom; // naam van de persoon
this.age=age; // zijn/haar leeftijd
}
// leeftijd ophalen
public int getAge(){
return age;
}
// identiteit van de persoon
public String toString(){
return ("["+nom+","+age+"]");
}
}; // klasse ‘persoon’
// fabrikant
public sort2() {
// een tabel met personen
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// sortering van de lijst met personen
Arrays.sort(amis,this);
// controle
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//maker
// de functie die personen vergelijkt
public int compare(Object o1, Object o2){
// moet het volgende opleveren
// -1 als o1 "kleiner is dan" o2
// 0 als o1 "gelijk aan" o2
// +1 als o1 "groter is dan" 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;
}//vergelijkt
// testfunctie
public static void main(String[] arg){
new sort2();
}//hand
}//klas
Laten we dit programma eens bekijken. De functie main maakt een object *sort2 aan. De constructor van de klasse *sort2 ziet er als volgt uit:
// fabrikant
public sort2() {
// een lijst met personen
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// sorteren van de lijst met personen
Arrays.sort(amis,this);
// controle
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//maker
De te sorteren array is een array van objecten van het type personne. De klasse personne is privé gedefinieerd (private) binnen de klasse sort2. De statische methode sort van de klasse Arrays weet niet hoe een array van objecten van het type personne moet worden gesorteerd, dus moeten we hier de vorm void sort(Object[] obj, Comparator C) gebruiken. Comparator is een interface die slechts één methode definieert:
en die de waarde 0 moet retourneren: als o1=o2, -1: als o1 < o2, +1: als o1 > o2. In het prototype void sort(Object[] obj, Comparator C) moet het tweede argument C een object zijn dat de interface Comparator implementeert. In de constructor sort2 is gekozen voor het huidige object this:
Dit dwingt ons tot twee dingen:
- aangeven dat de klasse sort2 de interface Comparator implementeert
- Schrijf de functie compare in de klasse sort2.
Deze luidt als volgt:
// de functie die personen vergelijkt
public int compare(Object o1, Object o2){
// moet het volgende opleveren
// -1 als o1 "kleiner is dan" o2
// 0 als o1 "gelijk aan" o2
// +1 als o1 "groter is dan" 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;
}//vergelijkt
Om twee objecten personne te vergelijken, wordt hier de leeftijd gebruikt (we hadden ook de naam kunnen gebruiken).
De resultaten van de uitvoering zijn als volgt:
We hadden ook op een andere manier te werk kunnen gaan om de interface Comparator te implementeren:
import java.util.*;
public class sort2 {
// een interne privéklaas
private class personne{
…….
}; // klasse persoon
// constructor
public sort2() {
// een array van personen
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// sorteren van de array met personen
Arrays.sort(amis,
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//vergelijken
}//klasse
);
// controle
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//generator
// de functie die personen vergelijkt
public int compare1(Object o1, Object o2){
// moet het volgende retourneren
// -1 als o1 "kleiner is dan" o2
// 0 als o1 "gelijk aan" o2
// +1 als o1 "groter dan" 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;
}//vergelijkt1
// main
public static void main(String[] arg){
new sort2();
}//main
}//klasse
De sorteerinstructie is nu als volgt geworden:
// sorteren van de lijst met personen
Arrays.sort(amis,
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//vergelijken
}//klas
);
De tweede parameter van de methode sort moet een object zijn dat de interface Comparator implementeert. Hier maken we zo’n object aan via new java.util.Comparator() en de tekst die volgt op {…} definieert de klasse waarvan we een object aanmaken. Dit wordt een anonieme klasse genoemd omdat deze geen naam heeft. In deze anonieme klasse, die de interface Comparator moet implementeren, definiëren we de methode compare van deze interface. Deze methode roept alleen de methode compare1 van de klasse sort2 aan. We komen dan weer terug bij het vorige geval.
De klasse sort2 implementeert de interface Comparator niet meer. Daarom wordt de declaratie ervan:
Nu testen we de methode binarySearch van de klasse Arrays aan de hand van het volgende voorbeeld:
import java.util.*;
public class sort4 {
// een interne privéklasse
private class personne{
// attributen
private String nom;
private int age;
// constructor
public personne(String nom, int age){
this.nom=nom; // naam van de persoon
this.age=age; // zijn/haar leeftijd
}
// de naam ophalen
public String getNom(){
return nom;
}
// leeftijd ophalen
public int getAge(){
return age;
}
// identiteit van de persoon
public String toString(){
return ("["+nom+","+age+"]");
}
}; // persoonsklasse
// constructor
public sort4() {
// een tabel met personen
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// vergelijkingen
java.util.Comparator comparateur1=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//vergelijkt
}//klasse
;
java.util.Comparator comparateur2=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare2(o1,o2);
}//vergelijkt
}//rangschikt
;
// sorteren van de tabel met personen
Arrays.sort(amis,comparateur1);
// controle
for(int i=0;i<3;i++)
System.out.println(amis[i]);
// zoekopdrachten
cherche("milou",amis,comparateur2);
cherche("xx",amis,comparateur2);
}//-generator
// de functie die personen vergelijkt
public int compare1(Object o1, Object o2){
// moet het volgende opleveren
// -1 als o1 "kleiner is dan" o2
// 0 als o1 "gelijk aan" o2
// +1 als o1 "groter is dan" 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
// de functie die een persoon met een naam vergelijkt
public int compare2(Object o1, Object o2){
// o1 is een persoon
// o2 is een String, de naam naam2 van een persoon
// moet het volgende opleveren
// -1 als o1.nom "kleiner is dan" nom2
// 0 als o1.nom "gelijk is aan" naam2
// +1 als o1.nom "groter is dan" naam2
personne p1=(personne)o1;
String nom1=p1.getNom();
String nom2=(String)o2;
return nom1.compareTo(nom2);
}//vergelijking2
public void cherche(String ami,personne[] amis, Comparator comparateur){
// zoek vriend in de vriendenlijst
int position=Arrays.binarySearch(amis,ami,comparateur);
// gevonden?
if(position>=0)
System.out.println(ami + " a " + amis[position].getAge() + " ans");
else System.out.println(ami + " n'existe pas dans le tableau");
}//zoeken
// hand
public static void main(String[] arg){
new sort4();
}//hand
}//klas
Hier zijn we iets anders te werk gegaan dan in de voorgaande voorbeelden. De twee objecten Comparator die nodig zijn voor de methoden sort en binarySearch zijn aangemaakt en toegewezen aan de variabelen comparateur1 en comparateur2.
// vergelijkers
java.util.Comparator comparateur1=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//vergelijkt
}//klas
;
java.util.Comparator comparateur2=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare2(o1,o2);
}//vergelijken
}//klasse
;
Er wordt twee keer een dichotomische zoekopdracht uitgevoerd op de tabel amis in de constructor van sort4:
De methode cherche ontvangt alle parameters die zij nodig heeft om de methode binarySearch aan te roepen:
public void cherche(String ami,personne[] amis, Comparator comparateur){
// vriend zoeken in de vriendenlijst
int position=Arrays.binarySearch(amis,ami,comparateur);
// gevonden?
if(position>=0)
System.out.println(ami + " a " + amis[position].getAge() + " ans");
else System.out.println(ami + " n'existe pas dans le tableau");
}//zoeken
De methode binarySearch werkt samen met de vergelijker comparateur2, die op zijn beurt gebruikmaakt van de methode compare2 van de klasse sort4. De methode rend retourneert de positie van de gezochte naam in de array, indien deze bestaat, of anders een getal <0. De methode compare2 wordt gebruikt om een object van het type personne te vergelijken met een naam van het type String.
// de functie die een persoon met een naam vergelijkt
public int compare2(Object o1, Object o2){
// o1 is een persoon
// o2 is een String, de naam naam2 van een persoon
// moet het volgende opleveren
// -1 als o1.nom "kleiner is dan" naam2
// 0 als o1.nom "gelijk aan" naam2
// +1 als o1.nom "groter dan" naam2
personne p1=(personne)o1;
String nom1=p1.getNom();
String nom2=(String)o2;
return nom1.compareTo(nom2);
}//vergelijkt2
In tegenstelling tot de methode sort, ontvangt de methode binarySearch geen twee objecten personne, maar één object personne en één object String in deze volgorde. De eerste parameter is een element uit de array amis, de tweede is de naam van de gezochte persoon.
4.7. De klasse Enumeration
Enumeration is een interface en geen klasse. Deze heeft de volgende methoden:
public abstract boolean hasMoreElements() | retourneert true als de opsomming nog elementen bevat |
public abstract Object nextElement() | geeft de referentie van het volgende element van de enumeratie terug |
Hoe gebruik je een opsomming? Meestal als volgt:
Enumeration e=… // er wordt een enumeratieobject opgehaald
while(e.hasMoreElements()){
// het element e.nextElement() verwerken
}
Hier is een voorbeeld:
// de geïmporteerde klassen
import java.util.*;
public class test1{
// het hoofdprogramma main – static – klassemethode
public static void main(String arg[]){
// het aanmaken van objecten (instanties van klassen)
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());
// polymorfisme
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// een vector
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
int i;
for(i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
// een opsomming
Enumeration E=V.elements();
i=0;
while(E.hasMoreElements()){
p2=(personne) E.nextElement();
System.out.println("V["+i+"]="+p2.toString());
i++;
}
}// einde main
}//einde klasse
Dit levert de volgende resultaten op:
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. De klasse Hashtable
Met de klasse Hashtable kan een woordenboek worden geïmplementeerd. Een woordenboek kan worden gezien als een tabel met twee kolommen:
sleutel | waarde |
sleutel1 | waarde1 |
sleutel2 | waarde2 |
.. | ... |
De sleutels zijn uniek, c.a.d. Er kunnen dus geen twee identieke sleutels zijn. De belangrijkste methoden en eigenschappen van de klasse Hashtable zijn de volgende:
public Hashtable() | constructor – maakt een leeg woordenboek aan |
public int size() | aantal elementen in het woordenboek – waarbij een element een (sleutel, waarde)-paar is |
public Object put(Object key, Object value) | voegt het paar (key, value) toe aan het woordenboek |
public Object get(Object key) | haalt het object op dat is gekoppeld aan de sleutel key of null als de sleutel key niet bestaat |
public boolean containsKey(Object key) | waar als de sleutel key in het woordenboek voorkomt |
public boolean contains(Object value) | waar als de waarde value in het woordenboek voorkomt |
public Enumeration keys() | geeft de sleutels van het woordenboek terug in de vorm van een opsomming |
public Object remove(Object key) | verwijdert het (sleutel, waarde)-paar waarbij sleutel = key |
public String toString() | identificeert het woordenboek |
Hier is een voorbeeld:
// de geïmporteerde klassen
import java.util.*;
public class test1{
// het hoofdprogramma main - static - klassemethode
public static void main(String arg[]){
// het aanmaken van objecten (instanties van klassen)
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());
// polymorfisme
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// een woordenboek
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++;
}
}//einde van main
}//einde klasse
De verkregen resultaten zijn als volgt:
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. De tekstbestanden
4.9.1. Schrijven
Om naar een bestand te schrijven, moet je beschikken over een schrijfstream. Hiervoor kun je de klasse FileWriter gebruiken. De meest gebruikte constructors zijn de volgende:
FileWriter(String fileName) | maakt het bestand met de naam fileName aan – je kunt er vervolgens in schrijven – een eventueel bestaand bestand met dezelfde naam wordt overschreven |
FileWriter(String fileName, boolean append) | idem – een eventueel bestaand bestand met dezelfde naam kan worden gebruikt door het in de toevoegmodus te openen (append=true) |
De klasse FileWriter biedt een aantal methoden om naar een bestand te schrijven, methoden die zijn overgenomen van de klasse Writer. Om naar een tekstbestand te schrijven, kun je het beste de klasse PrintWriter gebruiken, waarvan de vaak gebruikte constructors de volgende zijn:
PrintWriter(Writer out) | het argument is van het type Writer, c.a.d. een schrijfstroom (naar een bestand, via het netwerk, …) |
PrintWriter(Writer out, boolean autoflush) | idem. Het tweede argument regelt het bufferen van de regels. Wanneer het op 'false' staat (de standaardinstelling), worden de regels die naar het bestand worden geschreven, eerst in een buffer in het geheugen opgeslagen. Zodra deze buffer vol is, wordt de inhoud naar het bestand geschreven. Dit verbetert de schijftoegang. Soms is dit gedrag echter ongewenst, met name wanneer er via het netwerk wordt geschreven. |
De nuttige methoden van de klasse PrintWriter zijn de volgende:
void print(Type T) | schrijft de gegevens T (String, int, …) |
void println(Type T) | idem, maar eindigt met een regeleinde |
void flush() | maakt de buffer leeg als we niet in de autoflush-modus zitten |
void close() | sluit de schrijfstroom |
Hier is een programma dat enkele regels naar een tekstbestand schrijft:
// imports
import java.io.*;
public class ecrire{
public static void main(String[] arg){
// het bestand openen
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// schrijven naar het bestand
try{
fic.println("Jean,Dupont,27");
fic.println("Pauline,Garcia,24");
fic.println("Gilles,Dumond,56");
} catch (Exception e){
Erreur(e,3);
}
// bestand sluiten
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// einde main
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Fout
}//klasse
Het bestand out dat bij de uitvoering is verkregen, ziet er als volgt uit:
4.9.2. Lezen
Om de inhoud van een bestand te lezen, moet er een leesstroom aan het bestand zijn gekoppeld. Hiervoor kan de klasse FileReader en de volgende constructor worden gebruikt:
FileReader(String bestandsnaam) | opent een leesstroom vanuit het opgegeven bestand. Genereert een uitzondering als de bewerking mislukt. |
De klasse FileReader beschikt over een aantal methoden om uit een bestand te lezen, methoden die zijn overgenomen van de klasse Reader. Om tekstregels uit een tekstbestand te lezen, kunt u het beste de klasse BufferedReader gebruiken met de volgende constructor:
BufferedReader(Reader in) | opent een gebufferde leesstroom vanuit een invoerstroom van het type in. Deze stroom van het type Reader kan afkomstig zijn van het toetsenbord, een bestand, het netwerk, ... |
De nuttige methoden van de klasse BufferedReader zijn de volgende:
int read() | leest een teken |
String readLine() | leest een regel tekst |
int read(char[] buffer, int offset, int grootte) | leest taille tekens uit het bestand en plaatst deze in de array buffer vanaf positie offset. |
void close() | sluit de leesstroom |
Hier volgt een programma dat de inhoud van het eerder aangemaakte bestand leest:
// geïmporteerde klassen
import java.util.*;
import java.io.*;
public class lire{
public static void main(String[] arg){
personne p=null;
// bestand geopend
BufferedReader IN=null;
try{
IN=new BufferedReader(new FileReader("out"));
} catch (Exception e){
Erreur(e,1);
}
// gegevens
String ligne=null;
String[] champs=null;
String prenom=null;
String nom=null;
int age=0;
// afhandeling van eventuele fouten
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));
}// einde while
} catch (Exception e){
Erreur(e,2);
}
// bestand sluiten
try{
IN.close();
} catch (Exception e){
Erreur(e,3);
}
}// einde main
// Fout
public static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}
}// einde klasse
De uitvoering van het programma levert de volgende resultaten op:
4.9.3. Een persoonopslagobject opslaan
We passen wat we zojuist hebben gezien toe om de klasse personne te voorzien van een methode waarmee de kenmerken van een persoon in een bestand kunnen worden opgeslagen. We voegen de methode sauveAttributs toe aan de definitie van de klasse personne:
// ------------------------------
// opslaan in tekstbestand
// ------------------------------
public void sauveAttributs(PrintWriter P){
P.println(""+this);
}
Voorafgaand aan de definitie van de klasse personne, mogen we niet vergeten het pakket java.io te importeren:
De methode sauveAttributs ontvangt als enige parameter de stream PrintWriter waarin ze moet schrijven. Een testprogramma zou er als volgt uit kunnen zien:
// imports
import java.io.*;
// persoon importeren;
public class sauver{
public static void main(String[] arg){
// bestand openen
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// schrijven naar het bestand
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);
}
// bestand sluiten
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// einde main
// Fout
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Fout
}//klasse
Laten we dit programma compileren en uitvoeren:
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. De binaire bestanden
4.10.1. De klasse RandomAccessFile
Met de klasse RandomAccessFile kunt u binaire bestanden beheren, met name bestanden met een vaste structuur zoals we die kennen uit de programmeertaal C/C++. Hier volgen enkele nuttige methoden en constructors:
RandomAccessFile(String bestandsnaam, String modus) | constructor – opent het opgegeven bestand in de opgegeven modus. Deze modus kan de volgende waarden aannemen: r: openen voor lezen rw: openen voor lezen en schrijven |
void writeTTT(TTT waarde) | schrijft waarde naar het bestand. TTT vertegenwoordigt het type van waarde. De geheugenweergave van waarde wordt ongewijzigd naar het bestand geschreven. Zo komen we writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat, ... Om een tekenreeks te schrijven, gebruikt men writeBytes(String tekenreeks). |
TTT readTTT() | leest en retourneert een waarde van het type TTT. Zo zijn er readBoolean, readByte, readInt, readDouble, readLong, readFloat,... De methode read() leest een byte. |
long length() | bestandsgrootte in bytes |
long getFilePointer() | huidige positie van de bestandsaanwijzer |
void seek(long pos) | zet de bestandsaanwijzer op byte pos |
4.10.2. De klasse 'article'
In alle volgende voorbeelden wordt de volgende klasse article gebruikt:
// de artikelstructuur
private static class article{
// de structuur wordt gedefinieerd
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//artikelklasse
De bovenstaande Java-klasse article komt overeen met de volgende C-structuur article
struct article{
char code[4];
char nom[20];
double prix;
int stockActuel;
int stockMinimum;
}//structuur
Zo beperken we de code tot 4 tekens en de naam tot 20.
4.10.3. Een record schrijven
Het volgende programma schrijft een item naar een bestand met de naam "data":
// geïmporteerde klassen
import java.io.*;
public class test1{
// test het schrijven van een structuur (in de zin van C) naar een binair bestand
// de artikel-structuur
private static class article{
// we definiëren de structuur
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//klasse artikel
public static void main(String arg[]){
// we definiëren het binaire bestand waarin de artikelen zullen worden opgeslagen
RandomAccessFile fic=null;
// we definiëren een artikel
article art=new article();
art.code="a100";
art.nom="velo";
art.prix=1000.80;
art.stockActuel=100;
art.stockMinimum=10;
// het bestand wordt gedefinieerd
try{
fic=new RandomAccessFile("data","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// er wordt geschreven
try{
ecrire(fic,art);
} catch (IOException E){
erreur("Erreur lors de l'écriture de l'enregistrement",2);
}//try-catch
// het is klaar
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}//main
// schrijfmethode
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// code
fic.writeBytes(art.code);
// de naam is beperkt tot 20 tekens
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);
// de prijs
fic.writeDouble(art.prix);
// voorraad
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// einde schrijven
// ------------------------fout
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// einde fout
}// einde klasse
Met het volgende programma kunnen we controleren of de uitvoering correct is verlopen.
4.10.4. Een record lezen
// geïmporteerde klassen
import java.io.*;
public class test2{
// test het schrijven van een structuur (in de zin van C) naar een binair bestand
// de structuur ‘article’
private static class article{
// we definiëren de structuur
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//klasse artikel
public static void main(String arg[]){
// we definiëren het binaire bestand waarin de artikelen zullen worden opgeslagen
RandomAccessFile fic=null;
// het bestand wordt geopend in leesmodus
try{
fic=new RandomAccessFile("data","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// het enige artikel uit het bestand wordt gelezen
article art=new article();
try{
lire(fic,art);
} catch (IOException E){
erreur("Erreur lors de la lecture de l'enregistrement",2);
}//try-catch
// we geven het gelezen record weer
affiche(art);
// klaar
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}// einde handgeschreven
// leesmethode
public static void lire(RandomAccessFile fic, article art) throws IOException{
// code lezen
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// naam
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prijs
art.prix=fic.readDouble();
// voorraden
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// einde schrijven
// ---------------------weergave
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);
}// einde weergave
// ------------------------fout
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// einde fout
}// einde klasse
De uitvoerresultaten zijn als volgt:
E:\data\serge\JAVA\random>java test2
code : a100
nom : velo
prix : 1000.8
Stock actuel : 100
Stock minimum : 10
Het record dat door het schrijfprogramma was geschreven, wordt inderdaad opgehaald.
4.10.5. Conversie van tekst naar binair
Het volgende programma is een uitbreiding van het programma voor het schrijven van een record. We schrijven nu meerdere records naar een binair bestand met de naam data.bin. De gegevens zijn afkomstig uit het volgende bestand 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
// geïmporteerde klassen
import java.io.*;
import java.util.*;
public class test3{
// tekstbestand --> binair bestand
// de artikelstructuur
private static class article{
// de structuur wordt gedefinieerd
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//artikelklasse
public static void main(String arg[]){
// het binaire bestand waarin de artikelen worden opgeslagen, wordt gedefinieerd
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin",1);
}
// de gegevens worden uit een tekstbestand gehaald
BufferedReader dataTxt=null;
try{
dataTxt=new BufferedReader(new FileReader("data.txt"));
} catch (IOException E){
erreur("Impossible d'ouvrir le fichier data.txt",2);
}
// .txt-bestand --> .bin-bestand
String ligne=null;
String[] champs=null;
int numLigne=0;
String champ=null;
article art=new article(); // aan te maken artikel
try{
while((ligne=dataTxt.readLine())!=null){
// een regel met +
numLigne++;
// opsplitsing in velden
champs=ligne.split(":");
// er zijn 5 velden nodig
if(champs.length!=5)
erreur("Ligne "+numLigne+" erronée dans data.txt",3);
//code
art.code=champs[0];
if(art.code.length()!=4)
erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
// achternaam, voornaam
art.nom=champs[1];
// prijs
try{
art.prix=Double.parseDouble(champs[2]);
} catch (Exception E){
erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
}
// huidige voorraad
try{
art.stockActuel=Integer.parseInt(champs[3]);
} catch (Exception E){
erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
}
// huidige voorraad
try{
art.stockActuel=Integer.parseInt(champs[3]);
} catch (Exception E){
erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
}
// het record wordt geschreven
try{
ecrire(dataBin,art);
} catch (IOException E){
erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
}
// ga naar de volgende regel
}// einde while
} catch (IOException E){
erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
}
// klaar
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);
}
}// einde main
// schrijfmethode
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// code
fic.writeBytes(art.code);
// de naam is beperkt tot 20 tekens
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);
// de prijs
fic.writeDouble(art.prix);
// voorraad
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// einde schrijven
// ------------------------fout
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// einde fout
}// einde klasse
Met het volgende programma kun je controleren of het programma correct heeft gewerkt.
4.10.6. Conversie van binair naar tekst
Het volgende programma leest de inhoud van het eerder aangemaakte binaire bestand data.bin en zet deze inhoud in het tekstbestand data.text. Als alles goed gaat, moet het bestand data.text identiek zijn aan het oorspronkelijke bestand data.txt.
// geïmporteerde klassen
import java.io.*;
import java.util.*;
public class test5{
// tekstbestand --> binair bestand
// de artikelstructuur
private static class article{
// de structuur wordt gedefinieerd
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//artikelklasse
// main
public static void main(String arg[]){
// het binaire bestand waarin de artikelen worden opgeslagen, wordt gedefinieerd
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
}
// de gegevens worden naar een tekstbestand geschreven
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-bestand --> .text-bestand
article art=new article(); // aan te maken artikel
// het binaire bestand wordt verwerkt
int numRecord=0;
long l=0; // bestandsgrootte
try{
l=dataBin.length();
} catch (IOException e){
erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
}
long pos=0; // huidige positie in het bestand
try{
pos=dataBin.getFilePointer();
} catch (IOException e){
erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
}
// zolang het einde van het bestand nog niet is bereikt
while(pos<l){
// het huidige record lezen en verwerken
numRecord++;
try{
lire(dataBin,art);
} catch (Exception e){
erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
}
affiche(art);
// de bijbehorende tekstregel schrijven naar dataTxt
dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);
// gaan we verder?
try{
pos=dataBin.getFilePointer();
} catch (IOException e){
erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
}
}// einde while
// klaar
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);
}
}// einde main
// leesmethode
public static void lire(RandomAccessFile fic, article art) throws IOException{
// code lezen
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// naam
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prijs
art.prix=fic.readDouble();
// voorraden
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// einde schrijven
// ---------------------weergave
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);
}// einde weergave
// ------------------------fout
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// einde fout
}// einde klasse
Hier volgt een voorbeeld van de uitvoering:
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. Directe toegang tot records
Dit laatste programma illustreert de mogelijkheid om rechtstreeks toegang te krijgen tot de records van een binair bestand. Het geeft het record weer van het bestand data.bin, waarvan het nummer als parameter wordt doorgegeven, waarbij het eerste record het nummer 1 draagt.
// geïmporteerde klassen
import java.io.*;
import java.util.*;
public class test6{
// tekstbestand --> binair bestand
// de artikelstructuur
private static class article{
// de structuur wordt gedefinieerd
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//artikelklasse
// main
public static void main(String[] args){
// de argumenten worden gecontroleerd
int nbArguments=args.length;
String syntaxe="syntaxe : pg numéro_de_fiche";
if(nbArguments!=1)
erreur(syntaxe,20);
// controle van het stamgegevensnummer
int numRecord=0;
try{
numRecord=Integer.parseInt(args[0]);
} catch(Exception e){
erreur(syntaxe+"\nNuméro de fiche incorrect",21);
}
// het binaire bestand wordt geopend in leesmodus
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
}
// ga naar het gewenste bestand
try{
dataBin.seek((numRecord-1)*40);
} catch (Exception e){
erreur("La fiche "+numRecord+" n'existe pas",23);
}
// het wordt gelezen
article art=new article();
try{
lire(dataBin,art);
} catch (Exception e){
erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
}
// we geven het weer
affiche(art);
// klaar
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",2);
}//try-catch
}// einde main
// leesmethode
public static void lire(RandomAccessFile fic, article art) throws IOException{
// code lezen
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// naam
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// prijs
art.prix=fic.readDouble();
// voorraden
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// einde schrijven
// ---------------------weergave
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);
}// einde weergave
// ------------------------fout
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// einde fout
}// einde klasse
Hier volgen enkele voorbeelden van uitvoeringen:
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. Reguliere expressies gebruiken
4.11.1. Het pakket java.util.regex
Het pakket java.util.regex maakt het gebruik van reguliere expressies mogelijk. Hiermee kan het formaat van een tekenreeks worden getoetst. Zo kan worden gecontroleerd of een tekenreeks die een datum weergeeft, daadwerkelijk de indeling dd/mm/jj heeft. Hiervoor wordt een patroon gebruikt en wordt de tekenreeks met dit patroon vergeleken. In dit voorbeeld moeten j, m en a dus cijfers zijn. Het patroon voor een geldige datumnotatie is dan "\d\d/\d\d/\d\d", waarbij het symbool \d een cijfer aangeeft. De volgende symbolen kunnen in een patroon worden gebruikt (Microsoft-documentatie):
Teken | Beschrijving |
\ | Markeer het volgende teken als speciaal teken of letterlijk teken. Bijvoorbeeld: "n" staat voor het teken "n". "\n" staat voor een nieuwe regel. De reeks "\\" staat voor "\", terwijl "\(" staat voor "(". |
^ | Komt overeen met het begin van de invoer. |
$ | Komt overeen met het einde van de invoer. |
* | Komt nul of meerdere keren overeen met het voorgaande teken. Zo komt "zo*" overeen met "z" of met "zoo". |
+ | Komt één of meerdere keren overeen met het voorgaande teken. Zo komt "zo+" overeen met "zoo", maar niet met "z". |
? | Komt overeen met het voorgaande teken nul of één keer. Bijvoorbeeld: "a?ve?" komt overeen met "ve" in "lever". |
. | Komt overeen met elk willekeurig teken, behalve het regeleinde-teken. |
(sjabloon) | Zoekt naar modèle en slaat de overeenkomst op. De overeenkomende subreeks kan uit de verkregen verzameling Matches worden gehaald met behulp van Item [0]...[n]. Om overeenkomsten te vinden met tekens tussen haakjes ( ), gebruikt u "\(" of "\)". |
x|y | Komt overeen met x of met y. Bijvoorbeeld: "z|foot" komt overeen met "z" of met "foot". "(z|f)oo" komt overeen met "zoo" of met "foo". |
{n} | n is een niet-negatief geheel getal. Komt exact overeen met n maal het teken. Bijvoorbeeld: "o{2}" komt niet overeen met "o" in "Bob," maar met de eerste twee "o" in "fooooot". |
{n,} | n is een niet-negatief geheel getal. Komt overeen met ten minste n keer het teken. Bijvoorbeeld: "o{2,}" komt niet overeen met "o" in "Bob", maar met alle "o" in "fooooot". "o{1,}" is gelijk aan "o+" en "o{0,}" is gelijk aan "o*". |
{n,m} | m en n zijn niet-negatieve gehele getallen. Komt overeen met ten minste n en ten hoogste m keer het teken. Bijvoorbeeld: "o{1,3}" komt overeen met de eerste drie "o" in "foooooot" en "o{0,1}" komt overeen met "o?". |
[xyz] | Tekenset. Komt overeen met een van de opgegeven tekens. Bijvoorbeeld: "[abc]" komt overeen met "a" in "plat". |
[^xyz] | Negatieve tekenset. Komt overeen met elk teken dat niet is vermeld. Bijvoorbeeld: "[^abc]" komt overeen met "p" in "plat". |
[a-z] | Tekenreeks. Komt overeen met elk teken in de opgegeven reeks. Bijvoorbeeld: "[a-z]" komt overeen met elk klein letterteken tussen "a" en "z". |
[^m-z] | Negatief tekenbereik. Komt overeen met elk teken dat niet in de opgegeven reeks voorkomt. Bijvoorbeeld: "[^m-z]" komt overeen met elk teken dat niet tussen "m" en "z" voorkomt. |
\b | Komt overeen met een grens die een woord aangeeft, met andere woorden, de positie tussen een woord en een spatie. Bijvoorbeeld: "er\b" komt overeen met "er" in "lever", maar niet met "er" in "verbe". |
\B | Komt overeen met een grens die geen woord vertegenwoordigt. "en*t\B" komt overeen met "ent" in "bien entendu". |
\d | Komt overeen met een teken dat een cijfer vertegenwoordigt. Komt overeen met [0-9]. |
\D | Komt overeen met een teken dat geen cijfer vertegenwoordigt. Komt overeen met [^0-9]. |
\f | Komt overeen met een pagina-einde-teken. |
\n | Komt overeen met een regeleinde-teken. |
\r | Komt overeen met een carriage return-teken. |
\s | Komt overeen met alle witruimte, inclusief spatie, tab, pagina-einde, enz. Komt overeen met "[ \f\n\r\t\v]". |
\S | Komt overeen met elk niet-wit spatiekarakter. Komt overeen met "[^ \f\n\r\t\v]". |
\t | Komt overeen met een tabteken. |
\v | Komt overeen met een verticaal tabteken. |
\w | Komt overeen met elk teken dat een woord vertegenwoordigt, inclusief een onderstrepingsteken. Komt overeen met "[A-Za-z0-9_]". |
\W | Komt overeen met elk teken dat geen woord vertegenwoordigt. Komt overeen met "[^A-Za-z0-9_]". |
\num | Komt overeen met num, waarbij num een positief geheel getal is. Verwijst naar de opgeslagen overeenkomsten. Bijvoorbeeld: „(.)\1” komt overeen met twee opeenvolgende identieke tekens. |
|
Een element in een sjabloon kan één of meerdere keren voorkomen. Laten we enkele voorbeelden bekijken rond het symbool \d, dat één cijfer vertegenwoordigt:
sjabloon | betekenis |
\d | een cijfer |
\d? | 0 of 1 cijfer |
\d* | 0 of meer cijfers |
\d+ | 1 of meer cijfers |
\d{2} | 2 cijfers |
\d{3,} | minstens 3 cijfers |
\d{5,7} | tussen 5 en 7 cijfers |
Laten we nu eens kijken naar het patroon waarmee het verwachte formaat van een tekenreeks kan worden beschreven:
gezochte tekenreeks | model |
een datum in de indeling dd/mm/jj | \d{2}/\d{2}/\d{2} |
een tijd in de notatie hh:mm:ss | \d{2}:\d{2}:\d{2} |
een geheel getal zonder teken | \d+ |
een reeks spaties, eventueel leeg | \s* |
een geheel getal zonder teken dat voorafgegaan of gevolgd kan worden door spaties | \s*\d+\s* |
een geheel getal dat een teken kan hebben en voorafgegaan of gevolgd kan worden door spaties | \s*[+|-]?\s*\d+\s* |
een reëel getal zonder teken dat voorafgegaan of gevolgd kan worden door spaties | \s*\d+(.\d*)?\s* |
een reëel getal dat een teken kan hebben en voorafgegaan of gevolgd kan worden door spaties | \s*[+|]?\s*\d+(.\d*)?\s* |
een tekenreeks die het woord 'juste' bevat | \bjuste\b |
Je kunt aangeven waar in de tekenreeks naar het patroon wordt gezocht:
patroon | betekenis |
^patroon | het patroon begint in de tekenreeks |
patroon$ | het patroon beëindigt de reeks |
^patroon$ | het patroon begint en eindigt de reeks |
patroon | het patroon wordt overal in de tekenreeks gezocht, te beginnen bij het begin ervan. |
gezochte tekenreeks | patroon |
een tekenreeks die eindigt met een uitroepteken | !$ |
een tekenreeks die eindigt op een punt | \.$ |
een tekenreeks die begint met de reeks // | ^// |
een tekenreeks die uit slechts één woord bestaat, eventueel gevolgd of voorafgegaan door spaties | ^\s*\w+\s*$ |
een tekenreeks die bestaat uit twee woorden, eventueel voorafgegaan of gevolgd door spaties | ^\s*\w+\s*\w+\s*$ |
een tekenreeks die het woord 'secret' bevat | \bsecret\b |
De deelverzamelingen van een patroon kunnen worden "opgehaald". Zo kan niet alleen worden gecontroleerd of een tekenreeks overeenkomt met een bepaald patroon, maar kunnen ook de elementen in die tekenreeks worden opgehaald die overeenkomen met de deelverzamelingen van het patroon die tussen haakjes zijn geplaatst. Als we bijvoorbeeld een tekenreeks analyseren die een datum dd/mm/jj bevat en we willen bovendien de elementen dd, mm en jj van deze datum ophalen, dan gebruiken we het patroon (\d\d)/(\d\d)/(\d\d).
4.11.2. Controleren of een tekenreeks overeenkomt met een bepaald patroon
Met de klasse Pattern kan worden gecontroleerd of een tekenreeks overeenkomt met een bepaald patroon. Hiervoor wordt de statische methode gebruikt
met: modèle: het te controleren patroon, chaine: de tekenreeks die met het patroon moet worden vergeleken. Het resultaat is de booleaanse waarde true als de tekenreeks overeenkomt met het patroon, false anders.
Hier volgt een voorbeeld:
import java.io.*;
import java.util.regex.*;
// beheer van reguliere expressies
public class regex1 {
public static void main(String[] args){
// een sjabloon voor een reguliere uitdrukking
String modèle1="^\\s*\\d+\\s*$";
// een exemplaar vergelijken met het sjabloon
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);
}//weergeven
}//klasse
en de uitvoerresultaten:
Merk op dat in het patroon "^\s*\d+\s*$" het teken \ moet worden verdubbeld vanwege de specifieke manier waarop Java dit teken interpreteert. We schrijven dus: String patroon1="^\\s*\\d+\\s*$";
4.11.3. Alle elementen in een tekenreeks vinden die aan een patroon voldoen
Laten we het patroon "\d+" en de tekenreeks " 123 456 789 " eens bekijken. Het patroon komt op drie verschillende plaatsen in de tekenreeks voor. Met de klassen Pattern en Matcher kunnen de verschillende voorkomens van een patroon in een tekenreeks worden opgehaald. De klasse Pattern is de klasse die reguliere expressies beheert. Een reguliere expressie die meer dan één keer wordt gebruikt, moet worden „gecompileerd”. Dit versnelt het zoeken naar het patroon in de tekenreeksen. De statische methode compile voert deze taak uit:
Deze methode neemt de tekenreeks van het patroon als parameter en retourneert een object van het type Pattern. Om het patroon van een object van het type Pattern te vergelijken met een tekenreeks, wordt de klasse Matcher gebruikt. Deze klasse maakt het mogelijk een patroon te vergelijken met een tekenreeks. Vanuit een object van het type Pattern is het mogelijk om met de methode matcher een object van het type Matcher te verkrijgen:
input is de tekenreeks die met het patroon moet worden vergeleken.
Om het patroon "\d+" te vergelijken met de tekenreeks " 123 456 789 ", kan men dus als volgt een object van het type Matcher aanmaken:
Op basis van het voorgaande object résultats kunnen we de verschillende voorkomens van het patroon in de tekenreeks ophalen. Hiervoor gebruiken we de methoden suivantes van de klasse Matcher:
De methode find zoekt in de doorzochte tekenreeks naar de eerste keer dat het patroon voorkomt. Bij een tweede aanroep van find wordt naar de volgende keer gezocht. En zo verder. De methode retourneert true als het patroon wordt gevonden, en false als dat niet het geval is. Het deel van de tekenreeks dat overeenkomt met de laatste door find gevonden voorkomen, wordt verkregen met de methode group en de positie ervan met de methode start. Als we het vorige voorbeeld voortzetten en alle voorkomens van het patroon "\d+" in de tekenreeks " 123 456 789 " willen weergeven, schrijven we dus:
while(résultats.find()){
System.out.println("séquence " + résultats.group() + " trouvée en position " + résultats.start());
}//while
Met de methode reset kan het object Matcher worden gereset naar het begin van de tekenreeks die met het patroon wordt vergeleken. Zo zal de methode find vervolgens opnieuw de eerste voorkoming van het patroon vinden.
Hier volgt een volledig voorbeeld:
import java.io.*;
import java.util.regex.*;
// beheer van reguliere expressies
public class regex2 {
public static void main(String[] args){
// meerdere exemplaren van het patroon in het document
String modèle2="\\d+";
Pattern regex2=Pattern.compile(modèle2);
String exemplaire3=" 123 456 789";
// zoeken naar voorkomen van het patroon in het exemplaar
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);
}//weergeven
}//klasse
De resultaten van de uitvoering:
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. Delen van een patroon ophalen
Deelverzamelingen van een patroon kunnen worden "opgehaald". Zo kan niet alleen worden gecontroleerd of een tekenreeks overeenkomt met een bepaald patroon, maar kunnen ook de elementen uit die tekenreeks worden opgehaald die overeenkomen met de deelverzamelingen van het patroon die tussen haakjes zijn geplaatst. Als we bijvoorbeeld een tekenreeks analyseren die een datum dd/mm/jj bevat en we willen bovendien de elementen dd, mm en jj van deze datum ophalen, dan gebruiken we het patroon (\d\d)/(\d\d)/(\d\d).
Laten we het volgende voorbeeld eens bekijken:
import java.io.*;
import java.util.regex.*;
// beheer van reguliere expressies
public class regex3 {
public static void main(String[] args){
// elementen in het sjabloon vastleggen
String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
Pattern regex3=Pattern.compile(modèle3);
String exemplaire4="Il est 18:05:49";
// sjablooncontrole
Matcher résultat=regex3.matcher(exemplaire4);
if (résultat.find()){
// het exemplaar komt overeen met het sjabloon
affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
// de groepen worden weergegeven
for (int i=0;i<=résultat.groupCount();i++){
affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
}//for
}else{
// het exemplaar komt niet overeen met het sjabloon
affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
}
}//Hoofd
public static void affiche(String msg){
System.out.println(msg);
}//geeft weer
}//sorteert
De uitvoering van dit programma levert de volgende resultaten op:
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
De wijziging zit in het volgende stukje code:
// sjablooncontrole
Matcher résultat=regex3.matcher(exemplaire4);
if (résultat.find()){
// het exemplaar komt overeen met het sjabloon
affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
// groepen worden weergegeven
for (int i=0;i<=résultat.groupCount();i++){
affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
}//voor
}else{
// het exemplaar komt niet overeen met het sjabloon
affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
}
De tekenreeks exemplaire4 wordt vergeleken met het patroon regex3 met behulp van de methode find. Er wordt vervolgens een exemplaar van het patroon regex3 gevonden in de tekenreeks exemplaire4. Als het patroon deelreeksen bevat die tussen haakjes staan, zijn deze beschikbaar via verschillende methoden van de klasse Matcher:
public int groupCount()
public String group(int group)
public int start(int group)
De methode groupCount geeft het aantal subassemblages weer dat in het model is gevonden en group(i) geeft subassemblage nr. i weer. Deze wordt in de tekenreeks gevonden op een positie die wordt aangegeven door start(i). Zoals in het voorbeeld:
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
Bij de eerste aanroep van de methode find wordt de tekenreeks 18:05:49 gevonden en worden automatisch de drie deelreeksen aangemaakt die door de haakjes in het sjabloon worden gedefinieerd, respectievelijk 18, 05 en 49.
4.11.5. Een oefenprogramma
Het vinden van de reguliere expressie waarmee we kunnen controleren of een tekenreeks aan een bepaald patroon voldoet, is soms een echte uitdaging. Met het volgende programma kun je oefenen. Het vraagt om een patroon en een tekenreeks en geeft vervolgens aan of de tekenreeks al dan niet aan het patroon voldoet.
import java.io.*;
import java.util.regex.*;
// beheer van reguliere expressies
public class regex4 {
public static void main(String[] args){
// gegevens
String modèle=null,chaine=null;
Pattern regex=null;
BufferedReader IN=null;
Matcher résultats=null;
int nbOccurrences=0;
// foutbeheer
try{
// de gebruiker wordt gevraagd welke sjablonen en exemplaren met dit exemplaar moeten worden vergeleken
while(true){
// invoerstroom
IN=new BufferedReader(new InputStreamReader(System.in));
// er wordt gevraagd om het sjabloon
System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
modèle=IN.readLine();
// klaar?
if(modèle.trim().toLowerCase().equals("fin")) break;
// de reguliere expressie wordt aangemaakt
regex=Pattern.compile(modèle);
// de gebruiker wordt gevraagd welke exemplaren met het sjabloon moeten worden vergeleken
while(true){
System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
chaine=IN.readLine();
// klaar?
if(chaine.trim().toLowerCase().equals("fin")) break;
// we maken het matcher-object aan
résultats=regex.matcher(chaine);
// er wordt gezocht naar exemplaren van het sjabloon
nbOccurrences=0;
while(résultats.find()){
// er is een exemplaar gevonden
nbOccurrences++;
// we geven deze weer
System.out.println("J'ai trouvé la correspondance ["+résultats.group()
+"] en position "+résultats.start());
// weergave van de subelementen
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
// volgende tekenreeks
}//while(résultats.find())
// is er ten minste één exemplaar gevonden?
if(nbOccurrences==0){
System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
}//if
// volgend patroon
}//while(true)
}//while(true)
}catch(Exception ex){
// fout
System.err.println("Erreur : "+ex.getMessage());
// einde met fout
System.exit(1);
}//try-catch
// einde
System.exit(0);
}//Main
}//klasse
Hier volgt een voorbeeld van de uitvoering:
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. De split-methode van de klasse Pattern
Laten we eens kijken naar een tekenreeks die bestaat uit velden die worden gescheiden door een scheidingsreeks die wordt uitgedrukt met behulp van een reguliere expressie. Als de velden bijvoorbeeld worden gescheiden door het teken , voorafgegaan of gevolgd door een willekeurig aantal spaties, dan zou de reguliere expressie die de scheidingsreeks van de velden modelleert "\s*,\s*" zijn. Met de methode split van de klasse Pattern kunnen we de velden in een array ophalen:
public String[] split(CharSequence input)
De tekenreeks input wordt opgesplitst in velden, die worden gescheiden door een scheidingsteken dat overeenkomt met het patroon van het huidige object Pattern. Om de velden op te halen van een regel waarvan het veldscheidingsteken een komma is, voorafgegaan of gevolgd door een willekeurig aantal spaties, schrijven we:
// een regel
String ligne="abc ,, def , ghi";
// een sjabloon
Pattern modèle=Pattern.compile("\\s*,\\s*");
// regels opsplitsen in velden
String[] champs=modèle.split(ligne);
Hetzelfde resultaat kan worden verkregen met de methode split van de klasse String:
public String[] split(String regex)
Hier volgt een testprogramma:
import java.io.*;
import java.util.regex.*;
// beheer van reguliere expressies
public class split1 {
public static void main(String[] args){
// een regel
String ligne="abc ,, def , ghi";
// een sjabloon
Pattern modèle=Pattern.compile("\\s*,\\s*");
// opdeling van een regel in velden
String[] champs=modèle.split(ligne);
// weergave
for(int i=0;i<champs.length;i++){
System.out.println("champs["+i+"]=["+champs[i]+"]");
}//for
// een andere manier om dit te doen
champs=ligne.split("\\s*,\\s*");
// weergave
for(int i=0;i<champs.length;i++){
System.out.println("champs["+i+"]=["+champs[i]+"]");
}//for
}//Hoofdpagina
}//klasse
De uitvoerresultaten:
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
4.12. Oefeningen
4.12.1. Oefening 1
Onder Unix worden programma's vaak als volgt aangeroepen:
$ pg -o1 v1 v2 ... -o2 v3 v4 …
waarbij -oi een optie vertegenwoordigt en vi een waarde die bij deze optie hoort. We willen een klasse options maken waarmee de argumentenreeks -o1 v1 v2 ... -o2 v3 v4 … kan worden geanalyseerd om de volgende entiteiten te construeren:
optionsValides | een woordenboek (Hashtable) waarvan de sleutels de geldige opties oi zijn. De waarde die aan de sleutel oi is gekoppeld, is een vector (Vector) waarvan de elementen de waarden v1 v2 … zijn die aan de optie -oi zijn gekoppeld |
optionsInvalides | woordenboek (Hashtable) waarvan de sleutels de ongeldige oi-opties zijn. De waarde die aan de sleutel oi is gekoppeld, is een vector (Vector) waarvan de elementen de waarden v1, v2 … zijn die aan de optie -oi zijn gekoppeld |
optionsSans | tekenreeks (String) met de lijst van waarden vi die niet aan een optie zijn gekoppeld |
erreur | een geheel getal met de waarde 0 als er geen fouten in de argumentenregel staan, anders iets anders: 1: er zijn ongeldige aanroepparameters 2: er zijn ongeldige opties 4: er zijn waarden die niet aan opties zijn gekoppeld Als er meerdere soorten fouten zijn, worden deze waarden bij elkaar opgeteld. |
Een optieobject kan op vier verschillende manieren worden aangemaakt:
public options (String arguments, String optionsAcceptables)
arguments | de te analyseren argumentenreeks -o1 v1 v2 ... -o2 v3 v4 … |
optionsAcceptables | de lijst met toegestane oi-opties |
Voorbeeld van een aanroep: opties opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");
Hier zijn beide argumenten tekenreeksen. Het is ook toegestaan dat deze tekenreeksen in woorden zijn opgesplitst en in een array van tekenreeksen zijn geplaatst. Hiervoor zijn drie andere constructors nodig:
public options (String[] arguments, String optionsAcceptables)
public options (String arguments, String[] optionsAcceptables)
public options (String[] arguments, String[] optionsAcceptables)
De klasse options zal de volgende interface (accessors) hebben:
geeft de referentie van de tabel optionsValides die bij het aanmaken van het object 'options' is aangemaakt
geeft de referentie van de array optionsInvalides die bij het aanmaken van het object 'options' is aangemaakt
geeft de referentie van de tekenreeks optionsSans weer, die is aangemaakt bij het aanmaken van het object 'options'
geeft de waarde van het attribuut ‘fout’ weer, dat is aangemaakt bij het aanmaken van het optieobject
geeft, indien er geen fout is, de waarden van de attributen optionsValides, optionsInvalides, optionsSans weer; anders wordt het foutnummer weergegeven.
Hier volgt een voorbeeldprogramma:
import java.io.*;
//importopties;
public class test1{
public static void main (String[] arg){
// de invoerstroom openen
String ligne;
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
affiche(e);
System.exit(1);
}
// argumenten van de constructor options(String, string) lezen
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));
}// einde while
}//einde van main
public static void affiche(Exception e){
System.err.println("Erreur : "+e);
}
}//einde klasse
Enkele resultaten:
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. Oefening 2
We willen een klasse stringtovector maken waarmee de inhoud van een object *String kan worden overgebracht naar een object *Vector. Deze klasse zou worden afgeleid van de klasse *Vector*:
en zou de volgende constructor hebben:
private void stringtovector(String S, String separateur, int[] tChampsVoulus,
boolean strict){
// maakt een vector aan met de velden van de tekenreeks S
// deze bestaat uit velden gescheiden door scheidingsteken
// als scheidingsteken=null, vormt de tekenreeks slechts één veld
// alleen de velden waarvan de indexen in de array tChampsVoulus
// zijn gewenst. De indexen beginnen bij 1
// als tChampsvoulus = null of de grootte nul is, worden alle velden opgenomen
// als strict=waar is, moeten alle gewenste velden aanwezig zijn
De klasse zou het volgende privé-attribuut hebben:
Dit attribuut wordt door de voorgaande constructor ingesteld met de volgende waarden:
0: de constructie is geslaagd
4: sommige vereiste velden ontbreken terwijl strict=true
De klasse zal ook twee methoden hebben:
die de waarde van het privé-attribuut erreur retourneert.
die de waarde van het object weergeeft in de vorm (fout, element 1, element 2, …) waarbij elementen i de elementen zijn van de vector die is opgebouwd uit de tekenreeks.
Een testprogramma zou er als volgt uit kunnen zien:
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());
}
}
De resultaten:
(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)
Enkele tips:
- Gebruik de methode split van de klasse String om de tekenreeks S in velden op te splitsen.
- Plaats de velden van S in een woordenboek D, geïndexeerd op veldnummer
- Haal uit het woordenboek D alleen die velden op waarvan de sleutel (index) voorkomt in de tabel tChampsVoulus.
4.12.3. Oefening 3
We willen de volgende constructor toevoegen aan de klasse stringtovector:
public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
// maakt een vector aan met de velden van de tekenreeks S
// deze bestaat uit velden die worden gescheiden door 'separator'
// als 'separator' = null, vormt de tekenreeks slechts één veld
// alleen de velden waarvan de indexen in sChampsVoulus staan, zijn gewenst
// de indexen beginnen bij 1
// als sChampsvoulus = null of "", worden alle velden opgenomen
// als strict=waar, moeten alle gewenste velden aanwezig zijn
De lijst met gewenste velden staat dus in een tekenreeks (String) in plaats van in een array van gehele getallen (int[]). Aan het privé-attribuut erreur van de klasse kan een nieuwe waarde worden toegekend:
2: de reeks indexen van de gewenste velden is onjuist
Hier is een voorbeeldprogramma:
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());
}
}
Enkele resultaten:
(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)
Enkele tips:
- We moeten teruggaan naar het geval van de vorige constructor door de velden van de tekenreeks sChampsVoulus over te brengen naar een array van gehele getallen. Hiervoor moet je sChampsVoulus opsplitsen in velden met behulp van een object StringTokenizer, waarvan het attribuut countTokens het aantal verkregen velden aangeeft. Vervolgens kun je een array van gehele getallen met de juiste afmetingen aanmaken en deze vullen met de verkregen velden.
- Om te controleren of een veld een geheel getal is, gebruik je de methode Integer.parseInt om het veld om te zetten in een geheel getal en verwerk je de uitzondering die wordt gegenereerd wanneer deze conversie niet mogelijk is.
4.12.4. Oefening 4
We willen een klasse **filetovector aanmaken waarmee de inhoud van een tekstbestand kan worden overgebracht naar een object *Vector. Deze klasse zou worden afgeleid van de klasse *Vector:
en zou de volgende constructor hebben:
// --------------------- constructor
public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
// maakt een vector aan met de regels uit het tekstbestand nomFichier
// de regels bestaan uit velden die worden gescheiden door een scheidingsteken
// als scheidingsteken=null, bestaat de regel uit slechts één veld
// alleen de velden waarvan de indexen in tChampsVoulus staan, zijn gewenst
// de indexen beginnen bij 1
// als tChampsvoulus = null of leeg is, worden alle velden opgenomen
// als strict=waar is, moeten alle gewenste velden aanwezig zijn
// als dat niet het geval is, wordt de regel niet opgeslagen en wordt de index
// wordt in de vector lignesErronees geplaatst
// lege regels worden genegeerd
// evenals de regels die beginnen met tagCommentaire als tagCommentaire != null
De klasse zou de volgende privé-attributen hebben:
Het attribuut erreur wordt door de vorige constructor ingesteld met de volgende waarden:
0: de constructie is geslaagd
1: het te verwerken bestand kon niet worden geopend
4: bepaalde vereiste velden ontbreken terwijl strict=true is ingesteld
8: er is een I/O-fout opgetreden tijdens het verwerken van het bestand
Het attribuut lignesErronees is een vector waarvan de elementen bestaan uit de nummers van de foutieve regels in de vorm van een tekenreeks. Een regel is foutief als deze de gevraagde velden niet kan leveren terwijl strict=true is ingesteld.
De klasse zal ook twee methoden hebben:
die de waarde van het privé-attribuut erreur retourneert.
die de waarde van het object weergeeft in de vorm (fout, element 1, element 2 …,(l1,l2,…)), waarbij elementen i de elementen zijn van de vector die op basis van het bestand is samengesteld en li de nummers van de foutieve regels.
Hier volgt een testvoorbeeld:
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());
}
}
De uitvoerresultaten:
[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,# opmerking) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# opmerking) (0,azerty,1,cvf,fff,qqqq) (0,s)]
Enkele tips
-
Het tekstbestand wordt regel voor regel verwerkt. De regel wordt in velden opgesplitst met behulp van de eerder besproken klasse
**stringtovector**. -
De elementen van de vector die op basis van het tekstbestand wordt samengesteld, zijn dus objecten van het type
**stringtovector**. -
De methode identite van filetovector kan gebruikmaken van de methode stringtovector.identite() om de elementen weer te geven, en van de methode Vector.toString() om de nummers van eventuele foutieve regels weer te geven.
4.12.5. Oefening 5
We willen de volgende constructor aan de klasse **filetovector** toevoegen:
public filetovector(String nomFichier, String separateur, String sChampsVoulus,
boolean strict, String tagCommentaire){
// maakt een vector aan met de regels uit het tekstbestand nomFichier
// de regels bestaan uit velden gescheiden door een scheidingsteken
// als scheidingsteken=null, bestaat de regel uit slechts één veld
// alleen de velden waarvan de indexen in tChampsVoulus staan, zijn gewenst
// de indexen beginnen bij 1
// als sChampsvoulus = null of leeg is, worden alle velden opgenomen
// als strict=waar, moeten alle gewenste velden aanwezig zijn
// als dat niet het geval is, wordt de regel niet opgeslagen en wordt de index
// wordt in de vector lignesErronees geplaatst
// lege regels worden genegeerd
// evenals de regels die beginnen met tagCommentaire als tagCommentaire != null
De lijst met indexen van de gewenste velden staat nu in een tekenreeks (String) in plaats van in een array van gehele getallen.
Het privé-attribuut erreur kan een extra waarde hebben:
2: de tekenreeks met de indexen van de gewenste velden is onjuist
Hier is een voorbeeld van een 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());
}
}
De resultaten:
[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,# opmerking) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# opmerking) (0,azerty,1,cvf,fff,qqqq) (0,s)]
Enkele tips
- We zetten de tekenreeks sChampsVoulus om in een array van gehele getallen tChampVoulus om terug te keren naar het geval van de vorige constructor.