Skip to content

4. Classes de uso comum

Neste capítulo, apresentamos várias classes Java de uso comum. Estas possuem numerosos atributos, métodos e construtores. Em cada caso, apresentamos apenas uma pequena parte das classes. Os detalhes destas estão disponíveis na ajuda do Java que apresentamos a seguir.

4.1. A documentação

Se tiver instalado o JDK da Sun na pasta <jdk>, a documentação está disponível na pasta <jdk>\docs:

Image

Por vezes, existe um ficheiro jdk, mas sem documentação. Esta pode ser encontrada no site da Sun http://www.sun.com. Na pasta docs encontra-se um ficheiro index.html, que constitui o ponto de partida da ajuda do JDK:

Image

Image

A ligação API & Language acima dá acesso às classes Java. A ligação Demos/Tutorials é particularmente útil para obter exemplos de programas Java. Sigamos a ligação API & Language:

Image

Vamos seguir a ligação «Java 2 Platform» API:

Image

Esta página é o verdadeiro ponto de partida da documentação sobre as classes. Pode criar um atalho para esta página para ter acesso rápido à mesma. O URL encontra-se em <jdk>\docs\api\index.html. Aqui encontram-se ligações para centenas de classes Java do JDK. Quando se está a dar os primeiros passos, a principal dificuldade é saber o que fazem estas diferentes classes. Numa primeira fase, esta ajuda só tem utilidade se se souber o nome da classe sobre a qual se pretende obter informações. Também é possível orientar-se pelos nomes das classes, que normalmente indicam a função da mesma.

Vejamos um exemplo e procuremos informações sobre a classe Vector, que implementa um tabuleiro dinâmico. Basta procurar na lista de classes do painel esquerdo o link da classe Vector:

Image

e clicar no link para obter a definição da classe:

Image

Aí encontra-se

  • a hierarquia em que a classe se insere, neste caso java.util.Vector
  • a lista de campos (atributos) da classe
  • a lista de construtores
  • a lista de métodos

A seguir, apresentamos várias classes. Convidamos o leitor a verificar sistematicamente a definição completa das classes utilizadas.

4.2. As classes de teste

Os exemplos que se seguem utilizam, por vezes, as classes personne e enseignant. Recordamos aqui a sua definição.

public class personne{
    // apelido, nome próprio, idade
  private String prenom;
  private String nom;
  private int age;

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

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

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

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

A classe enseignant deriva da classe personne e está definida da seguinte forma:

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

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

Iremos também utilizar uma classe etudiant derivada da classe personne e definida da seguinte forma:


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. A classe String

A classe String representa cadeias de caracteres. Seja nom uma variável de cadeia de caracteres:

String nome;

nom é uma referência a um objeto ainda não inicializado. É possível inicializá-lo de duas formas:

nom="cavalo" ou nom=new String("cavalo")

Ambos os métodos são equivalentes. Se, mais tarde, escrevermos nom=&quot;peixe&quot;, ***nom*** passará a referenciar um novo objeto. O objeto anterior, *String(&quot;cheval&quot;),* é perdido e o espaço de memória que ocupava será recuperado.

A classe String possui diversos atributos e métodos. Aqui estão alguns deles:

public char charAt(int i)
retorna o caractere i da cadeia, sendo que o primeiro caractere tem o índice 0. Assim, String("cavalo").charAt(3) é igual a 'v'
public int compareTo(cadeia2)
chaine1.compareTo(chaine2) compara chaine1 com chaine2 e devolve 0 se chaine1 = chaine2, 1 se chaine1 > chaine2 e -1 se chaine1 < chaine2
public boolean equals(Object anObject)
chaine1.equals(cadeia2) retorna verdadeiro se cadeia1 = cadeia2, falso caso contrário
public String toLowerCase()
chaine1.toLowerCase() converte a cadeia1 em minúsculas
public String toUpperCase()
chaine1.toUpperCase() converte a string1 em maiúsculas
public String trim()
chaine1.trim() remove os espaços à esquerda e à direita da string1
public String substring(int beginIndex, int endIndex)
String("chapeau").subString(2,4) devolve a cadeia "ape"
public char[] toCharArray()
permite colocar os caracteres da cadeia numa matriz de caracteres
int length()
número de caracteres da cadeia
int indexOf(String chaine2)
retorna a primeira posição de chaine2 na cadeia atual ou -1 se chaine2 não estiver presente
int indexOf(String cadeia2, int startIndex)
retorna a primeira posição de chaine2 na cadeia atual ou -1 se chaine2 não estiver presente. A pesquisa começa a partir do carácter n.º startIndex.
int lastIndexOf(String cadeia2)
retorna a última posição de chaine2 na cadeia atual ou -1 se chaine2 não estiver presente
boolean startsWith(String cadeia2)
retorna verdadeiro se a cadeia atual começar por chaine2
boolean endsWith(String cadeia2)
retorna verdadeiro se a cadeia atual terminar em chaine2
boolean matches(String regex)
retorna verdadeiro se a cadeia atual corresponder à expressão regular regex.
String[] split(String regex)
A cadeia atual é composta por campos separados por uma sequência de caracteres modelada pela expressão regular regex. O método split permite recuperar os campos numa matriz.
String replace(char oldChar, char newChar)
substitui, na cadeia atual, o caractere oldChar pelo caractere newChar.

Eis um programa de exemplo:

// importações
import java.io.*;

public class string1{
    // uma classe de demonstração
    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]+"]");
        }//para
        affiche("(\"  abc  \").trim()=["+"  abc  ".trim()+"]");
    }//Main

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

e os resultados obtidos:

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. A classe Vector

Um vetor é um array dinâmico cujos elementos são referências a objetos. Trata-se, portanto, de um array de objetos cujo tamanho pode variar ao longo do tempo, o que não é possível com os arrays estáticos que vimos até agora. Aqui estão alguns campos, construtores ou métodos desta classe:

public Vector()
constrói um vetor vazio
public final int size()
número de elementos do vetor
public final void addElement(Object obj)
adiciona o objeto referenciado por obj ao vetor
public final Object elementAt(int index)
referência ao objeto n.º index do vetor — os índices começam em 0
public final Enumeration elements()
o conjunto de elementos do vetor sob a forma de uma enumeração
public final Object firstElement()
referência ao primeiro elemento do vetor
public final Object lastElement()
referência ao último elemento do vetor
public final boolean isEmpty()
retorna «true» se o vetor estiver vazio
public final void removeElementAt(int index)
remove o elemento com o índice index
public final void removeAllElements()
esvazia o vetor de todos os seus elementos
public final String toString()
retorna uma cadeia de caracteres que identifica o vetor

Eis um programa de teste:


// as classes importadas
import java.util.*;

public class test1{

// o programa principal main - estático - método de classe

  public static void main(String arg[]){

// a criação de objetos (instâncias de classes)
    personne p=new personne("Jean","Dupont",30);
    enseignant en=new enseignant("Paula","Hanson",56,27);
    etudiant et=new etudiant("Chris","Garot",22,"19980405");
    System.out.println("p="+p.toString());
    System.out.println("en="+en.toString());
    System.out.println("et="+et.toString());

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

// um vetor
    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());
    }
} // fim de main
}// fim da classe

Vamos compilar este programa:

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

Vamos executar o ficheiro 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)

Daqui em diante, não repetiremos o processo de compilação e execução dos programas de teste. Basta repetir o que foi feito acima.

4.5. A classe ArrayList

A classe ArrayList é análoga à classe Vector. A única diferença significativa surge quando é utilizada simultaneamente por várias threads de execução. Os métodos de sincronização das threads para o acesso a um Vector ou a um ArrayList diferem. Fora deste caso, pode utilizar-se indistintamente um ou outro. Aqui estão alguns campos, construtores ou métodos desta classe:

ArrayList()
constrói uma lista vazia
int size()
número de elementos da lista
void add(Object obj)
adiciona o objeto referenciado por obj à lista
void add(int index, Object obj)
adiciona o objeto referenciado por obj à tabela na posição index
Object get(int index)
referência do objeto n.º index da tabela — os índices começam em 0
boolean isEmpty()
retorna verdadeiro se a tabela estiver vazia
void remove(int index)
remove o elemento com o índice index
void clear()
esvazia a matriz de todos os seus elementos
Object[] toArray()
converte a matriz dinâmica numa matriz clássica
String toString()
retorna uma cadeia de caracteres que identifica a matriz

Eis um programa de teste:

// as classes importadas
import java.util.*;

public class test1{

// o programa principal main - estático - método de classe

  public static void main(String arg[]){

// a criação de objetos instâncias de classes
    personne p=new personne("Jean","Dupont",30);
    enseignant en=new enseignant("Paula","Hanson",56,27);
    etudiant et=new etudiant("Chris","Garot",22,"19980405");
    System.out.println("p="+p);
    System.out.println("en="+en);
    System.out.println("et="+et);

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

// um vetor
    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);
    }
} // fim de main
}// fim da classe

Os resultados obtidos são os mesmos que anteriormente.

4.6. A classe Arrays

A classe java.util.Arrays dá acesso a métodos estáticos que permitem realizar diversas operações em tabelas, nomeadamente ordenações e pesquisas de elementos. Aqui estão alguns desses métodos:

static void sort(matriz)
ordena o tableau utilizando, para tal, a ordem implícita do tipo de dados da matriz, seja um número ou cadeias de caracteres.
static void sort (Object[] tabela, Comparator C)
ordena o tableau utilizando a função de comparação C para comparar os elementos
static int binarySearch(tabela, elemento)
retorna a posição de élément em tableau ou um valor <0 caso contrário. A matriz deve estar previamente ordenada.
static int binarySearch(Object[] tabela, Object elemento, Comparator C)
O mesmo, mas utiliza a função de comparação C para comparar dois elementos da matriz.

Eis um primeiro exemplo:

import java.util.*;

public class sort2 implements Comparator{

   // uma classe privada interna
  private class personne{
    private String nom;
    private int age;
    public personne(String nom, int age){
      this.nom=nom; // nome da pessoa
      this.age=age; // a sua idade
    }
     // obter a idade
    public int getAge(){
      return age;
    }
     // identidade da pessoa
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // classe «pessoa»

   // fabricante
  public sort2() {
     // uma tabela de pessoas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // ordenação da tabela de pessoas
    Arrays.sort(amis,this);
     // verificação
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//criador

   // a função que compara pessoas
  public int compare(Object o1, Object o2){
    // deve devolver
     // -1 se o1 for «menor que» o2
     // 0 se o1 for «igual a» o2
     // +1 se o1 for «maior que» 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;
  }//compara

    // função de teste
  public static void main(String[] arg){
    new sort2();
  }//mão

}//classe

Vamos analisar este programa. A função main cria um objeto *sort2*. O construtor da classe *sort2* é o seguinte:

   // construtor
  public sort2() {
     // uma tabela de pessoas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // ordenação da tabela de pessoas
    Arrays.sort(amis,this);
     // verificação
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//fabricante

A tabela a ordenar é uma tabela de objetos personne. A classe personne está definida de forma privada (private) no interior da classe sort2. O método estático sort da classe Arrays não sabe como ordenar um array de objetos personne, pelo que, neste caso, é necessário utilizar a forma void sort(Object[] obj, Comparator C). Comparator é uma interface que define apenas um método:

    int compare(Object o1, Object o2)

e que deve devolver 0: se o1=o2, -1: se o1 < o2, +1: se o1 > o2. No protótipo void sort(Object[] obj, Comparator C), o segundo argumento C deve ser um objeto que implemente a interface Comparator. No construtor sort2, escolheu-se o objeto atual this:

     // ordenação da tabela de pessoas
    Arrays.sort(amis,this);

Isto obriga-nos a fazer duas coisas:

  1. indicar que a classe sort2 implementa a interface Comparator
public class sort2 implements Comparator{
  1. escrever a função compare na classe sort2.

Esta é a seguinte:

   // a função que compara pessoas
  public int compare(Object o1, Object o2){
    // deve devolver
     // -1 se o1 for «menor que» o2
     // 0 se o1 for «igual a» o2
     // +1 se o1 for «maior que» 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;
  }//compara

Para comparar dois objetos personne, utiliza-se aqui a idade (também se poderia ter utilizado o nome).

Os resultados da execução são os seguintes:

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

Poderíamos ter procedido de forma diferente para implementar a interface Comparator:

import java.util.*;

public class sort2 {

  // uma classe privada interna
  private class personne{
…….
  }; // classe pessoa

   // construtor
  public sort2() {
     // um array de pessoas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // ordenação da matriz de pessoas
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//compara
      }//classe
    );
     // verificação
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//criador

   // a função que compara pessoas
  public int compare1(Object o1, Object o2){
    // deve devolver
     // -1 se o1 for «menor que» o2
     // 0 se o1 for «igual a» o2
     // +1 se o1 for «maior que» 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;
  }//compara1

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

A instrução de ordenação ficou assim:

     // ordenação da tabela de pessoas
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//comparar
      }//turma
    );

O segundo parâmetro do método sort deve ser um objeto que implemente a interface Comparator. Aqui, criamos um objeto desse tipo através do método new java.util.Comparator() e o texto que se segue {….} define a classe da qual se cria um objeto. Chama-se a isto uma classe anónima, uma vez que não tem nome. Nesta classe anónima, que deve implementar a interface Comparator, define-se o método compare dessa interface. Este método limita-se a chamar o método compare1 da classe sort2. Assim, voltamos ao caso anterior.

A classe sort2 já não implementa a interface Comparator. Assim, a sua declaração passa a ser:

public class sort2 {

Agora, testamos o método binarySearch da classe Arrays no exemplo seguinte:

import java.util.*;

public class sort4 {

  // uma classe privada interna
  private class personne{
      // atributos
    private String nom;
    private int age;

        // construtor
    public personne(String nom, int age){
      this.nom=nom; // nome da pessoa
      this.age=age; // a sua idade
    }

     // obter o nome
    public String getNom(){
      return nom;
    }

     // obter a idade
    public int getAge(){
      return age;
    }
     // identidade da pessoa
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // categoria da pessoa

   // fabricante
  public sort4() {
     // uma tabela de pessoas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};

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

     // ordenação da tabela de pessoas
    Arrays.sort(amis,comparateur1);
    // verificação
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
    // pesquisas
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);
  }//criador

   // a função que compara pessoas
  public int compare1(Object o1, Object o2){
    // deve devolver
     // -1 se o1 for «menor que» o2
     // 0 se o1 for «igual a» o2
     // +1 se o1 for «maior que» 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;
  }//compara1

   // a função que compara uma pessoa com um nome
  public int compare2(Object o1, Object o2){
    // o1 é uma pessoa
     // o2 é uma String, o nome nom2 de uma pessoa
     // deve devolver
     // -1 se o1.nom for «menor que» nom2
     // 0 se o1.nom for «igual a» nom2
     // +1 se o1.nom for «maior que» nome2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//compara2

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // procura amigo na tabela de amigos
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // encontrado?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//procurar

  // mão
  public static void main(String[] arg){
    new sort4();
  }//mão
}//turma

Aqui, procedemos de forma um pouco diferente dos exemplos anteriores. Os dois objetos Comparator necessários para os métodos sort e binarySearch foram criados e atribuídos às variáveis comparateur1 e comparateur2.

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

É efetuada duas vezes uma pesquisa dicotómica na tabela amis no construtor de sort4:

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

O método cherche recebe todos os parâmetros de que necessita para chamar o método binarySearch:

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // procurar amigo na lista de amigos
    int position=Arrays.binarySearch(amis,ami,comparateur);
     // encontrado?
    if(position>=0)
        System.out.println(ami + " a " + amis[position].getAge() + " ans");
    else System.out.println(ami + " n'existe pas dans le tableau");
  }//procura

O método binarySearch funciona em conjunto com o comparador comparateur2, que, por sua vez, recorre ao método compare2 da classe sort4. O método rend devolve a posição do nome procurado na tabela, caso este exista, ou um número <0, caso contrário. O método compare2 serve para comparar um objeto personne com um nome do tipo String.

   // a função que compara uma pessoa com um nome
  public int compare2(Object o1, Object o2){
    // o1 é uma pessoa
     // o2 é uma String, o nome «nome2» de uma pessoa
     // deve devolver
     // -1 se o1.nom for «menor que» nome2
     // 0 se o1.nom for «igual a» nome2
     // +1 se o1.nom for «maior que» nome2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//compara2

Ao contrário do método sort, o método binarySearch não recebe dois objetos personne, mas sim um objeto personne e um objeto String, por esta ordem. O primeiro parâmetro é um elemento da matriz amis, o segundo é o nome da pessoa procurada.

4.7. A classe Enumeration

Enumeration é uma interface e não uma classe. Possui os seguintes métodos:

public abstract boolean hasMoreElements()
retorna «true» se a enumeração ainda tiver elementos
public abstract Object nextElement()
retorna a referência ao elemento seguinte da enumeração

Como se utiliza uma enumeração? Geralmente, da seguinte forma:

    Enumeration e=                // recupera-se um objeto de enumeração
    while(e.hasMoreElements()){
        // utilizar o elemento e.nextElement()
    }

Eis um exemplo:


// as classes importadas
import java.util.*;

public class test1{

// o programa principal main - estático - método de classe

  public static void main(String arg[]){

// a criação de objetos (instâncias de classes)
    personne p=new personne("Jean","Dupont",30);
    enseignant en=new enseignant("Paula","Hanson",56,27);
    etudiant et=new etudiant("Chris","Garot",22,"19980405");
    System.out.println("p="+p.toString());
    System.out.println("en="+en.toString());
    System.out.println("et="+et.toString());

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

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

// uma enumeração
    Enumeration E=V.elements();
    i=0;
    while(E.hasMoreElements()){
      p2=(personne) E.nextElement();
      System.out.println("V["+i+"]="+p2.toString());
      i++;
    }
   }// fim de main
}//fim da classe

Obtêm-se os seguintes resultados:

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. A classe Hashtable

A classe Hashtable permite implementar um dicionário. Pode-se considerar um dicionário como uma tabela com duas colunas:

chave
valor
chave1
valor1
chave2
valor2
..
...

As chaves são únicas, c.a.d. Não pode haver duas chaves idênticas. Os principais métodos e propriedades da classe Hashtable são os seguintes:

public Hashtable()
construtor - cria um dicionário vazio
public int size()
número de elementos no dicionário — sendo que um elemento é um par (chave, valor)
public Object put(Object key, Object value)
adiciona o par (chave, valor) ao dicionário
public Object get(Object key)
recupera o objeto associado à chave key ou null, caso a chave key não exista
public boolean containsKey(Object key)
verdadeiro se a chave key existir no dicionário
public boolean contains(Object value)
verdadeiro se o valor value existir no dicionário
public Enumeration keys()
retorna as chaves do dicionário sob a forma de uma enumeração
public Object remove(Object key)
remove o par (chave, valor) em que chave = key
public String toString()
identifica o dicionário

Eis um exemplo:


// as classes importadas
import java.util.*;

public class test1{

// o programa principal main - estático - método de classe

  public static void main(String arg[]){

// a criação de objetos instâncias de classes
    personne p=new personne("Jean","Dupont",30);
    enseignant en=new enseignant("Paula","Hanson",56,27);
    etudiant et=new etudiant("Chris","Garot",22,"19980405");
    System.out.println("p="+p.toString());
    System.out.println("en="+en.toString());
    System.out.println("et="+et.toString());

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

// um dicionário
    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++;
    }
  }//fim de main
}//fim da classe

Os resultados obtidos são os seguintes:

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. Os ficheiros de texto

4.9.1. Escrever

Para escrever num ficheiro, é necessário dispor de um fluxo de escrita. Para tal, pode utilizar-se a classe FileWriter. Os construtores mais frequentemente utilizados são os seguintes:

FileWriter(String fileName)
cria o ficheiro com o nome fileName — é possível, em seguida, escrever nesse ficheiro — um eventual ficheiro com o mesmo nome é substituído
FileWriter(String fileName,
boolean append)
idem — um eventual ficheiro com o mesmo nome pode ser utilizado abrindo-o no modo de adição (append=true)

A classe FileWriter oferece vários métodos para escrever num ficheiro, métodos herdados da classe Writer. Para escrever num ficheiro de texto, é preferível utilizar a classe PrintWriter, cujos construtores mais utilizados são os seguintes:

PrintWriter(Writer out)
o argumento é do tipo Writer, c.a.d. um fluxo de escrita (num ficheiro, na rede, …)
PrintWriter(Writer out, boolean autoflush)
O mesmo. O segundo argumento controla o armazenamento em buffer das linhas. Quando está definido como falso (valor por defeito), as linhas escritas no ficheiro passam por um buffer na memória. Quando este fica cheio, o conteúdo é gravado no ficheiro. Isto melhora o acesso ao disco. No entanto, por vezes, este comportamento é indesejável, nomeadamente quando se escreve na rede.

Os métodos úteis da classe PrintWriter são os seguintes:

void print(Type T)
escreve o valor T (String, int, …)
void println(Type T)
faz o mesmo, terminando com um caractere de fim de linha
void flush()
esvazia o buffer se não estivermos no modo autoflush
void close()
fecha o fluxo de escrita

Eis um programa que escreve algumas linhas num ficheiro de texto:

// importações
import java.io.*;

public class ecrire{
  public static void main(String[] arg){
    // abertura do ficheiro
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // gravação no ficheiro
    try{
      fic.println("Jean,Dupont,27");
      fic.println("Pauline,Garcia,24");
      fic.println("Gilles,Dumond,56");
    } catch (Exception e){
      Erreur(e,3);
    }
     // fecho do ficheiro
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// fim da função

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

O ficheiro out obtido após a execução é o seguinte:

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

4.9.2. Ler

Para ler o conteúdo de um ficheiro, é necessário dispor de um fluxo de leitura associado ao ficheiro. Para tal, pode utilizar-se a classe FileReader e o seguinte construtor:

FileReader(String nomeDoFicheiro)
abre um fluxo de leitura a partir do ficheiro indicado. Lança uma exceção se a operação falhar.

A classe FileReader possui vários métodos para ler um ficheiro, métodos herdados da classe Reader. Para ler linhas de texto num ficheiro de texto, é preferível utilizar a classe BufferedReader com o seguinte construtor:

BufferedReader(Reader in)
abre um fluxo de leitura com buffer a partir de um fluxo de entrada in. Este fluxo do tipo Reader pode provir do teclado, de um ficheiro, da rede, etc.

Os métodos úteis da classe BufferedReader são os seguintes:

int read()
lê um carácter
String readLine()
lê uma linha de texto
int read(char[] buffer, int offset, int tamanho)
taille caracteres do ficheiro e coloca-os na matriz buffer a partir da posição offset.
void close()
fecha o fluxo de leitura

Eis um programa que lê o conteúdo do ficheiro criado anteriormente:

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

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

    // gestão de eventuais erros
    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));
      }// fim do while
    } catch (Exception e){
      Erreur(e,2);
    }

     // fecho do ficheiro
    try{
      IN.close();
    } catch (Exception e){
      Erreur(e,3);
    }
  }// fim de main

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

}// fim da classe

A execução do programa produz os seguintes resultados:

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

4.9.3. Gravação de um objeto «pessoa»

Aplicamos o que acabámos de ver para dotar a classe personne de um método que permita guardar num ficheiro os atributos de uma pessoa. Adicionamos o método sauveAttributs à definição da classe personne:


    // ------------------------------
  // guardar em ficheiro de texto
    // ------------------------------
  public void sauveAttributs(PrintWriter P){
    P.println(""+this);
  }

Antes de definir a classe personne, não se esqueça de importar o pacote java.io:

import java.io.*;

O método sauveAttributs recebe como único parâmetro o fluxo PrintWriter, no qual deve escrever. Um programa de teste poderia ser o seguinte:

// importações
import java.io.*;
// importar pessoa;

public class sauver{
  public static void main(String[] arg){
    // abertura do ficheiro
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // gravação no ficheiro
    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);
    }
     // fecho do ficheiro
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// fim da função main

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

Vamos compilar e executar este programa:

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. Os ficheiros binários

4.10.1. A classe RandomAccessFile

A classe RandomAccessFile permite gerir ficheiros binários, nomeadamente aqueles com estrutura fixa, como os que se conhecem na linguagem C/C++. Aqui estão alguns métodos e construtores úteis:

RandomAccessFile(String nomeDoFicheiro, String modo)
construtor — abre o ficheiro indicado no modo especificado. Este modo pode assumir os seguintes valores:
r: abertura para leitura
rw: abertura para leitura e escrita
void writeTTT(TTT valor)
grava o valor no ficheiro. TTT representa o tipo de valor. A representação em memória do valor é gravada tal como está no ficheiro. Assim, encontramos writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat,... Para gravar uma cadeia de caracteres, utiliza-se writeBytes(String cadeia).
TTT readTTT()
lê e devolve um valor do tipo TTT. Assim, temos readBoolean, readByte, readInt, readDouble, readLong, readFloat,... O método read() lê um byte.
long length()
tamanho do ficheiro em bytes
long getFilePointer()
posição atual do ponteiro do ficheiro
void seek(long pos)
posiciona o cursor do ficheiro no byte pos

4.10.2. A classe «artigo»

Todos os exemplos que se seguem utilizarão a seguinte classe article:

     // a estrutura do artigo
    private static class article{
         // define-se a estrutura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//classe do artigo

A classe Java article acima será equivalente à seguinte estrutura article em C

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

Assim, limitaremos o código a 4 caracteres e o nome a 20.

4.10.3. Escrever um registo

O programa seguinte grava um registo num ficheiro chamado «data»:

// classes importadas
import java.io.*;

public class test1{

// testa a gravação de uma estrutura (no sentido do C) num ficheiro binário

     // a estrutura do artigo
    private static class article{
         // define-se a estrutura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//classe «artigo»

    public static void main(String arg[]){

         // define-se o ficheiro binário no qual os artigos serão armazenados
        RandomAccessFile fic=null;

         // define-se um artigo
        article art=new article();
        art.code="a100";
        art.nom="velo";
        art.prix=1000.80;
        art.stockActuel=100;
        art.stockMinimum=10;

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

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

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

   // método de escrita
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // código
      fic.writeBytes(art.code);
       // o nome está limitado a 20 caracteres
      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);
       // o preço
      fic.writeDouble(art.prix);
       // os stocks
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// fim da escrita

   // ------------------------erro
  public static void erreur(String msg, int exitCode){
          System.err.println(msg);
          System.exit(exitCode);
  }// fim do erro
}// fim da classe

É o seguinte programa que nos permite verificar se a execução decorreu corretamente.

4.10.4. Ler um registo

// classes importadas
import java.io.*;

public class test2{

// testa a gravação de uma estrutura (no sentido do C) num ficheiro binário

     // a estrutura «artigo»
    private static class article{
         // define-se a estrutura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//classe «artigo»

    public static void main(String arg[]){

         // define-se o ficheiro binário no qual os artigos serão armazenados
        RandomAccessFile fic=null;

       // abre-se o ficheiro para leitura
      try{
          fic=new RandomAccessFile("data","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data",1);
      }//try-catch

       // lê-se o único artigo do ficheiro
    article art=new article();
      try{
          lire(fic,art);
      } catch (IOException E){
          erreur("Erreur lors de la lecture de l'enregistrement",2);
      }//try-catch

       // exibimos o registo lido
      affiche(art);

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

    // método de leitura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // leitura do código
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nome
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // preço
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// fim da gravação

     // ---------------------exibir
    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);
    }// fim do anúncio

     // ------------------------erro
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fim do erro
}// fim da classe

Os resultados da execução são os seguintes:

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

Consegue-se recuperar corretamente o registo que tinha sido gravado pelo programa de gravação.

4.10.5. Conversão de texto para binário

O programa seguinte é uma extensão do programa de gravação de um registo. Agora, gravamos vários registos num ficheiro binário denominado data.bin. Os dados são retirados do seguinte ficheiro 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
// classes importadas
import java.io.*;
import java.util.*;

public class test3{

// ficheiro de texto --> ficheiro binário

     // a estrutura do artigo
    private static class article{
         // define-se a estrutura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//classe «artigo»

    public static void main(String arg[]){

         // define-se o ficheiro binário no qual os artigos serão armazenados
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin",1);
        }

         // os dados são extraídos de um ficheiro de texto
    BufferedReader dataTxt=null;
        try{
            dataTxt=new BufferedReader(new FileReader("data.txt"));
        } catch (IOException E){
            erreur("Impossible d'ouvrir le fichier data.txt",2);
        }

       // ficheiro .txt --> ficheiro .bin
      String ligne=null;
      String[] champs=null;
      int numLigne=0;
      String champ=null;
    article art=new article();    // artigo a criar
      try{
          while((ligne=dataTxt.readLine())!=null){
          // uma linha de +
              numLigne++;
         // decomposição em campos
        champs=ligne.split(":");
         // são necessários 5 campos
              if(champs.length!=5)
                  erreur("Ligne "+numLigne+" erronée dans data.txt",3);
         //código
              art.code=champs[0];
              if(art.code.length()!=4) 
            erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
         // nome, apelido
              art.nom=champs[1];
         // preço
              try{
                  art.prix=Double.parseDouble(champs[2]);
              } catch (Exception E){
                  erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
              }
        // stock atual
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }
        // stock atual
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }             
               // grava-se o registo
              try{
                  ecrire(dataBin,art);
              } catch (IOException E){
                  erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
              }
               // passa-se para a linha seguinte
          }// fim do while
      } catch (IOException E){
          erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
      }        
        // terminado
      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);
      }
  }// fim de main

   // método de gravação
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // código
      fic.writeBytes(art.code);
       // o nome está limitado a 20 caracteres
      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);
       // o preço
      fic.writeDouble(art.prix);
       // os stocks
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// fim da escrita

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

}// fim da classe

É o seguinte programa que permite verificar se este funcionou corretamente.

4.10.6. Conversão de binário para texto

O programa seguinte lê o conteúdo do ficheiro binário data.bin criado anteriormente e transfere esse conteúdo para o ficheiro de texto data.text. Se tudo correr bem, o ficheiro data.text deverá ser idêntico ao ficheiro original data.txt.

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

public class test5{

// ficheiro de texto --> ficheiro binário

     // a estrutura do artigo
    private static class article{
         // define-se a estrutura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//classe artigo

     // função principal
    public static void main(String arg[]){

         // define-se o ficheiro binário no qual os artigos serão armazenados
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","r");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
        }

         // os dados são gravados num ficheiro de texto
    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);
        }

      // ficheiro .bin --> ficheiro .text
    article art=new article();    // artigo a criar

       // o ficheiro binário é processado
      int numRecord=0;
      long l=0;    // tamanho do ficheiro
      try{
          l=dataBin.length();
      } catch (IOException e){
          erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
      }
      long pos=0;    // posição atual no ficheiro
      try{
          pos=dataBin.getFilePointer();
      } catch (IOException e){
          erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
      }

     // enquanto não se tiver ultrapassado o fim do ficheiro
        while(pos<l){
            // ler o registo atual e processá-lo
            numRecord++;
            try{
                lire(dataBin,art);
            } catch (Exception e){
                erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
            }
            affiche(art);

             // escrever a linha de texto correspondente em dataTxt
         dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);

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

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

  }// fim de main

     // método de leitura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
      // leitura do código
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nome
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // preço
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// fim da gravação

     // ---------------------exibir
    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);
    }// fim do anúncio

     // ------------------------erro
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fim do erro
}// fim da classe

Eis um exemplo de execução:

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. Acesso direto aos registos

Este último programa ilustra a possibilidade de aceder diretamente aos registos de um ficheiro binário. Apresenta o registo do ficheiro data.bin, cujo número é passado como parâmetro, sendo que o primeiro registo tem o número 1.

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

public class test6{

// ficheiro de texto --> ficheiro binário

     // a estrutura do artigo
    private static class article{
         // define-se a estrutura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//classe artigo

     // função principal
    public static void main(String[] args){

       // verifica-se os argumentos
      int nbArguments=args.length;
      String syntaxe="syntaxe : pg numéro_de_fiche";
      if(nbArguments!=1)
          erreur(syntaxe,20);
     // verificação do n.º do registo
      int numRecord=0;
      try{
          numRecord=Integer.parseInt(args[0]);
      } catch(Exception e){
          erreur(syntaxe+"\nNuméro de fiche incorrect",21);
      }

       // abre-se o ficheiro binário em modo de leitura
    RandomAccessFile dataBin=null;
      try{
          dataBin=new RandomAccessFile("data.bin","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
      }

       // posiciona-se no registo pretendido
      try{
          dataBin.seek((numRecord-1)*40);
      } catch (Exception e){
          erreur("La fiche "+numRecord+" n'existe pas",23);
      }

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

       // exibe-se
      affiche(art);

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

  }// fim de main

    // método de leitura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // leitura de código
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nome
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // preço
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// fim da gravação

     // ---------------------exibir
    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);
    }// fim da exibição

     // ------------------------erro
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fim do erro
}// fim da classe

Eis alguns exemplos de execução:

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. Utilizar expressões regulares

4.11.1. O pacote java.util.regex

O pacote java.util.regex permite a utilização de expressões regulares. Estas permitem verificar o formato de uma cadeia de caracteres. Assim, é possível verificar se uma cadeia que representa uma data está, de facto, no formato dd/mm/aa. Para tal, utiliza-se um modelo e compara-se a cadeia com esse modelo. Assim, neste exemplo, j, m e a devem ser algarismos. O modelo de um formato de data válido é, então, «\d\d/\d\d/\d\d», em que o símbolo \d representa um algarismo. Os símbolos que podem ser utilizados num modelo são os seguintes (documentação da Microsoft):

Caractere
Descrição
\
Marca o caractere seguinte como caractere especial ou literal. Por exemplo, «n» corresponde ao caractere «n». «\n» corresponde a um caractere de nova linha. A sequência «\\» corresponde a «\», enquanto que «\»(» corresponde a «(».
^
Corresponde ao início da entrada.
$
Corresponde ao fim da entrada.
*
Corresponde ao carácter anterior zero ou mais vezes. Assim, «zo*» corresponde a «z» ou a «zoo».
+
Corresponde ao carácter anterior uma ou mais vezes. Assim, «zo+» corresponde a «zoo», mas não a «z».
?
Corresponde ao carácter anterior zero ou uma vez. Por exemplo, «a?ve?» corresponde a «ve» em «lever».
.
Corresponde a qualquer carácter único, exceto ao carácter de nova linha.
(padrão)
Procura o modèle e memoriza a correspondência. A subcadeia correspondente pode ser extraída da coleção Matches obtida, utilizando o Item [0]...[n]. Para encontrar correspondências com caracteres entre parênteses ( ), utilize "\(" ou "\)".
x|y
Corresponde tanto a x como a y. Por exemplo, «z|foot» corresponde a «z» ou a «foot». «(z|f)oo» corresponde a «zoo» ou a «foo».
{n}
n é um número inteiro não negativo. Corresponde exatamente a n vezes o carácter. Por exemplo, «o{2}» não corresponde a «o» em «Bob,», mas aos dois primeiros «o» em «fooooot».
{n,}
n é um número inteiro não negativo. Corresponde a, pelo menos, n vezes o carácter. Por exemplo, «o{2,}» não corresponde ao «o» em «Bob», mas a todos os «o» em «fooooot». «o{1,}» equivale a «o+» e «o{0,}» equivale a «o*».
{n,m}
m e n são números inteiros não negativos. Corresponde a, no mínimo, n e, no máximo, m vezes o carácter. Por exemplo, «o{1,3}» corresponde aos três primeiros «o» em «foooooot» e «o{0,1}» equivale a «o?».
[xyz]
Conjunto de caracteres. Corresponde a um dos caracteres indicados. Por exemplo, «[abc]» corresponde a «a» em «plat».
[^xyz]
Conjunto de caracteres negativo. Corresponde a qualquer caractere não indicado. Por exemplo, «[^abc]» corresponde a «p» em «plat».
[a-z]
Intervalo de caracteres. Corresponde a qualquer caractere da série especificada. Por exemplo, «[a-z]» corresponde a qualquer letra minúscula entre «a» e «z».
[^m-z]
Intervalo de caracteres negativo. Corresponde a qualquer caractere que não se encontre na série especificada. Por exemplo, «[^m-z]» corresponde a qualquer caractere que não se encontre entre «m» e «z».
\b
Corresponde a um delimitador que representa uma palavra, ou seja, à posição entre uma palavra e um espaço. Por exemplo, «er\b» corresponde a «er» em «lever», mas não a «er» em «verbe».
\B
Corresponde a um limite que não representa uma palavra. «en*t\B» corresponde a «ent» em «bien entendu».
\d
Corresponde a um carácter que representa um algarismo. É equivalente a [0-9].
\D
Corresponde a um carácter que não representa um algarismo. É equivalente a [^0-9].
\f
Corresponde a um carácter de salto de página.
\n
Corresponde a um carácter de nova linha.
\r
Corresponde a um carácter de retorno de carro.
\s
Corresponde a qualquer espaço em branco, incluindo espaço, tabulação, salto de página, etc. É equivalente a «[ \f\n\r\t\v]».
\S
Corresponde a qualquer caractere de espaço não em branco. Equivale a «[^ \f\n\r\t\v]».
\t
Corresponde a um carácter de tabulação.
\v
Corresponde a um carácter de tabulação vertical.
\w
Corresponde a qualquer carácter que represente uma palavra e inclua um sublinhado. É equivalente a «[A-Za-z0-9_]».
\W
Corresponde a qualquer carácter que não represente uma palavra. É equivalente a «[^A-Za-z0-9_]».
\num
Corresponde a num, em que num é um número inteiro positivo. Refere-se às correspondências guardadas. Por exemplo, «(.)\1» corresponde a dois caracteres idênticos consecutivos.
\n
Corresponde a n, em que n é um valor de escape octal. Os valores de escape octais devem ter 1, 2 ou 3 dígitos. Por exemplo, «\11» e «\011» correspondem ambos a um carácter de tabulação. "\0011" equivale a "\001" e "1". Os valores de escape octais não devem exceder 256. Se tal acontecer, apenas os dois primeiros dígitos serão considerados na expressão. Permite utilizar os códigos ASCII em expressões regulares.
\xn
Corresponde a n, em que n é um valor de escape hexadecimal. Os valores de escape hexadecimais têm de incluir obrigatoriamente dois dígitos. Por exemplo, «\x41» corresponde a «A». «\x041» equivale a «\x04» e «1». Permite utilizar os códigos ASCII em expressões regulares.

Um elemento num modelo pode estar presente uma ou mais vezes. Vejamos alguns exemplos relacionados com o símbolo \d, que representa um algarismo:

modelo
significado
\d
um algarismo
\d?
0 ou 1 dígito
\d*
0 ou mais dígitos
\d+
1 ou mais dígitos
\d{2}
2 algarismos
\d{3,}
pelo menos 3 algarismos
\d{5,7}
entre 5 e 7 dígitos

Imaginemos agora o modelo capaz de descrever o formato esperado para uma cadeia de caracteres:

cadeia procurada
modelo
uma data no formato dd/mm/aa
\d{2}/\d{2}/\d{2}
uma hora no formato hh:mm:ss
\d{2}:\d{2}:\d{2}
um número inteiro sem sinal
\d+
uma sequência de espaços, que pode estar vazia
\s*
um número inteiro sem sinal que pode ser precedido ou seguido de espaços
\s*\d+\s*
um número inteiro que pode ser com sinal e precedido ou seguido de espaços
\s*[+|-]?\s*\d+\s*
um número real sem sinal que pode ser precedido ou seguido de espaços
\s*\d+(.\d*)?\s*
um número real que pode ser com sinal e precedido ou seguido de espaços
\s*[+|]?\s*\d+(.\d*)?\s*
uma cadeia de caracteres que contenha a palavra «justo»
\bjusto\b
  

É possível especificar onde se procura o padrão na cadeia:

padrão
significado
^padrão
o padrão inicia a cadeia
padrão$
o modelo termina a cadeia
^padrão$
o modelo inicia e termina a cadeia
padrão
o padrão é procurado em toda a cadeia, começando pelo início da mesma.
cadeia procurada
padrão
uma cadeia que termina com um ponto de exclamação
!$
uma cadeia que termina com um ponto
\.$
uma cadeia que começa com a sequência //
^//
uma cadeia que contém apenas uma palavra, eventualmente seguida ou precedida de espaços
^\s*\w+\s*$
uma cadeia que contenha duas palavras, eventualmente seguidas ou precedidas de espaços
^\s*\w+\s*\w+\s*$
uma cadeia que contenha a palavra «secret»
\bsecret\b

Os subconjuntos de um padrão podem ser «recuperados». Assim, não só é possível verificar se uma cadeia corresponde a um padrão específico, como também é possível recuperar nessa cadeia os elementos correspondentes aos subconjuntos do padrão que foram colocados entre parênteses. Assim, se analisarmos uma cadeia que contenha uma data dd/mm/aa e, além disso, quisermos recuperar os elementos dd, mm, aa dessa data, utilizaremos o modelo (\d\d)/(\d\d)/(\d\d).

4.11.2. Verificar se uma cadeia corresponde a um modelo determinado

A classe Pattern permite verificar se uma cadeia corresponde a um modelo determinado. Para tal, utiliza-se o método estático

boolean Matches(String modèle, String chaine)

com: modèle: o modelo a verificar, chaine: a cadeia a comparar com o modelo. O resultado é o valor booleano true se a cadeia corresponder ao modelo, false caso contrário.

Eis um exemplo:

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

// gestão de expressões regulares
public class regex1 {
  public static void main(String[] args){
    // uma expressão regular modelo
    String modèle1="^\\s*\\d+\\s*$";
    // comparar um exemplo com o modelo
    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);
  }//exibe
}//classificar

e os resultados da execução:

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

Note-se que, no padrão "^\s*\d+\s*$", o carácter \ deve ser duplicado devido à interpretação específica que o Java faz deste carácter. Escreve-se, portanto: String padrão1="^\\s*\\d+\\s*$";

4.11.3. Encontrar todos os elementos de uma cadeia que correspondam a um padrão

Consideremos o padrão "\d+" e a cadeia " 123 456 789 ". Encontramos o padrão em três locais diferentes da cadeia. As classes Pattern e Matcher permitem recuperar as diferentes ocorrências de um padrão numa cadeia. A classe Pattern é a classe que gere as expressões regulares. Uma expressão regular utilizada mais do que uma vez tem de ser «compilada». Isto acelera as pesquisas do padrão nas cadeias de caracteres. O método estático compile realiza essa tarefa:

public static Pattern compile(String regex)

Recebe como parâmetro a cadeia do padrão e devolve um objeto Pattern. Para comparar o padrão de um objeto Pattern com uma cadeia de caracteres, utiliza-se a classe Matcher. Esta permite a comparação de um padrão com uma cadeia de caracteres. A partir de um objeto Pattern, é possível obter um objeto do tipo Matcher com o método matcher:

public Matcher matcher(CharSequence input)

input é a cadeia de caracteres que deve ser comparada com o padrão.

Assim, para comparar o padrão «\d+» com a cadeia « 123 456 789 », é possível criar um objeto Matcher da seguinte forma:

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

A partir do objeto résultats anterior, será possível recuperar as diferentes ocorrências do padrão na cadeia. Para tal, utilizam-se os métodos suivantes da classe Matcher:

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

O método find procura na cadeia analisada a primeira ocorrência do padrão. Uma segunda chamada ao método find procurará a ocorrência seguinte. E assim sucessivamente. O método devolve true se encontrar o padrão; caso contrário, devolve false. A parte da cadeia correspondente à última ocorrência encontrada por find é obtida com o método group e a sua posição com o método start. Assim, se continuarmos com o exemplo anterior e quisermos apresentar todas as ocorrências do padrão «\d+» na cadeia « 123 456 789 », escreveremos:

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

O método reset permite reiniciar o objeto Matcher no início da cadeia comparada com o padrão. Assim, o método find voltará a encontrar a primeira ocorrência do padrão.

Eis um exemplo completo:

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

// gestão de expressões regulares
public class regex2 {
  public static void main(String[] args){
    // várias ocorrências do padrão na instância
    String modèle2="\\d+";
    Pattern regex2=Pattern.compile(modèle2);
    String exemplaire3="  123  456 789";
    // pesquisa de ocorrências do modelo no exemplar
    Matcher matcher2=regex2.matcher(exemplaire3);
    while(matcher2.find()){
      affiche("séquence " + matcher2.group() + " trouvée en position " + matcher2.start());
    }//enquanto
  }//Main

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

Resultados da execução:

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. Recuperar partes de um modelo

É possível «extrair» subconjuntos de um modelo. Assim, não só se pode verificar se uma cadeia de caracteres corresponde a um modelo específico, como também se pode extrair dessa cadeia os elementos correspondentes aos subconjuntos do modelo que foram colocados entre parênteses. Assim, se analisarmos uma cadeia de caracteres que contenha uma data dd/mm/aa e quisermos, além disso, extrair os elementos dd, mm e aa dessa data, utilizaremos o padrão (\d\d)/(\d\d)/(\d\d).

Vejamos o seguinte exemplo:

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

// gestão de expressões regulares
public class regex3 {
  public static void main(String[] args){
    // captura de elementos no modelo
    String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
    Pattern regex3=Pattern.compile(modèle3);
    String exemplaire4="Il est 18:05:49";
    // verificação do modelo
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // a instância corresponde ao modelo
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // são apresentados os grupos
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//for
      }else{
        // o exemplar não corresponde ao modelo
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }
    }//Principal

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

A execução deste programa produz os seguintes resultados:

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

A novidade encontra-se na seguinte parte do código:

    // verificação do modelo
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // o exemplar corresponde ao modelo
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // são apresentados os grupos
      for (int i=0;i<=résultat.groupCount();i++){
        affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
      }//para
      }else{
        // o exemplar não corresponde ao modelo
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }

A cadeia exemplaire4 é comparada com o modelo regex3 através do método find. É então encontrada uma ocorrência do modelo regex3 na cadeia exemplaire4. Se o modelo incluir subconjuntos entre parênteses, estes estão disponíveis através de vários métodos da classe Matcher:


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

O método groupCount fornece o número de subconjuntos encontrados no modelo e o group(i) fornece o subconjunto n.º i. Este é encontrado na cadeia numa posição determinada pelo start(i). Assim, no exemplo:

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

A primeira chamada ao método find irá encontrar a cadeia 18:05:49 e criar automaticamente os três subconjuntos definidos pelos parênteses do modelo, respetivamente 18, 05 e 49.

4.11.5. Um programa de treino

Encontrar a expressão regular que nos permite verificar se uma cadeia corresponde a um determinado modelo é, por vezes, um verdadeiro desafio. O programa seguinte permite praticar. Solicita um modelo e uma cadeia e indica, em seguida, se a cadeia corresponde ou não ao modelo.

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

// gestão de expressões regulares
public class regex4 {
  public static void main(String[] args){

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

    // gestão de erros
    try{
      // solicita-se ao utilizador os modelos e os exemplares a comparar com este
      while(true){
        // fluxo de entrada
        IN=new BufferedReader(new InputStreamReader(System.in));
        // solicita-se o modelo
        System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
        modèle=IN.readLine();
        // Concluído?
        if(modèle.trim().toLowerCase().equals("fin")) break;
        // cria-se a expressão regular
        regex=Pattern.compile(modèle);
        // solicita-se ao utilizador os exemplos a comparar com o modelo
        while(true){
          System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
          chaine=IN.readLine();
          // Concluído?
          if(chaine.trim().toLowerCase().equals("fin")) break;
          // criamos o objeto matcher
          résultats=regex.matcher(chaine);
          // procura-se as ocorrências do modelo
          nbOccurrences=0;
          while(résultats.find()){
            // encontrou-se uma ocorrência
            nbOccurrences++;
            // exibe-se
            System.out.println("J'ai trouvé la correspondance ["+résultats.group()
            +"] en position "+résultats.start());
            // exibição dos subelementos
            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));
              }//para j
            }//if
            // cadeia seguinte
          }//enquanto (résultats.find())
          // foi encontrada pelo menos uma ocorrência?
          if(nbOccurrences==0){
            System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
          }//if
          // padrão seguinte
        }//while(true)
      }//while(true)
    }catch(Exception ex){
      // erro
      System.err.println("Erreur : "+ex.getMessage());
      // fim com erro
      System.exit(1);
    }//try-catch
    // fim
    System.exit(0);
  }//Main
}//classe

Eis um exemplo de execução:

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. O método split da classe Pattern

Consideremos uma cadeia de caracteres composta por campos separados por uma cadeia separadora definida por uma expressão regular. Por exemplo, se os campos forem separados pelo caractere , precedido ou seguido por um número qualquer de espaços, a expressão regular que modela a cadeia separadora dos campos seria «\s*,\s*». O método split da classe Pattern permite-nos recuperar os campos numa matriz:


public String[] split(CharSequence input)

A cadeia input é decomposta em campos, estando estes separados por um separador que corresponde ao modelo do objeto Pattern atual. Para recuperar os campos de uma linha cujo separador de campos seja a vírgula precedida ou seguida por um número qualquer de espaços, escrever-se-á:

    // uma linha
    String ligne="abc  ,, def  , ghi";
    // um modelo
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // decomposição de linha em campos
    String[] champs=modèle.split(ligne);

É possível obter o mesmo resultado com o método split da classe String:


public String[] split(String regex)

Eis um programa de teste:

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

// gestão de expressões regulares
public class split1 {
  public static void main(String[] args){
    // uma linha
    String ligne="abc  ,, def  , ghi";
    // um modelo
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // decomposição de linha em campos
    String[] champs=modèle.split(ligne);
    // visualização
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
    // outra forma de o fazer
    champs=ligne.split("\\s*,\\s*");
    // visualização
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//para
  }//Principal
}//classe

Resultados da execução:

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

4.12. Exercícios

4.12.1. Exercício 1

No Unix, os programas são frequentemente chamados da seguinte forma:

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

onde -oi representa uma opção e vi um valor associado a essa opção. Pretende-se criar uma classe «options» que permita analisar a cadeia de argumentos -o1 v1 v2 ... -o2 v3 v4 …, de modo a construir as seguintes entidades:

optionsValides
dicionário (Hashtable) cujas chaves são as opções oi válidas. O valor associado à chave oi é um vetor (Vector) cujos elementos são os valores v1 v2 … associados à opção -oi
optionsInvalides
dicionário (Hashtable) cujas chaves são as opções oi inválidas. O valor associado à chave oi é um vetor (Vector) cujos elementos são os valores v1, v2… associados à opção -oi
optionsSans
cadeia de caracteres (String) que apresenta a lista dos valores vi não associados a uma opção
erreur
um número inteiro igual a 0 se não houver erros na linha de argumentos; caso contrário, outro valor:
1: existem parâmetros de chamada inválidos
2: existem opções inválidas
4: existem valores não associados a opções
Se houver vários tipos de erros, estes valores são cumulativos.

Um objeto «options» pode ser criado de 4 formas diferentes:

public options (String arguments, String optionsAcceptables)

arguments
a linha de argumentos -o1 v1 v2 ... -o2 v3 v4 … a analisar
optionsAcceptables
a lista de opções «oi» aceitáveis

Exemplo de chamada: options opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");

Aqui, os dois argumentos são cadeias de caracteres. Serão aceites os casos em que essas cadeias tenham sido divididas em palavras e colocadas numa matriz de cadeias de caracteres. Para tal, são necessários mais três construtores:

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

A classe «options» apresentará a seguinte interface (acessores):

public Hashtable getOptionsValides()

retorna a referência da tabela optionsValides criada aquando da criação do objeto «options»

public Hashtable getOptionsInvalides()

retorna a referência à matriz optionsInvalides criada aquando da criação do objeto «options»

public String getOptionsSans()

retorna a referência da cadeia optionsSans criada durante a criação do objeto «options»

public int getErreur()

retorna o valor do atributo «erro» criado durante a criação do objeto de opções

public String toString()

se não houver erros, apresenta os valores dos atributos optionsValides, optionsInvalides, optionsSans; caso contrário, apresenta o número do erro.

Eis um programa de exemplo:


import java.io.*;
//opções de importação;

public class test1{
  
  
  public static void main (String[] arg){
    
    // abertura do fluxo de entrada
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    // leitura dos argumentos do construtor 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));
    }// fim do while
  }//fim do main


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

}//fim da classe

Alguns resultados:

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. Exercício 2

Pretende-se criar uma classe «stringtovector» que permita transferir o conteúdo de um objeto String para um objeto Vector. Esta classe seria derivada da classe Vector:

class stringtovector extends Vector

e teria o seguinte código-fonte:


    private void stringtovector(String S, String separateur, int[] tChampsVoulus,
            boolean strict){
        
        // cria um vetor com os campos da cadeia S
        // esta é constituída por campos separados por separador
        // se separador=nulo, a cadeia forma apenas um único campo
        // só são desejados os campos cujos índices constam na tabela tChampsVoulus 
        // são os desejados. Os índices começam em 1
        // se tChampsvoulus for nulo ou tiver tamanho nulo, são selecionados todos os campos
        // se strict=verdadeiro, todos os campos desejados devem estar presentes

A classe teria o seguinte atributo privado:

        private int erreur;

Este atributo é definido pelo construtor anterior com os seguintes valores:

0: a construção decorreu sem problemas

4: faltam alguns campos obrigatórios, apesar de strict=true

A classe terá também dois métodos:

    public int getErreur()

que devolve o valor do atributo privado erreur.

    public String identite(){

que exibe o valor do objeto na forma (erro, elemento 1, elemento 2, …) em que os elementos i são os elementos do vetor construído a partir da cadeia de caracteres.

Um programa de teste poderia ser o seguinte:


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

Os resultados:

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

Algumas sugestões:

  1. Para dividir a cadeia S em campos, utilize o método split da classe String.
  2. Colocar os campos de S num dicionário D indexado pelo número do campo
  3. Recuperar do dicionário D apenas os campos cuja chave (índice) conste na tabela tChampsVoulus.

4.12.3. Exercício 3

Pretende-se adicionar à classe stringtovector o seguinte construtor:


public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
        
        // cria um vetor com os campos da cadeia S
        // esta é constituída por campos separados pelo separador
        // se separador=nulo, a cadeia de caracteres forma apenas um único campo
        // só são desejados os campos cujos índices se encontram em sChampsVoulus
        // os índices começam em 1
        // se sChampsvoulus for nulo ou "", são selecionados todos os campos
        // se strict=verdadeiro, todos os campos pretendidos têm de estar presentes

A lista dos campos pretendidos encontra-se, portanto, numa cadeia de caracteres (String) em vez de um tabuleiro de inteiros (int[]). Ao atributo privado erreur da classe pode ser atribuído um novo valor:

2: a cadeia de índices dos campos pretendidos está incorreta

Eis um programa de exemplo:


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

Alguns resultados:

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

Algumas sugestões:

  1. É necessário voltar ao caso do construtor anterior, transferindo os campos da cadeia sChampsVoulus para uma matriz de inteiros. Para tal, dividir sChampsVoulus em campos utilizando um objeto StringTokenizer, cujo atributo countTokens indicará o número de campos obtidos. Pode-se então criar uma matriz de inteiros com a dimensão adequada e preenchê-la com os campos obtidos.
  2. Para saber se um campo é um inteiro, utilize o método Integer.parseInt para converter o campo num inteiro e trate a exceção que será gerada caso essa conversão seja impossível.

4.12.4. Exercício 4

Pretende-se criar uma classe «filetovector» que permita transferir o conteúdo de um ficheiro de texto para um objeto Vector. Esta classe seria derivada da classe Vector:

class filetovector extends Vector

e teria o seguinte construtor:


    // --------------------- construtor 
    public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
        
        // cria um vetor com as linhas do ficheiro de texto nomFichier
        // as linhas são compostas por campos separados por separador
        // se separador=nulo, a linha forma apenas um único campo
        // só são desejados os campos cujos índices constam em tChampsVoulus
        // os índices começam em 1
        // se tChampsvoulus for nulo ou vazio, são selecionados todos os campos
        // se strict=verdadeiro, todos os campos pretendidos devem estar presentes
        // caso contrário, a linha não é guardada e o seu índice
        // é colocado no vetor lignesErronees
        // as linhas em branco são ignoradas
        // assim como as linhas que começam por tagCommentaire, se tagCommentaire != null

A classe teria os seguintes atributos privados:

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

O atributo erreur é definido pelo construtor anterior com os seguintes valores:

0: a construção decorreu com sucesso

1: não foi possível abrir o ficheiro a processar

4: faltam alguns campos solicitados, apesar de strict=true

8: ocorreu um erro de E/S durante a processamento do ficheiro

O atributo lignesErronees é um vetor cujos elementos são os números das linhas com erros, na forma de cadeias de caracteres. Uma linha é considerada com erro se não conseguir fornecer os campos solicitados, quando strict=true.

A classe terá também dois métodos:

    public int getErreur()

que devolve o valor do atributo privado erreur.

    public String identite(){

que apresenta o valor do objeto na forma (erro, elemento 1, elemento 2 …,(l1,l2,…)), em que os elementos i são os elementos do vetor construído a partir do ficheiro e li são os números das linhas com erros.

Eis um exemplo de teste:


import java.io.*;
//importar 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());
  }
}

Resultados da execução:

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

Algumas dicas

  1. O ficheiro de texto é processado linha a linha. A linha é dividida em campos através da classe stringtovector, analisada anteriormente.

  2. Os elementos do vetor criado a partir do ficheiro de texto são, portanto, objetos do tipo stringtovector.

  3. O método identite de filetovector poderá recorrer ao método stringtovector.identite() para apresentar os seus elementos, bem como ao método Vector.toString() para apresentar os números das eventuais linhas com erros.

4.12.5. Exercício 5

Pretende-se adicionar à classe «filetovector» o seguinte construtor:


    public filetovector(String nomFichier, String separateur, String sChampsVoulus,
            boolean strict, String tagCommentaire){
        
        // cria um vetor com as linhas do ficheiro de texto nomFichier
        // as linhas são compostas por campos separados por separador
        // se separador=null, a linha forma apenas um único campo
        // só são desejados os campos cujos índices constam em tChampsVoulus
        // os índices começam em   1
        // se sChampsvoulus for nulo ou vazio, são selecionados todos os campos
        // se strict=verdadeiro, todos os campos pretendidos devem estar presentes
        // caso contrário, a linha não é memorizada e o seu índice
        // é colocado no vetor lignesErronees
        // as linhas em branco são ignoradas
        // assim como as linhas que começam por tagCommentaire, se tagCommentaire != null

A lista de índices dos campos pretendidos encontra-se agora numa cadeia de caracteres (String), em vez de estar numa matriz de números inteiros.

O atributo privado erreur pode ter um valor adicional:

2: a cadeia de caracteres dos índices dos campos pretendidos está incorreta

Eis um exemplo de teste:


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

Os resultados:

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

Algumas dicas

  1. Vamos transformar a cadeia sChampsVoulus numa tabela de números inteiros tChampVoulus para voltarmos ao caso do construtor anterior.