Skip to content

4. Classes de uso comum

Apresentamos neste capítulo algumas classes Java de uso comum. Elas possuem diversos atributos, métodos e construtores. Em cada caso, apresentamos apenas uma pequena parte das classes. Os detalhes sobre elas estão disponíveis na documentação do Java, que apresentamos a seguir.

4.1. A documentação

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

Image

Às vezes, temos um jdk, mas sem documentação. Ela pode ser encontrada no site da Sun http://www.sun.com. Na pasta docs, encontra-se um arquivo index.html, que é o ponto de partida da ajuda do JDK:

Image

Image

O link API & Language acima dá acesso às classes Java. O link Demos/Tutorials é particularmente útil para obter exemplos de programas Java. Vamos seguir o link API & Language:

Image

Vamos acessar o link Java 2 Platform API:

Image

Esta página é o verdadeiro ponto de partida da documentação sobre as classes. É possível criar um atalho para ela para ter acesso rápido. O URL está em <jdk>\docs\api\index.html. Nela, encontram-se links para centenas de classes Java do JDK. Quando se está começando, a principal dificuldade é saber o que essas diferentes classes fazem. Inicialmente, portanto, essa ajuda só tem utilidade se você souber o nome da classe sobre a qual deseja obter informações. Também é possível se orientar pelos nomes das classes, que normalmente indicam a função da classe.

Vamos dar um exemplo e procurar informações sobre a classe Vector, que implementa um array 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

Lá você encontrará

  • a hierarquia na qual a classe está inserida, neste caso java.util.Vector
  • a lista de campos (atributos) da classe
  • a lista de construtores
  • a lista de métodos

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

4.2. As classes de teste

Os exemplos a seguir utilizam, em alguns casos, as classes personne e enseignant. Recordamos aqui suas definições.

public class personne{
    // sobrenome, nome, 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 é derivada 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+")";    
  }
}

Também utilizaremos 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 maneiras:

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

Os dois métodos são equivalentes. Se, posteriormente, escrevermos nom=&quot;peixe&quot;, ***nom*** passará a referenciar um novo objeto. O objeto antigo *String(&quot;cheval&quot;)* será perdido e o espaço de memória que ele 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 string, sendo que o primeiro caractere tem índice 0. Assim, String("cavalo").charAt(3) é igual a 'v'
public int compareTo(cadeia2)
chaine1.compareTo(chaine2) compara chaine1 com chaine2 e retorna 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 string1 em letras minúsculas
public String toUpperCase()
chaine1.toUpperCase() converte a string1 em maiúsculas
public String trim()
chaine1.trim() remove os espaços no início e no final de string1
public String substring(int beginIndex, int endIndex)
String("chapeau").subString(2,4) retorna a string "ape"
public char[] toCharArray()
permite colocar os caracteres da string em uma matriz de caracteres
int length()
número de caracteres da string
int indexOf(String string2)
retorna a primeira posição de chaine2 na string atual ou -1 se chaine2 não estiver presente
int indexOf(String string2, int startIndex)
retorna a primeira posição de chaine2 na string atual ou -1 se chaine2 não estiver presente. A busca começa a partir do caractere nº startIndex.
int lastIndexOf(String string2)
retorna a última posição de chaine2 na string atual ou -1 se chaine2 não estiver presente
boolean startsWith(String string2)
retorna verdadeiro se a string atual começar com chaine2
boolean endsWith(String string2)
retorna verdadeiro se a string atual terminar com chaine2
boolean matches(String regex)
retorna verdadeiro se a string atual corresponder à expressão regular regex.
String[] split(String regex)
A string 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 em um array.
String replace(char oldChar, char newChar)
substitui, na string atual, o caractere oldChar pelo caractere newChar.

Aqui está 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 dessa classe:

public Vector()
cria 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 na 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 verdadeiro se o vetor estiver vazio
public final void removeElementAt(int index)
remove o elemento com índice index
public final void removeAllElements()
esvazia o vetor de todos os seus elementos
public final String toString()
retorna uma string de identificação do vetor

Aqui está 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());

// 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 arquivo 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 mais 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. Ela difere desta essencialmente apenas quando é utilizada simultaneamente por várias threads de execução. Os métodos de sincronização de threads para o acesso a um Vector ou a um ArrayList diferem. Fora desse caso, pode-se utilizar indistintamente um ou outro. Aqui estão alguns campos, construtores ou métodos dessa classe:

ArrayList()
cria 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 à matriz 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 matriz estiver vazia
void remove(int index)
remove o elemento com índice index
void clear()
esvazia a matriz de todos os seus elementos
Object[] toArray()
converte a matriz dinâmica em uma matriz clássica
String toString()
retorna uma string de identificação da matriz

Aqui está 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[]){

// 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 de antes.

4.6. A classe Arrays

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

static void sort(matriz)
ordena tableau utilizando, para isso, a ordem implícita do tipo de dados da matriz, seja número ou cadeias de caracteres.
static void sort (Object[] array, Comparator C)
ordena o array tableau utilizando a função de comparação C para comparar os elementos
static int binarySearch(matriz, 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[] array, Object elemento, Comparator C)
O mesmo, mas utiliza a função de comparação C para comparar dois elementos da matriz.

Aqui está 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; // 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)};
     // classificaçã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 retornar
     // -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 examinar este programa. A função main cria um objeto *sort2*. O construtor da classe *sort2* é o seguinte:

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

A tabela a ser classificada é uma tabela de objetos personne. A classe personne é definida de forma privada (private) dentro da classe sort2. O método estático sort da classe Arrays não sabe como classificar um array de objetos personne; portanto, somos obrigados a usar aqui 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 retornar 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, escolhemos o objeto atual this:

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

Isso nos obriga 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.

Ela é a seguinte:

   // a função que compara pessoas
  public int compare(Object o1, Object o2){
    // deve retornar
     // -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 usado o nome).

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

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

Poderíamos ter procedido de maneira 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)};
     // classificaçã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 retornar
     // -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 classificação ficou da seguinte forma:

     // classificaçã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 esse objeto por meio de new java.util.Comparator(), e o texto que se segue {…} define a classe da qual estamos criando um objeto. Chamamos isso de classe anônima, pois ela não possui nome. Nessa classe anônima, que deve implementar a interface Comparator, definimos o método compare dessa interface. Este método se limita a chamar o método compare1 da classe sort2. Assim, voltamos ao caso anterior.

A classe sort2 não implementa mais a interface Comparator. Portanto, sua declaração passa a ser:

public class sort2 {

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

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; // sua idade
    }

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

     // recuperar a idade
    public int getAge(){
      return age;
    }
     // identidade da pessoa
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // classe 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
      ;          

     // classificaçã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 retornar
     // -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 a um nome
  public int compare2(Object o1, Object o2){
    // o1 é uma pessoa
     // o2 é uma String, o nome nom2 de uma pessoa
     // deve retornar
     // -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");
  }//procura

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

Aqui, procedemos de maneira 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
      ;          

Uma pesquisa dicotômica na tabela amis é realizada duas vezes no construtor de sort4:

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

O método cherche recebe todos os parâmetros necessários para chamar o método binarySearch:

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // procura 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 opera com o comparador comparateur2, que, por sua vez, utiliza o método compare2 da classe sort4. O método rend retorna a posição do nome procurado na tabela, caso ele 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 a um nome
  public int compare2(Object o1, Object o2){
    // o1 é uma pessoa
     // o2 é uma String, o nome nom2 de uma pessoa
     // deve retornar
     // -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, nessa 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. Ela possui os seguintes métodos:

public abstract boolean hasMoreElements()
retorna verdadeiro se a enumeração ainda tiver elementos
public abstract Object nextElement()
retorna a referência do próximo elemento da enumeração

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

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

Veja 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[]){

// 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 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 na 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

Veja 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 arquivos de texto

4.9.1. Gravação

Para gravar em um arquivo, é necessário dispor de um fluxo de gravação. Para isso, pode-se utilizar a classe FileWriter. Os construtores mais utilizados são os seguintes:

FileWriter(String fileName)
cria o arquivo com o nome fileName — em seguida, é possível gravar nele — um eventual arquivo com o mesmo nome será sobrescrito
FileWriter(String fileName,
boolean append)
idem — um eventual arquivo com o mesmo nome pode ser utilizado abrindo-o no modo de acréscimo (append=true)

A classe FileWriter oferece vários métodos para gravar em um arquivo, métodos herdados da classe Writer. Para gravar em um arquivo de texto, é preferível usar 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 gravação (em um arquivo, na rede, …)
PrintWriter(Writer out, boolean autoflush)
O mesmo. O segundo argumento controla o buffer das linhas. Quando é definido como falso (valor padrão), as linhas gravadas no arquivo passam por um buffer na memória. Quando este fica cheio, o conteúdo é gravado no arquivo. Isso melhora o acesso ao disco. No entanto, às vezes, esse comportamento é indesejável, especialmente ao gravar na rede.

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

void print(Type T)
grava o dado 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 gravação

Aqui está um programa que grava algumas linhas em um arquivo de texto:

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

public class ecrire{
  public static void main(String[] arg){
    // abertura do arquivo
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // gravação no arquivo
    try{
      fic.println("Jean,Dupont,27");
      fic.println("Pauline,Garcia,24");
      fic.println("Gilles,Dumond,56");
    } catch (Exception e){
      Erreur(e,3);
    }
     // fechamento do arquivo
    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 arquivo out obtido na execução é o seguinte:

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

4.9.2. Ler

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

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

A classe FileReader possui vários métodos para ler um arquivo, métodos herdados da classe Reader. Para ler linhas de texto em um arquivo 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. Esse fluxo do tipo Reader pode ser proveniente do teclado, de um arquivo, da rede, etc.

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

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

Aqui está um programa que lê o conteúdo do arquivo criado anteriormente:

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

public class lire{
  public static void main(String[] arg){
    personne p=null;
     // abertura do arquivo
    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 possíveis 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);
    }

     // fechamento do arquivo
    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. Salvamento de um objeto “pessoa”

Aplicamos o que acabamos de ver para fornecer à classe personne um método que permita salvar os atributos de uma pessoa em um arquivo. Adicionamos o método sauveAttributs à definição da classe personne:


    // ------------------------------
  // salvar em arquivo 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 gravar. 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 arquivo
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // gravação no arquivo
    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);
    }
     // fechamento do arquivo
    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 arquivos binários

4.10.1. A classe RandomAccessFile

A classe RandomAccessFile permite gerenciar arquivos binários, especialmente aqueles com estrutura fixa, como os conhecidos na linguagem C/C++. Aqui estão alguns métodos e construtores úteis:

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

4.10.2. A classe artigo

Todos os exemplos a seguir 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. Gravar um registro

O programa a seguir grava um registro em um arquivo chamado “data”:

// classes importadas
import java.io.*;

public class test1{

// testa a gravação de uma estrutura (no sentido do C) em um arquivo 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 arquivo 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 arquivo
        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

        // pronto
        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);
       // estoques
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// fim da gravação

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

É o programa a seguir que nos permite verificar se a execução ocorreu corretamente.

4.10.4. Ler um registro

// classes importadas
import java.io.*;

public class test2{

// testa a gravação de uma estrutura (no sentido do C) em um arquivo binário

     // a estrutura artigo
    private static class article{
         // definimos 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 arquivo binário no qual os artigos serão armazenados
        RandomAccessFile fic=null;

       // abre-se o arquivo 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 arquivo
    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 registro lido
      affiche(art);

       // concluído
      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();
    // estoques
        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

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    

Conseguimos recuperar o registro que havia sido gravado pelo programa de gravação.

4.10.5. Conversão de texto para binário

O programa a seguir é uma extensão do programa de gravação de um registro. Agora, gravamos vários registros em um arquivo binário chamado data.bin. Os dados são extraídos do seguinte arquivo 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{

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

     // a estrutura do artigo
    private static class article{
         // definimos 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 arquivo 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 arquivo 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);
        }

       // arquivo .txt --> arquivo .bin
      String ligne=null;
      String[] champs=null;
      int numLigne=0;
      String champ=null;
    article art=new article();    // artigo a ser criado
      try{
          while((ligne=dataTxt.readLine())!=null){
          // uma linha de +
              numLigne++;
         // divisã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);
         // sobrenome, nome
              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);
              }
        // estoque atual
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }
        // estoque atual
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }             
               // gravamos o registro
              try{
                  ecrire(dataBin,art);
              } catch (IOException E){
                  erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
              }
               // avança para a próxima linha
          }// fim do while
      } catch (IOException E){
          erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
      }        
        // concluído
      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);
       // estoques
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// fim da gravação

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

}// fim da classe

Este é o programa que permite verificar se o anterior funcionou corretamente.

4.10.6. Conversão de binário para texto

O programa a seguir lê o conteúdo do arquivo binário data.bin criado anteriormente e grava esse conteúdo no arquivo de texto data.text. Se tudo correr bem, o arquivo data.text deve ser idêntico ao arquivo original data.txt.

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

public class test5{

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

     // a estrutura do artigo
    private static class article{
         // definimos 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 arquivo 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 em um arquivo 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);
        }

      // arquivo .bin --> arquivo .text
    article art=new article();    // artigo a ser criado

       // o arquivo binário é processado
      int numRecord=0;
      long l=0;    // tamanho do arquivo
      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 arquivo
      try{
          pos=dataBin.getFilePointer();
      } catch (IOException e){
          erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
      }

     // enquanto não tiver sido ultrapassado o fim do arquivo
        while(pos<l){
            // ler o registro 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);

             // gravação da 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

       // pronto
      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 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();
    // estoques
        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

Aqui está 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 registros

Este último programa ilustra a possibilidade de acessar diretamente os registros de um arquivo binário. Ele exibe o registro do arquivo data.bin, cujo número é passado como parâmetro, sendo que o primeiro registro tem o número 1.

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

public class test6{

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

     // a estrutura do artigo
    private static class article{
         // definimos 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úmero do registro
      int numRecord=0;
      try{
          numRecord=Integer.parseInt(args[0]);
      } catch(Exception e){
          erreur(syntaxe+"\nNuméro de fiche incorrect",21);
      }

       // abre-se o arquivo binário para 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);
      }

       // navega-se até o registro desejado
      try{
          dataBin.seek((numRecord-1)*40);
      } catch (Exception e){
          erreur("La fiche "+numRecord+" n'existe pas",23);
      }

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

       // exibimos o arquivo
      affiche(art);

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

  }// fim do main

    // método de leitura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // código de leitura
        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();
    // estoques
        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

Aqui estão 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. Usar expressões regulares

4.11.1. O pacote java.util.regex

O pacote java.util.regex permite o uso de expressões regulares. Elas permitem verificar o formato de uma sequência de caracteres. Assim, é possível verificar se uma sequência que representa uma data está, de fato, no formato dd/mm/aa. Para isso, utiliza-se um modelo e compara-se a sequência com esse modelo. Portanto, neste exemplo, j, m e a devem ser números. O modelo de um formato de data válido é, portanto, “\d\d/\d\d/\d\d”, em que o símbolo \d representa um algarismo. Os símbolos que podem ser utilizados em um 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 “\(" corresponde a “(”.
^
Corresponde ao início da entrada.
$
Corresponde ao final da entrada.
*
Corresponde ao caractere anterior zero ou mais vezes. Assim, “zo*” corresponde a “z” ou a “zoo”.
+
Corresponde ao caractere anterior uma ou mais vezes. Assim, “zo+” corresponde a “zoo”, mas não a “z”.
?
Corresponde ao caractere anterior zero ou uma vez. Por exemplo, “a?ve?” corresponde a “ve” em “lever”.
.
Corresponde a qualquer caractere único, exceto o caractere de nova linha.
(padrão)
Pesquisa por modèle e armazena a correspondência. A subcadeia correspondente pode ser extraída da coleção Matches obtida, usando Item [0]...[n]. Para encontrar correspondências com caracteres entre parênteses ( ), use "\(" ou "\)".
x|y
Corresponde tanto a x quanto 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 caractere. 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 caractere. 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 pelo menos n e, no máximo, m vezes o caractere. 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 “prato”.
[a-z]
Intervalo de caracteres. Corresponde a qualquer caractere na 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 esteja na série especificada. Por exemplo, “[^m-z]” corresponde a qualquer caractere que não esteja 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 delimitador que não representa uma palavra. “en*t\B” corresponde a “ent” em “bien entendu”.
\d
Corresponde a um caractere que representa um algarismo. É equivalente a [0-9].
\D
Corresponde a um caractere que não representa um algarismo. É equivalente a [^0-9].
\f
Corresponde a um caractere de quebra de página.
\n
Corresponde a um caractere de nova linha.
\r
Corresponde a um caractere de retorno de carro.
\s
Corresponde a qualquer espaço em branco, incluindo espaço, tabulação, quebra 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 caractere de tabulação.
\v
Corresponde a um caractere de tabulação vertical.
\w
Corresponde a qualquer caractere que represente uma palavra e inclua um sublinhado. É equivalente a “[A-Za-z0-9_]”.
\W
Corresponde a qualquer caractere que não represente uma palavra. É equivalente a “[^A-Za-z0-9_]”.
\num
Corresponde a num, onde num é um número inteiro positivo. Refere-se às correspondências armazenadas. Por exemplo, “(.)\1” corresponde a dois caracteres idênticos consecutivos.
\n
Corresponde a n, onde n é um valor de escape octal. Os valores de escape octais devem conter 1, 2 ou 3 dígitos. Por exemplo, “\11” e “\011” correspondem ambos a um caractere de tabulação. "\0011" equivale a "\001" e "1". Os valores de escape octais não devem exceder 256. Se isso ocorrer, apenas os dois primeiros dígitos serão considerados na expressão. Permite usar os códigos ASCII em expressões regulares.
\xn
Corresponde a n, onde n é um valor de escape hexadecimal. Os valores de escape hexadecimais devem conter obrigatoriamente dois dígitos. Por exemplo, "\x41" corresponde a "A". "\x041" equivale a "\x04" e "1". Permite usar os códigos ASCII em expressões regulares.

Um elemento em um modelo pode aparecer uma ou mais vezes. Vejamos alguns exemplos relacionados ao símbolo \d, que representa um dígito:

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

Imaginemos agora o modelo capaz de descrever o formato esperado para uma sequência 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 por espaços
\s*\d+\s*
um número inteiro que pode ser com sinal e precedido ou seguido por espaços
\s*[+|-]?\s*\d+\s*
um número real sem sinal que pode ser precedido ou seguido por espaços
\s*\d+(.\d*)?\s*
um número real que pode ser com sinal e precedido ou seguido por espaços
\s*[+|]?\s*\d+(.\d*)?\s*
uma sequência de caracteres contendo a palavra “justo”
\bjusto\b
  

É possível especificar onde procurar o padrão na sequência:

padrão
significado
^padrão
o padrão inicia a sequência
padrão$
o modelo encerra a sequência
^padrão$
o modelo inicia e encerra a sequência
padrão
o padrão é procurado em toda a sequência, começando pelo início dela.
sequência pesquisada
padrão
uma sequência que termina com um ponto de exclamação
!$
uma sequência que termina com um ponto
\.$
uma sequência que começa com //
^//
uma sequência contendo apenas uma palavra, eventualmente seguida ou precedida por espaços
^\s*\w+\s*$
uma sequência contendo duas palavras, eventualmente seguidas ou precedidas por espaços
^\s*\w+\s*\w+\s*$
uma sequência 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 sequência corresponde a um padrão específico, como também é possível recuperar nessa sequência os elementos correspondentes aos subconjuntos do padrão que foram colocados entre parênteses. Assim, se analisarmos uma sequência que contenha uma data dd/mm/aa e quisermos, além disso, extrair os elementos dd, mm e aa dessa data, utilizaremos o modelo (\d\d)/(\d\d)/(\d\d).

4.11.2. Verificar se uma sequência corresponde a um modelo determinado

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

boolean Matches(String modèle, String chaine)

com: modèle: o modelo a ser verificado; chaine: a sequência a ser comparada com o modelo. O resultado é o valor booleano true se a sequência corresponder ao modelo; false caso contrário.

Veja um exemplo:

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

// gerenciamento 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*$]

Observe que, no padrão "^\s*\d+\s*$", o caractere \ deve ser duplicado devido à interpretação específica que o Java faz desse caractere. Portanto, escreve-se: String padrão1 = "^\\s*\\d+\\s*$";

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

Consideremos o padrão "\d+" e a string " 123 456 789 ". O padrão aparece em três pontos diferentes da string. As classes Pattern e Matcher permitem recuperar as diferentes ocorrências de um padrão em uma string. A classe Pattern é a classe responsável pelas expressões regulares. Uma expressão regular usada mais de uma vez precisa ser “compilada”. Isso acelera as buscas pelo padrão nas cadeias de caracteres. O método estático compile realiza essa tarefa:

public static Pattern compile(String regex)

Ela recebe como parâmetro a string do padrão e retorna um objeto Pattern. Para comparar o padrão de um objeto Pattern com uma string de caracteres, utiliza-se a classe Matcher. Essa classe permite a comparação de um padrão com uma string 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 sequência de caracteres que deve ser comparada ao padrão.

Assim, para comparar o padrão "\d+" com a sequência " 123 456 789 ", é possível criar um objeto Matcher da seguinte maneira:

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 isso, 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 string analisada a primeira ocorrência do padrão. Uma segunda chamada ao método find procurará a ocorrência seguinte. E assim por diante. O método retorna true se encontrar o padrão; caso contrário, retorna false. A parte da sequência correspondente à última ocorrência encontrada por find é obtida com o método group e sua posição com o método start. Assim, se continuarmos com o exemplo anterior e quisermos exibir todas as ocorrências do padrão "\d+" na sequência " 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 reinicializar o objeto Matcher no início da sequência comparada ao padrão. Assim, o método find encontrará novamente a primeira ocorrência do padrão.

Veja um exemplo completo:

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

// gerenciamento 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";
    // busca por 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
}//classifica

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

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

Vejamos o seguinte exemplo:

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

// gerenciamento 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+"]");
      // exibição dos 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+"]");
      }
    }//Página inicial

    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 está 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+"]");
      // exibindo 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 sequência exemplaire4 é comparada ao modelo regex3 por meio do método find. Uma ocorrência do modelo regex3 é então encontrada na sequência exemplaire4. Se o modelo incluir subconjuntos entre parênteses, estes estão disponíveis por meio de diversos 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 group(i) fornece o subconjunto nº i. Este é encontrado na sequência em uma posição determinada por 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 localizará a sequência 18:05:49 e criará automaticamente os três subconjuntos definidos pelos parênteses do modelo, respectivamente 18, 05 e 49.

4.11.5. Um programa de treinamento

Encontrar a expressão regular que nos permite verificar se uma string corresponde a um determinado modelo pode, às vezes, ser um verdadeiro desafio. O programa a seguir permite praticar. Ele solicita um modelo e uma string e, em seguida, indica se a string corresponde ou não ao modelo.

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

// gerenciamento 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;

    // gerenciamento de erros
    try{
      // solicita-se ao usuário os modelos e os exemplares a serem comparados 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 usuário os exemplos a serem comparados 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;
          // cria-se 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++;
            // ela é exibida
            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
            // próxima sequência
          }//while(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
          // próximo padrão
        }//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

Aqui está 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 sequência de caracteres composta por campos separados por uma sequência separadora definida por uma expressão regular. Por exemplo, se os campos forem separados pelo caractere , precedido ou seguido por qualquer número de espaços, a expressão regular que modela a sequência separadora dos campos seria “\s*,\s*”. O método split da classe Pattern nos permite recuperar os campos em um array:


public String[] split(CharSequence input)

A cadeia input é decomposta em campos, 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 qualquer número de espaços, escreveremos:

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

Aqui está um programa de teste:

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

// gerenciamento 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);
    // exibição
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
    // outra maneira de fazer
    champs=ligne.split("\\s*,\\s*");
    // exibição
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
  }//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 costumam ser chamados da seguinte maneira:

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

onde -oi representa uma opção e vi um valor associado a essa opção. Deseja-se criar uma classe options que permita analisar a sequência de argumentos -o1 v1 v2 ... -o2 v3 v4 … a fim de 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 fornece a lista dos valores vi não associados a uma opção
erreur
número inteiro igual a 0 se não houver erros na linha de argumentos; caso contrário, outro valor:
1: há parâmetros de chamada inválidos
2: há opções inválidas
4: há valores não associados a opções
Se houver vários tipos de erros, esses valores são somados.

Um objeto options pode ser criado de quatro maneiras diferentes:

public options (String arguments, String optionsAcceptables)

arguments
a linha de argumentos -o1 v1 v2 ... -o2 v3 v4 … a ser analisada
optionsAcceptables
a lista de opções 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 aceitos os casos em que essas cadeias tenham sido divididas em palavras e colocadas em um array de cadeias de caracteres. Isso requer três outros 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 (acessadores):

public Hashtable getOptionsValides()

retorna a referência da tabela optionsValides criada durante a criação do objeto de opções

public Hashtable getOptionsInvalides()

retorna a referência da matriz optionsInvalides criada durante a criação do objeto “options”

public String getOptionsSans()

retorna a referência da cadeia optionsSans criada durante a criação do objeto de opções

public int getErreur()

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

public String toString()

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

Aqui está 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 de 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

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

class stringtovector extends Vector

e teria o seguinte construtor:


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

A classe teria o seguinte atributo privado:

        private int erreur;

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

0: a construção ocorreu sem problemas

4: alguns campos solicitados estão ausentes, embora strict=true

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

    public int getErreur()

que retorna o valor do atributo privado erreur.

    public String identite(){

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

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 dicas:

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

4.12.3. Exercício 3

Deseja-se adicionar à classe stringtovector o seguinte construtor:


public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
        
        // cria um vetor com os campos da string S
        // esta é composta por campos separados pelo separador
        // se separador=nulo, a string forma apenas um único campo
        // somente os campos cujos índices estão em sChampsVoulus são desejados
        // os índices começam em 1
        // se sChampsvoulus for nulo ou "", todos os campos são selecionados
        // se strict=verdadeiro, todos os campos desejados devem estar presentes

A lista dos campos desejados está, portanto, em uma string (String) em vez de uma matriz de inteiros (int[]). O atributo privado erreur da classe pode receber um novo valor:

2: a sequência de índices dos campos desejados está incorreta

Aqui está 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 dicas:

  1. É preciso seguir o exemplo do construtor anterior, transferindo os campos da string sChampsVoulus para uma matriz de inteiros. Para isso, divida sChampsVoulus em campos usando um objeto StringTokenizer, cujo atributo countTokens indicará o número de campos obtidos. Em seguida, é possível criar uma matriz de inteiros com o tamanho adequado e preenchê-la com os campos obtidos.
  2. Para verificar se um campo é inteiro, utilize o método Integer.parseInt para converter o campo em inteiro e lide com a exceção que será gerada caso essa conversão seja impossível.

4.12.4. Exercício 4

Deseja-se criar uma classe filetovector que permita transferir o conteúdo de um arquivo de texto para um objeto *Vector. Essa 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 arquivo 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 estejam em tChampsVoulus
        // os índices começam em 1
        // se tChampsvoulus for nulo ou vazio, todos os campos são incluídos
        // se strict=verdadeiro, todos os campos desejados devem estar presentes
        // caso contrário, a linha não é armazenada e seu índice
        // é colocado no vetor lignesErronees
        // as linhas em branco são ignoradas
        // assim como as linhas que começam com 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 ocorreu sem problemas

1: não foi possível abrir o arquivo a ser processado

4: alguns campos solicitados estão ausentes, embora strict=true

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

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 puder fornecer os campos solicitados, embora strict=true.

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

    public int getErreur()

que retorna o valor do atributo privado erreur.

    public String identite(){

que exibe 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 arquivo e li são os números das linhas com erros.

Aqui está um exemplo de teste:


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

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

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 arquivo de texto é processado linha por linha. A linha é dividida em campos por meio da classe **stringtovector,** estudada anteriormente.

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

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

4.12.5. Exercício 5

Deseja-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 arquivo 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 estejam em tChampsVoulus
        // os índices começam em   1
        // se sChampsvoulus for nulo ou vazio, todos os campos são selecionados
        // se strict=verdadeiro, todos os campos desejados devem estar presentes
        // caso contrário, a linha não é armazenada e seu índice
        // é colocado no vetor lignesErronees
        // as linhas em branco são ignoradas
        // assim como as linhas que começam com tagCommentaire, se tagCommentaire != null

A lista de índices dos campos desejados agora está em uma cadeia de caracteres (String), em vez de estar em uma matriz de inteiros.

O atributo privado erreur pode ter um valor adicional:

2: a sequência de índices dos campos desejados está incorreta

Aqui está 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 sequência sChampsVoulus em uma tabela de inteiros tChampVoulus para nos aproximarmos do caso do construtor anterior.