4. Classes de uso comum
Neste capítulo, apresentamos várias classes Java de uso comum. Estas possuem numerosos atributos, métodos e construtores. Em cada caso, apresentamos apenas uma pequena parte das classes. Os detalhes destas estão disponíveis na ajuda do Java que apresentamos a seguir.
4.1. A documentação
Se tiver instalado o JDK da Sun na pasta <jdk>, a documentação está disponível na pasta <jdk>\docs:

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


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

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

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

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

Aí encontra-se
- a hierarquia em que a classe se insere, neste caso java.util.Vector
- a lista de campos (atributos) da classe
- a lista de construtores
- a lista de métodos
A seguir, apresentamos várias classes. Convidamos o leitor a verificar sistematicamente a definição completa das classes utilizadas.
4.2. As classes de teste
Os exemplos que se seguem utilizam, por vezes, as classes personne e enseignant. Recordamos aqui a sua definição.
public class personne{
// apelido, nome próprio, idade
private String prenom;
private String nom;
private int age;
// construtor 1
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// construtor 2
public personne(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
// toString
public String toString(){
return "personne("+prenom+","+nom+","+age+")";
}
// acessórios
public String getPrenom(){
return prenom;
}
public String getNom(){
return nom;
}
public int getAge(){
return age;
}
//modificadores
public void setPrenom(String P){
this.prenom=P;
}
public void setNom(String N){
this.nom=N;
}
public void setAge(int age){
this.age=age;
}
}
A classe enseignant deriva da classe personne e está definida da seguinte forma:
class enseignant extends personne{
// atributos
private int section;
// construtor
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
// toString
public String toString(){
return "etudiant("+super.toString()+","+section+")";
}
}
Iremos também utilizar uma classe etudiant derivada da classe personne e definida da seguinte forma:
class etudiant extends personne{
String numero;
public etudiant(String P, String N, int age,String numero){
super(P,N,age);
this.numero=numero;
}
public String toString(){
return "etudiant("+super.toString()+","+numero+")";
}
}
4.3. A classe String
A classe String representa cadeias de caracteres. Seja nom uma variável de cadeia de caracteres:
String nome;
nom é uma referência a um objeto ainda não inicializado. É possível inicializá-lo de duas formas:
nom="cavalo" ou nom=new String("cavalo")
Ambos os métodos são equivalentes. Se, mais tarde, escrevermos nom="peixe", ***nom*** passará a referenciar um novo objeto. O objeto anterior, *String("cheval"),* é perdido e o espaço de memória que ocupava será recuperado.
A classe String possui diversos atributos e métodos. Aqui estão alguns deles:
public char charAt(int i) | retorna o caractere i da cadeia, sendo que o primeiro caractere tem o índice 0. Assim, String("cavalo").charAt(3) é igual a 'v' |
public int compareTo(cadeia2) | chaine1.compareTo(chaine2) compara chaine1 com chaine2 e devolve 0 se chaine1 = chaine2, 1 se chaine1 > chaine2 e -1 se chaine1 < chaine2 |
public boolean equals(Object anObject) | chaine1.equals(cadeia2) retorna verdadeiro se cadeia1 = cadeia2, falso caso contrário |
public String toLowerCase() | chaine1.toLowerCase() converte a cadeia1 em minúsculas |
public String toUpperCase() | chaine1.toUpperCase() converte a string1 em maiúsculas |
public String trim() | chaine1.trim() remove os espaços à esquerda e à direita da string1 |
public String substring(int beginIndex, int endIndex) | String("chapeau").subString(2,4) devolve a cadeia "ape" |
public char[] toCharArray() | permite colocar os caracteres da cadeia numa matriz de caracteres |
int length() | número de caracteres da cadeia |
int indexOf(String chaine2) | retorna a primeira posição de chaine2 na cadeia atual ou -1 se chaine2 não estiver presente |
int indexOf(String cadeia2, int startIndex) | retorna a primeira posição de chaine2 na cadeia atual ou -1 se chaine2 não estiver presente. A pesquisa começa a partir do carácter n.º startIndex. |
int lastIndexOf(String cadeia2) | retorna a última posição de chaine2 na cadeia atual ou -1 se chaine2 não estiver presente |
boolean startsWith(String cadeia2) | retorna verdadeiro se a cadeia atual começar por chaine2 |
boolean endsWith(String cadeia2) | retorna verdadeiro se a cadeia atual terminar em chaine2 |
boolean matches(String regex) | retorna verdadeiro se a cadeia atual corresponder à expressão regular regex. |
String[] split(String regex) | A cadeia atual é composta por campos separados por uma sequência de caracteres modelada pela expressão regular regex. O método split permite recuperar os campos numa matriz. |
String replace(char oldChar, char newChar) | substitui, na cadeia atual, o caractere oldChar pelo caractere newChar. |
Eis um programa de exemplo:
// importações
import java.io.*;
public class string1{
// uma classe de demonstração
public static void main(String[] args){
String uneChaine="l'oiseau vole au-dessus des nuages";
affiche("uneChaine="+uneChaine);
affiche("uneChaine.Length="+uneChaine.length());
affiche("chaine[10]="+uneChaine.charAt(10));
affiche("uneChaine.IndexOf(\"vole\")="+uneChaine.indexOf("vole"));
affiche("uneChaine.IndexOf(\"x\")="+uneChaine.indexOf("x"));
affiche("uneChaine.LastIndexOf('a')="+uneChaine.lastIndexOf('a'));
affiche("uneChaine.LastIndexOf('x')="+uneChaine.lastIndexOf('x'));
affiche("uneChaine.substring(4,7)="+uneChaine.substring(4,7));
affiche("uneChaine.ToUpper()="+uneChaine.toUpperCase());
affiche("uneChaine.ToLower()="+uneChaine.toLowerCase());
affiche("uneChaine.Replace('a','A')="+uneChaine.replace('a','A'));
String[] champs=uneChaine.split("\\s+");
for (int i=0;i<champs.length;i++){
affiche("champs["+i+"]=["+champs[i]+"]");
}//para
affiche("(\" abc \").trim()=["+" abc ".trim()+"]");
}//Main
// exibe
public static void affiche(String msg){
// exibe msg
System.out.println(msg);
}//exibe
}//classe
e os resultados obtidos:
uneChaine=l'oiseau vole au-dessus des nuages
uneChaine.Length=34
chaine[10]=o
uneChaine.IndexOf("vole")=9
uneChaine.IndexOf("x")=-1
uneChaine.LastIndexOf('a')=30
uneChaine.LastIndexOf('x')=-1
uneChaine.substring(4,7)=sea
uneChaine.ToUpper()=L'OISEAU VOLE AU-DESSUS DES NUAGES
uneChaine.ToLower()=l'oiseau vole au-dessus des nuages
uneChaine.Replace('a','A')=l'oiseAu vole Au-dessus des nuAges
champs[0]=[l'oiseau]
champs[1]=[vole]
champs[2]=[au-dessus]
champs[3]=[des]
champs[4]=[nuages]
(" abc ").trim()=[abc]
4.4. A classe Vector
Um vetor é um array dinâmico cujos elementos são referências a objetos. Trata-se, portanto, de um array de objetos cujo tamanho pode variar ao longo do tempo, o que não é possível com os arrays estáticos que vimos até agora. Aqui estão alguns campos, construtores ou métodos desta classe:
public Vector() | constrói um vetor vazio |
public final int size() | número de elementos do vetor |
public final void addElement(Object obj) | adiciona o objeto referenciado por obj ao vetor |
public final Object elementAt(int index) | referência ao objeto n.º index do vetor — os índices começam em 0 |
public final Enumeration elements() | o conjunto de elementos do vetor sob a forma de uma enumeração |
public final Object firstElement() | referência ao primeiro elemento do vetor |
public final Object lastElement() | referência ao último elemento do vetor |
public final boolean isEmpty() | retorna «true» se o vetor estiver vazio |
public final void removeElementAt(int index) | remove o elemento com o índice index |
public final void removeAllElements() | esvazia o vetor de todos os seus elementos |
public final String toString() | retorna uma cadeia de caracteres que identifica o vetor |
Eis um programa de teste:
// as classes importadas
import java.util.*;
public class test1{
// o programa principal main - estático - método de classe
public static void main(String arg[]){
// a criação de objetos (instâncias de classes)
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// o polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// um vetor
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
for(int i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
} // fim de main
}// fim da classe
Vamos compilar este programa:
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>dir
10/06/2002 10:41 1 134 personne.class
10/06/2002 10:41 619 enseignant.class
10/06/2002 10:41 610 etudiant.class
10/06/2002 10:42 1 035 test1.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>javac test1.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>dir
10/06/2002 10:41 1 134 personne.class
10/06/2002 10:41 619 enseignant.class
10/06/2002 10:41 610 etudiant.class
10/06/2002 10:42 1 035 test1.java
10/06/2002 10:43 1 506 test1.class
Vamos executar o ficheiro test1.class:
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\vector>java test1
p=personne(Jean,Dupont,30)
en=etudiant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=etudiant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
Taille du vecteur V = 3
V[0]=personne(Jean,Dupont,30)
V[1]=etudiant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)
Daqui em diante, não repetiremos o processo de compilação e execução dos programas de teste. Basta repetir o que foi feito acima.
4.5. A classe ArrayList
A classe ArrayList é análoga à classe Vector. A única diferença significativa surge quando é utilizada simultaneamente por várias threads de execução. Os métodos de sincronização das threads para o acesso a um Vector ou a um ArrayList diferem. Fora deste caso, pode utilizar-se indistintamente um ou outro. Aqui estão alguns campos, construtores ou métodos desta classe:
ArrayList() | constrói uma lista vazia |
int size() | número de elementos da lista |
void add(Object obj) | adiciona o objeto referenciado por obj à lista |
void add(int index, Object obj) | adiciona o objeto referenciado por obj à tabela na posição index |
Object get(int index) | referência do objeto n.º index da tabela — os índices começam em 0 |
boolean isEmpty() | retorna verdadeiro se a tabela estiver vazia |
void remove(int index) | remove o elemento com o índice index |
void clear() | esvazia a matriz de todos os seus elementos |
Object[] toArray() | converte a matriz dinâmica numa matriz clássica |
String toString() | retorna uma cadeia de caracteres que identifica a matriz |
Eis um programa de teste:
// as classes importadas
import java.util.*;
public class test1{
// o programa principal main - estático - método de classe
public static void main(String arg[]){
// a criação de objetos instâncias de classes
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p);
System.out.println("en="+en);
System.out.println("et="+et);
// polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2);
personne p3=(personne)et;
System.out.println("p3="+p3);
// um vetor
ArrayList personnes=new ArrayList();
personnes.add(p);personnes.add(en);personnes.add(et);
System.out.println("Nombre de personnes = "+personnes.size());
for(int i=0;i<personnes.size();i++){
p2=(personne) personnes.get(i);
System.out.println("personnes["+i+"]="+p2);
}
} // fim de main
}// fim da classe
Os resultados obtidos são os mesmos que anteriormente.
4.6. A classe Arrays
A classe java.util.Arrays dá acesso a métodos estáticos que permitem realizar diversas operações em tabelas, nomeadamente ordenações e pesquisas de elementos. Aqui estão alguns desses métodos:
static void sort(matriz) | ordena o tableau utilizando, para tal, a ordem implícita do tipo de dados da matriz, seja um número ou cadeias de caracteres. |
static void sort (Object[] tabela, Comparator C) | ordena o tableau utilizando a função de comparação C para comparar os elementos |
static int binarySearch(tabela, elemento) | retorna a posição de élément em tableau ou um valor <0 caso contrário. A matriz deve estar previamente ordenada. |
static int binarySearch(Object[] tabela, Object elemento, Comparator C) | O mesmo, mas utiliza a função de comparação C para comparar dois elementos da matriz. |
Eis um primeiro exemplo:
import java.util.*;
public class sort2 implements Comparator{
// uma classe privada interna
private class personne{
private String nom;
private int age;
public personne(String nom, int age){
this.nom=nom; // nome da pessoa
this.age=age; // a sua idade
}
// obter a idade
public int getAge(){
return age;
}
// identidade da pessoa
public String toString(){
return ("["+nom+","+age+"]");
}
}; // classe «pessoa»
// fabricante
public sort2() {
// uma tabela de pessoas
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// ordenação da tabela de pessoas
Arrays.sort(amis,this);
// verificação
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//criador
// a função que compara pessoas
public int compare(Object o1, Object o2){
// deve devolver
// -1 se o1 for «menor que» o2
// 0 se o1 for «igual a» o2
// +1 se o1 for «maior que» o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//compara
// função de teste
public static void main(String[] arg){
new sort2();
}//mão
}//classe
Vamos analisar este programa. A função main cria um objeto *sort2*. O construtor da classe *sort2* é o seguinte:
// construtor
public sort2() {
// uma tabela de pessoas
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// ordenação da tabela de pessoas
Arrays.sort(amis,this);
// verificação
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//fabricante
A tabela a ordenar é uma tabela de objetos personne. A classe personne está definida de forma privada (private) no interior da classe sort2. O método estático sort da classe Arrays não sabe como ordenar um array de objetos personne, pelo que, neste caso, é necessário utilizar a forma void sort(Object[] obj, Comparator C). Comparator é uma interface que define apenas um método:
e que deve devolver 0: se o1=o2, -1: se o1 < o2, +1: se o1 > o2. No protótipo void sort(Object[] obj, Comparator C), o segundo argumento C deve ser um objeto que implemente a interface Comparator. No construtor sort2, escolheu-se o objeto atual this:
Isto obriga-nos a fazer duas coisas:
- indicar que a classe sort2 implementa a interface Comparator
- escrever a função compare na classe sort2.
Esta é a seguinte:
// a função que compara pessoas
public int compare(Object o1, Object o2){
// deve devolver
// -1 se o1 for «menor que» o2
// 0 se o1 for «igual a» o2
// +1 se o1 for «maior que» o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//compara
Para comparar dois objetos personne, utiliza-se aqui a idade (também se poderia ter utilizado o nome).
Os resultados da execução são os seguintes:
Poderíamos ter procedido de forma diferente para implementar a interface Comparator:
import java.util.*;
public class sort2 {
// uma classe privada interna
private class personne{
…….
}; // classe pessoa
// construtor
public sort2() {
// um array de pessoas
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// ordenação da matriz de pessoas
Arrays.sort(amis,
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//compara
}//classe
);
// verificação
for(int i=0;i<3;i++)
System.out.println(amis[i]);
}//criador
// a função que compara pessoas
public int compare1(Object o1, Object o2){
// deve devolver
// -1 se o1 for «menor que» o2
// 0 se o1 for «igual a» o2
// +1 se o1 for «maior que» o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//compara1
// main
public static void main(String[] arg){
new sort2();
}//main
}//classe
A instrução de ordenação ficou assim:
// ordenação da tabela de pessoas
Arrays.sort(amis,
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//comparar
}//turma
);
O segundo parâmetro do método sort deve ser um objeto que implemente a interface Comparator. Aqui, criamos um objeto desse tipo através do método new java.util.Comparator() e o texto que se segue {….} define a classe da qual se cria um objeto. Chama-se a isto uma classe anónima, uma vez que não tem nome. Nesta classe anónima, que deve implementar a interface Comparator, define-se o método compare dessa interface. Este método limita-se a chamar o método compare1 da classe sort2. Assim, voltamos ao caso anterior.
A classe sort2 já não implementa a interface Comparator. Assim, a sua declaração passa a ser:
Agora, testamos o método binarySearch da classe Arrays no exemplo seguinte:
import java.util.*;
public class sort4 {
// uma classe privada interna
private class personne{
// atributos
private String nom;
private int age;
// construtor
public personne(String nom, int age){
this.nom=nom; // nome da pessoa
this.age=age; // a sua idade
}
// obter o nome
public String getNom(){
return nom;
}
// obter a idade
public int getAge(){
return age;
}
// identidade da pessoa
public String toString(){
return ("["+nom+","+age+"]");
}
}; // categoria da pessoa
// fabricante
public sort4() {
// uma tabela de pessoas
personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
new personne("tournesol",40)};
// comparadores
java.util.Comparator comparateur1=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//compara
}//classe
;
java.util.Comparator comparateur2=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare2(o1,o2);
}//compara
}//classifica
;
// ordenação da tabela de pessoas
Arrays.sort(amis,comparateur1);
// verificação
for(int i=0;i<3;i++)
System.out.println(amis[i]);
// pesquisas
cherche("milou",amis,comparateur2);
cherche("xx",amis,comparateur2);
}//criador
// a função que compara pessoas
public int compare1(Object o1, Object o2){
// deve devolver
// -1 se o1 for «menor que» o2
// 0 se o1 for «igual a» o2
// +1 se o1 for «maior que» o2
personne p1=(personne)o1;
personne p2=(personne)o2;
int age1=p1.getAge();
int age2=p2.getAge();
if(age1<age2) return (-1);
else if (age1==age2) return (0);
else return +1;
}//compara1
// a função que compara uma pessoa com um nome
public int compare2(Object o1, Object o2){
// o1 é uma pessoa
// o2 é uma String, o nome nom2 de uma pessoa
// deve devolver
// -1 se o1.nom for «menor que» nom2
// 0 se o1.nom for «igual a» nom2
// +1 se o1.nom for «maior que» nome2
personne p1=(personne)o1;
String nom1=p1.getNom();
String nom2=(String)o2;
return nom1.compareTo(nom2);
}//compara2
public void cherche(String ami,personne[] amis, Comparator comparateur){
// procura amigo na tabela de amigos
int position=Arrays.binarySearch(amis,ami,comparateur);
// encontrado?
if(position>=0)
System.out.println(ami + " a " + amis[position].getAge() + " ans");
else System.out.println(ami + " n'existe pas dans le tableau");
}//procurar
// mão
public static void main(String[] arg){
new sort4();
}//mão
}//turma
Aqui, procedemos de forma um pouco diferente dos exemplos anteriores. Os dois objetos Comparator necessários para os métodos sort e binarySearch foram criados e atribuídos às variáveis comparateur1 e comparateur2.
// comparadores
java.util.Comparator comparateur1=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare1(o1,o2);
}//compara
}//classe
;
java.util.Comparator comparateur2=
new java.util.Comparator(){
public int compare(Object o1, Object o2){
return compare2(o1,o2);
}//compara
}//classe
;
É efetuada duas vezes uma pesquisa dicotómica na tabela amis no construtor de sort4:
O método cherche recebe todos os parâmetros de que necessita para chamar o método binarySearch:
public void cherche(String ami,personne[] amis, Comparator comparateur){
// procurar amigo na lista de amigos
int position=Arrays.binarySearch(amis,ami,comparateur);
// encontrado?
if(position>=0)
System.out.println(ami + " a " + amis[position].getAge() + " ans");
else System.out.println(ami + " n'existe pas dans le tableau");
}//procura
O método binarySearch funciona em conjunto com o comparador comparateur2, que, por sua vez, recorre ao método compare2 da classe sort4. O método rend devolve a posição do nome procurado na tabela, caso este exista, ou um número <0, caso contrário. O método compare2 serve para comparar um objeto personne com um nome do tipo String.
// a função que compara uma pessoa com um nome
public int compare2(Object o1, Object o2){
// o1 é uma pessoa
// o2 é uma String, o nome «nome2» de uma pessoa
// deve devolver
// -1 se o1.nom for «menor que» nome2
// 0 se o1.nom for «igual a» nome2
// +1 se o1.nom for «maior que» nome2
personne p1=(personne)o1;
String nom1=p1.getNom();
String nom2=(String)o2;
return nom1.compareTo(nom2);
}//compara2
Ao contrário do método sort, o método binarySearch não recebe dois objetos personne, mas sim um objeto personne e um objeto String, por esta ordem. O primeiro parâmetro é um elemento da matriz amis, o segundo é o nome da pessoa procurada.
4.7. A classe Enumeration
Enumeration é uma interface e não uma classe. Possui os seguintes métodos:
public abstract boolean hasMoreElements() | retorna «true» se a enumeração ainda tiver elementos |
public abstract Object nextElement() | retorna a referência ao elemento seguinte da enumeração |
Como se utiliza uma enumeração? Geralmente, da seguinte forma:
Enumeration e=… // recupera-se um objeto de enumeração
while(e.hasMoreElements()){
// utilizar o elemento e.nextElement()
}
Eis um exemplo:
// as classes importadas
import java.util.*;
public class test1{
// o programa principal main - estático - método de classe
public static void main(String arg[]){
// a criação de objetos (instâncias de classes)
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// o polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// um vetor
Vector V=new Vector();
V.addElement(p);V.addElement(en);V.addElement(et);
System.out.println("Taille du vecteur V = "+V.size());
int i;
for(i=0;i<V.size();i++){
p2=(personne) V.elementAt(i);
System.out.println("V["+i+"]="+p2.toString());
}
// uma enumeração
Enumeration E=V.elements();
i=0;
while(E.hasMoreElements()){
p2=(personne) E.nextElement();
System.out.println("V["+i+"]="+p2.toString());
i++;
}
}// fim de main
}//fim da classe
Obtêm-se os seguintes resultados:
p=personne(Jean,Dupont,30)
en=enseignant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=enseignant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
Taille du vecteur V = 3
V[0]=personne(Jean,Dupont,30)
V[1]=enseignant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)
V[0]=personne(Jean,Dupont,30)
V[1]=enseignant(personne(Paula,Hanson,56),27)
V[2]=etudiant(personne(Chris,Garot,22),19980405)
4.8. A classe Hashtable
A classe Hashtable permite implementar um dicionário. Pode-se considerar um dicionário como uma tabela com duas colunas:
chave | valor |
chave1 | valor1 |
chave2 | valor2 |
.. | ... |
As chaves são únicas, c.a.d. Não pode haver duas chaves idênticas. Os principais métodos e propriedades da classe Hashtable são os seguintes:
public Hashtable() | construtor - cria um dicionário vazio |
public int size() | número de elementos no dicionário — sendo que um elemento é um par (chave, valor) |
public Object put(Object key, Object value) | adiciona o par (chave, valor) ao dicionário |
public Object get(Object key) | recupera o objeto associado à chave key ou null, caso a chave key não exista |
public boolean containsKey(Object key) | verdadeiro se a chave key existir no dicionário |
public boolean contains(Object value) | verdadeiro se o valor value existir no dicionário |
public Enumeration keys() | retorna as chaves do dicionário sob a forma de uma enumeração |
public Object remove(Object key) | remove o par (chave, valor) em que chave = key |
public String toString() | identifica o dicionário |
Eis um exemplo:
// as classes importadas
import java.util.*;
public class test1{
// o programa principal main - estático - método de classe
public static void main(String arg[]){
// a criação de objetos instâncias de classes
personne p=new personne("Jean","Dupont",30);
enseignant en=new enseignant("Paula","Hanson",56,27);
etudiant et=new etudiant("Chris","Garot",22,"19980405");
System.out.println("p="+p.toString());
System.out.println("en="+en.toString());
System.out.println("et="+et.toString());
// polimorfismo
personne p2=(personne)en;
System.out.println("p2="+p2.toString());
personne p3=(personne)et;
System.out.println("p3="+p3.toString());
// um dicionário
Hashtable H=new Hashtable();
H.put("personne1",p);
H.put("personne2",en);
H.put("personne3",et);
Enumeration E=H.keys();
int i=0;
String cle;
while(E.hasMoreElements()){
cle=(String) E.nextElement();
p2=(personne) H.get(cle);
System.out.println("clé "+i+"="+cle+" valeur="+p2.toString());
i++;
}
}//fim de main
}//fim da classe
Os resultados obtidos são os seguintes:
p=personne(Jean,Dupont,30)
en=enseignant(personne(Paula,Hanson,56),27)
et=etudiant(personne(Chris,Garot,22),19980405)
p2=enseignant(personne(Paula,Hanson,56),27)
p3=etudiant(personne(Chris,Garot,22),19980405)
clé 0=personne3 valeur=etudiant(personne(Chris,Garot,22),19980405)
clé 1=personne2 valeur=enseignant(personne(Paula,Hanson,56),27)
clé 2=personne1 valeur=personne(Jean,Dupont,30)
4.9. Os ficheiros de texto
4.9.1. Escrever
Para escrever num ficheiro, é necessário dispor de um fluxo de escrita. Para tal, pode utilizar-se a classe FileWriter. Os construtores mais frequentemente utilizados são os seguintes:
FileWriter(String fileName) | cria o ficheiro com o nome fileName — é possível, em seguida, escrever nesse ficheiro — um eventual ficheiro com o mesmo nome é substituído |
FileWriter(String fileName, boolean append) | idem — um eventual ficheiro com o mesmo nome pode ser utilizado abrindo-o no modo de adição (append=true) |
A classe FileWriter oferece vários métodos para escrever num ficheiro, métodos herdados da classe Writer. Para escrever num ficheiro de texto, é preferível utilizar a classe PrintWriter, cujos construtores mais utilizados são os seguintes:
PrintWriter(Writer out) | o argumento é do tipo Writer, c.a.d. um fluxo de escrita (num ficheiro, na rede, …) |
PrintWriter(Writer out, boolean autoflush) | O mesmo. O segundo argumento controla o armazenamento em buffer das linhas. Quando está definido como falso (valor por defeito), as linhas escritas no ficheiro passam por um buffer na memória. Quando este fica cheio, o conteúdo é gravado no ficheiro. Isto melhora o acesso ao disco. No entanto, por vezes, este comportamento é indesejável, nomeadamente quando se escreve na rede. |
Os métodos úteis da classe PrintWriter são os seguintes:
void print(Type T) | escreve o valor T (String, int, …) |
void println(Type T) | faz o mesmo, terminando com um caractere de fim de linha |
void flush() | esvazia o buffer se não estivermos no modo autoflush |
void close() | fecha o fluxo de escrita |
Eis um programa que escreve algumas linhas num ficheiro de texto:
// importações
import java.io.*;
public class ecrire{
public static void main(String[] arg){
// abertura do ficheiro
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// gravação no ficheiro
try{
fic.println("Jean,Dupont,27");
fic.println("Pauline,Garcia,24");
fic.println("Gilles,Dumond,56");
} catch (Exception e){
Erreur(e,3);
}
// fecho do ficheiro
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// fim da função
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Erro
}//classe
O ficheiro out obtido após a execução é o seguinte:
4.9.2. Ler
Para ler o conteúdo de um ficheiro, é necessário dispor de um fluxo de leitura associado ao ficheiro. Para tal, pode utilizar-se a classe FileReader e o seguinte construtor:
FileReader(String nomeDoFicheiro) | abre um fluxo de leitura a partir do ficheiro indicado. Lança uma exceção se a operação falhar. |
A classe FileReader possui vários métodos para ler um ficheiro, métodos herdados da classe Reader. Para ler linhas de texto num ficheiro de texto, é preferível utilizar a classe BufferedReader com o seguinte construtor:
BufferedReader(Reader in) | abre um fluxo de leitura com buffer a partir de um fluxo de entrada in. Este fluxo do tipo Reader pode provir do teclado, de um ficheiro, da rede, etc. |
Os métodos úteis da classe BufferedReader são os seguintes:
int read() | lê um carácter |
String readLine() | lê uma linha de texto |
int read(char[] buffer, int offset, int tamanho) | lê taille caracteres do ficheiro e coloca-os na matriz buffer a partir da posição offset. |
void close() | fecha o fluxo de leitura |
Eis um programa que lê o conteúdo do ficheiro criado anteriormente:
// classes importadas
import java.util.*;
import java.io.*;
public class lire{
public static void main(String[] arg){
personne p=null;
// abertura do ficheiro
BufferedReader IN=null;
try{
IN=new BufferedReader(new FileReader("out"));
} catch (Exception e){
Erreur(e,1);
}
// dados
String ligne=null;
String[] champs=null;
String prenom=null;
String nom=null;
int age=0;
// gestão de eventuais erros
try{
while((ligne=IN.readLine())!=null){
champs=ligne.split(",");
prenom=champs[0];
nom=champs[1];
age=Integer.parseInt(champs[2]);
System.out.println(""+new personne(prenom,nom,age));
}// fim do while
} catch (Exception e){
Erreur(e,2);
}
// fecho do ficheiro
try{
IN.close();
} catch (Exception e){
Erreur(e,3);
}
}// fim de main
// Erro
public static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}
}// fim da classe
A execução do programa produz os seguintes resultados:
4.9.3. Gravação de um objeto «pessoa»
Aplicamos o que acabámos de ver para dotar a classe personne de um método que permita guardar num ficheiro os atributos de uma pessoa. Adicionamos o método sauveAttributs à definição da classe personne:
// ------------------------------
// guardar em ficheiro de texto
// ------------------------------
public void sauveAttributs(PrintWriter P){
P.println(""+this);
}
Antes de definir a classe personne, não se esqueça de importar o pacote java.io:
O método sauveAttributs recebe como único parâmetro o fluxo PrintWriter, no qual deve escrever. Um programa de teste poderia ser o seguinte:
// importações
import java.io.*;
// importar pessoa;
public class sauver{
public static void main(String[] arg){
// abertura do ficheiro
PrintWriter fic=null;
try{
fic=new PrintWriter(new FileWriter("out"));
} catch (Exception e){
Erreur(e,1);
}
// gravação no ficheiro
try{
new personne("Jean","Dupont",27).sauveAttributs(fic);
new personne("Pauline","Garcia",24).sauveAttributs(fic);
new personne("Gilles","Dumond",56).sauveAttributs(fic);
} catch (Exception e){
Erreur(e,3);
}
// fecho do ficheiro
try{
fic.close();
} catch (Exception e){
Erreur(e,2);
}
}// fim da função main
// Erro
private static void Erreur(Exception e, int code){
System.err.println("Erreur : "+e);
System.exit(code);
}//Erro
}//classe
Vamos compilar e executar este programa:
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>javac sauver.java
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>dir
10/06/2002 10:52 1 352 personne.class
10/06/2002 10:53 842 sauver.java
10/06/2002 10:53 1 258 sauver.class
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>java sauver
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>dir
10/06/2002 10:52 1 352 personne.class
10/06/2002 10:53 842 sauver.java
10/06/2002 10:53 1 258 sauver.class
10/06/2002 10:53 83 out
E:\data\serge\JAVA\poly juin 2002\Chapitre 3\sauveAttributs>more out
personne(Jean,Dupont,27)
personne(Pauline,Garcia,24)
personne(Gilles,Dumond,56)
4.10. Os ficheiros binários
4.10.1. A classe RandomAccessFile
A classe RandomAccessFile permite gerir ficheiros binários, nomeadamente aqueles com estrutura fixa, como os que se conhecem na linguagem C/C++. Aqui estão alguns métodos e construtores úteis:
RandomAccessFile(String nomeDoFicheiro, String modo) | construtor — abre o ficheiro indicado no modo especificado. Este modo pode assumir os seguintes valores: r: abertura para leitura rw: abertura para leitura e escrita |
void writeTTT(TTT valor) | grava o valor no ficheiro. TTT representa o tipo de valor. A representação em memória do valor é gravada tal como está no ficheiro. Assim, encontramos writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat,... Para gravar uma cadeia de caracteres, utiliza-se writeBytes(String cadeia). |
TTT readTTT() | lê e devolve um valor do tipo TTT. Assim, temos readBoolean, readByte, readInt, readDouble, readLong, readFloat,... O método read() lê um byte. |
long length() | tamanho do ficheiro em bytes |
long getFilePointer() | posição atual do ponteiro do ficheiro |
void seek(long pos) | posiciona o cursor do ficheiro no byte pos |
4.10.2. A classe «artigo»
Todos os exemplos que se seguem utilizarão a seguinte classe article:
// a estrutura do artigo
private static class article{
// define-se a estrutura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe do artigo
A classe Java article acima será equivalente à seguinte estrutura article em C
struct article{
char code[4];
char nom[20];
double prix;
int stockActuel;
int stockMinimum;
}//estrutura
Assim, limitaremos o código a 4 caracteres e o nome a 20.
4.10.3. Escrever um registo
O programa seguinte grava um registo num ficheiro chamado «data»:
// classes importadas
import java.io.*;
public class test1{
// testa a gravação de uma estrutura (no sentido do C) num ficheiro binário
// a estrutura do artigo
private static class article{
// define-se a estrutura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe «artigo»
public static void main(String arg[]){
// define-se o ficheiro binário no qual os artigos serão armazenados
RandomAccessFile fic=null;
// define-se um artigo
article art=new article();
art.code="a100";
art.nom="velo";
art.prix=1000.80;
art.stockActuel=100;
art.stockMinimum=10;
// define-se o ficheiro
try{
fic=new RandomAccessFile("data","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// escreve-se
try{
ecrire(fic,art);
} catch (IOException E){
erreur("Erreur lors de l'écriture de l'enregistrement",2);
}//try-catch
// está concluído
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}//main
// método de escrita
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// código
fic.writeBytes(art.code);
// o nome está limitado a 20 caracteres
art.nom=art.nom.trim();
int l=art.nom.length();
int nbBlancs=20-l;
if(nbBlancs>0){
String blancs="";
for(int i=0;i<nbBlancs;i++) blancs+=" ";
art.nom+=blancs;
} else art.nom=art.nom.substring(0,20);
fic.writeBytes(art.nom);
// o preço
fic.writeDouble(art.prix);
// os stocks
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// fim da escrita
// ------------------------erro
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fim do erro
}// fim da classe
É o seguinte programa que nos permite verificar se a execução decorreu corretamente.
4.10.4. Ler um registo
// classes importadas
import java.io.*;
public class test2{
// testa a gravação de uma estrutura (no sentido do C) num ficheiro binário
// a estrutura «artigo»
private static class article{
// define-se a estrutura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe «artigo»
public static void main(String arg[]){
// define-se o ficheiro binário no qual os artigos serão armazenados
RandomAccessFile fic=null;
// abre-se o ficheiro para leitura
try{
fic=new RandomAccessFile("data","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data",1);
}//try-catch
// lê-se o único artigo do ficheiro
article art=new article();
try{
lire(fic,art);
} catch (IOException E){
erreur("Erreur lors de la lecture de l'enregistrement",2);
}//try-catch
// exibimos o registo lido
affiche(art);
// fim
try{
fic.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data",2);
}//try-catch
}// fim manual
// método de leitura
public static void lire(RandomAccessFile fic, article art) throws IOException{
// leitura do código
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nome
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// preço
art.prix=fic.readDouble();
// stocks
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fim da gravação
// ---------------------exibir
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fim do anúncio
// ------------------------erro
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fim do erro
}// fim da classe
Os resultados da execução são os seguintes:
E:\data\serge\JAVA\random>java test2
code : a100
nom : velo
prix : 1000.8
Stock actuel : 100
Stock minimum : 10
Consegue-se recuperar corretamente o registo que tinha sido gravado pelo programa de gravação.
4.10.5. Conversão de texto para binário
O programa seguinte é uma extensão do programa de gravação de um registo. Agora, gravamos vários registos num ficheiro binário denominado data.bin. Os dados são retirados do seguinte ficheiro data.txt:
E:\data\serge\JAVA\random>more data.txt
a100:velo:1000:100:10
b100:pompe:65:6:2
c100:arc:867:10:5
d100:fleches - lot de 6:450:12:8
e100:jouet:10:2:3
// classes importadas
import java.io.*;
import java.util.*;
public class test3{
// ficheiro de texto --> ficheiro binário
// a estrutura do artigo
private static class article{
// define-se a estrutura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe «artigo»
public static void main(String arg[]){
// define-se o ficheiro binário no qual os artigos serão armazenados
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","rw");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin",1);
}
// os dados são extraídos de um ficheiro de texto
BufferedReader dataTxt=null;
try{
dataTxt=new BufferedReader(new FileReader("data.txt"));
} catch (IOException E){
erreur("Impossible d'ouvrir le fichier data.txt",2);
}
// ficheiro .txt --> ficheiro .bin
String ligne=null;
String[] champs=null;
int numLigne=0;
String champ=null;
article art=new article(); // artigo a criar
try{
while((ligne=dataTxt.readLine())!=null){
// uma linha de +
numLigne++;
// decomposição em campos
champs=ligne.split(":");
// são necessários 5 campos
if(champs.length!=5)
erreur("Ligne "+numLigne+" erronée dans data.txt",3);
//código
art.code=champs[0];
if(art.code.length()!=4)
erreur("Code erroné en ligne "+numLigne+" du fichier data.txt",12);
// nome, apelido
art.nom=champs[1];
// preço
try{
art.prix=Double.parseDouble(champs[2]);
} catch (Exception E){
erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
}
// stock atual
try{
art.stockActuel=Integer.parseInt(champs[3]);
} catch (Exception E){
erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
}
// stock atual
try{
art.stockActuel=Integer.parseInt(champs[3]);
} catch (Exception E){
erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
}
// grava-se o registo
try{
ecrire(dataBin,art);
} catch (IOException E){
erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
}
// passa-se para a linha seguinte
}// fim do while
} catch (IOException E){
erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
}
// terminado
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",10);
}
try{
dataTxt.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.txt",11);
}
}// fim de main
// método de gravação
public static void ecrire(RandomAccessFile fic, article art) throws IOException{
// código
fic.writeBytes(art.code);
// o nome está limitado a 20 caracteres
art.nom=art.nom.trim();
int l=art.nom.length();
int nbBlancs=20-l;
if(nbBlancs>0){
String blancs="";
for(int i=0;i<nbBlancs;i++) blancs+=" ";
art.nom+=blancs;
} else art.nom=art.nom.substring(0,20);
fic.writeBytes(art.nom);
// o preço
fic.writeDouble(art.prix);
// os stocks
fic.writeInt(art.stockActuel);
fic.writeInt(art.stockMinimum);
}// fim da escrita
// ------------------------erro
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fim do erro
}// fim da classe
É o seguinte programa que permite verificar se este funcionou corretamente.
4.10.6. Conversão de binário para texto
O programa seguinte lê o conteúdo do ficheiro binário data.bin criado anteriormente e transfere esse conteúdo para o ficheiro de texto data.text. Se tudo correr bem, o ficheiro data.text deverá ser idêntico ao ficheiro original data.txt.
// classes importadas
import java.io.*;
import java.util.*;
public class test5{
// ficheiro de texto --> ficheiro binário
// a estrutura do artigo
private static class article{
// define-se a estrutura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe artigo
// função principal
public static void main(String arg[]){
// define-se o ficheiro binário no qual os artigos serão armazenados
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
}
// os dados são gravados num ficheiro de texto
PrintWriter dataTxt=null;
try{
dataTxt=new PrintWriter(new FileWriter("data.text"));
} catch (IOException E){
erreur("Impossible d'ouvrir le fichier data.text en écriture",2);
}
// ficheiro .bin --> ficheiro .text
article art=new article(); // artigo a criar
// o ficheiro binário é processado
int numRecord=0;
long l=0; // tamanho do ficheiro
try{
l=dataBin.length();
} catch (IOException e){
erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
}
long pos=0; // posição atual no ficheiro
try{
pos=dataBin.getFilePointer();
} catch (IOException e){
erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
}
// enquanto não se tiver ultrapassado o fim do ficheiro
while(pos<l){
// ler o registo atual e processá-lo
numRecord++;
try{
lire(dataBin,art);
} catch (Exception e){
erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
}
affiche(art);
// escrever a linha de texto correspondente em dataTxt
dataTxt.println(art.code.trim()+":"+art.nom.trim()+":"+art.prix+":"+art.stockActuel+":"+art.stockMinimum);
// continuamos?
try{
pos=dataBin.getFilePointer();
} catch (IOException e){
erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
}
}// fim do while
// terminou
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",2);
}
try{
dataTxt.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.text",2);
}
}// fim de main
// método de leitura
public static void lire(RandomAccessFile fic, article art) throws IOException{
// leitura do código
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nome
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// preço
art.prix=fic.readDouble();
// stocks
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fim da gravação
// ---------------------exibir
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fim do anúncio
// ------------------------erro
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fim do erro
}// fim da classe
Eis um exemplo de execução:
E:\data\serge\JAVA\random>java test5
code : a100
nom : velo
prix : 1000.0
Stock actuel : 100
Stock minimum : 0
code : b100
nom : pompe
prix : 65.0
Stock actuel : 6
Stock minimum : 0
code : c100
nom : arc
prix : 867.0
Stock actuel : 10
Stock minimum : 0
code : d100
nom : fleches - lot de 6
prix : 450.0
Stock actuel : 12
Stock minimum : 0
code : e100
nom : jouet
prix : 10.0
Stock actuel : 2
Stock minimum : 0
E:\data\serge\JAVA\random>more data.text
a100:velo:1000.0:100:0
b100:pompe:65.0:6:0
c100:arc:867.0:10:0
d100:fleches - lot de 6:450.0:12:0
e100:jouet:10.0:2:0
4.10.7. Acesso direto aos registos
Este último programa ilustra a possibilidade de aceder diretamente aos registos de um ficheiro binário. Apresenta o registo do ficheiro data.bin, cujo número é passado como parâmetro, sendo que o primeiro registo tem o número 1.
// classes importadas
import java.io.*;
import java.util.*;
public class test6{
// ficheiro de texto --> ficheiro binário
// a estrutura do artigo
private static class article{
// define-se a estrutura
public String code;
public String nom;
public double prix;
public int stockActuel;
public int stockMinimum;
}//classe artigo
// função principal
public static void main(String[] args){
// verifica-se os argumentos
int nbArguments=args.length;
String syntaxe="syntaxe : pg numéro_de_fiche";
if(nbArguments!=1)
erreur(syntaxe,20);
// verificação do n.º do registo
int numRecord=0;
try{
numRecord=Integer.parseInt(args[0]);
} catch(Exception e){
erreur(syntaxe+"\nNuméro de fiche incorrect",21);
}
// abre-se o ficheiro binário em modo de leitura
RandomAccessFile dataBin=null;
try{
dataBin=new RandomAccessFile("data.bin","r");
} catch (Exception E){
erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
}
// posiciona-se no registo pretendido
try{
dataBin.seek((numRecord-1)*40);
} catch (Exception e){
erreur("La fiche "+numRecord+" n'existe pas",23);
}
// leitura do registo
article art=new article();
try{
lire(dataBin,art);
} catch (Exception e){
erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
}
// exibe-se
affiche(art);
// Está concluído
try{
dataBin.close();
} catch (Exception E){
erreur("Impossible de fermer le fichier data.bin",2);
}//try-catch
}// fim de main
// método de leitura
public static void lire(RandomAccessFile fic, article art) throws IOException{
// leitura de código
art.code="";
for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
// nome
art.nom="";
for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
art.nom=art.nom.trim();
// preço
art.prix=fic.readDouble();
// stocks
art.stockActuel=fic.readInt();
art.stockMinimum=fic.readInt();
}// fim da gravação
// ---------------------exibir
public static void affiche(article art){
System.out.println("code : "+art.code);
System.out.println("nom : "+art.nom);
System.out.println("prix : "+art.prix);
System.out.println("Stock actuel : "+art.stockActuel);
System.out.println("Stock minimum : "+art.stockMinimum);
}// fim da exibição
// ------------------------erro
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// fim do erro
}// fim da classe
Eis alguns exemplos de execução:
E:\data\serge\JAVA\random>java test6 2
code : b100
nom : pompe
prix : 65.0
Stock actuel : 6
Stock minimum : 0
E:\data\serge\JAVA\random>java.bat test6 20
Erreur lors de la lecture de l'enregistrement 20
4.11. Utilizar expressões regulares
4.11.1. O pacote java.util.regex
O pacote java.util.regex permite a utilização de expressões regulares. Estas permitem verificar o formato de uma cadeia de caracteres. Assim, é possível verificar se uma cadeia que representa uma data está, de facto, no formato dd/mm/aa. Para tal, utiliza-se um modelo e compara-se a cadeia com esse modelo. Assim, neste exemplo, j, m e a devem ser algarismos. O modelo de um formato de data válido é, então, «\d\d/\d\d/\d\d», em que o símbolo \d representa um algarismo. Os símbolos que podem ser utilizados num modelo são os seguintes (documentação da Microsoft):
Caractere | Descrição |
\ | Marca o caractere seguinte como caractere especial ou literal. Por exemplo, «n» corresponde ao caractere «n». «\n» corresponde a um caractere de nova linha. A sequência «\\» corresponde a «\», enquanto que «\»(» corresponde a «(». |
^ | Corresponde ao início da entrada. |
$ | Corresponde ao fim da entrada. |
* | Corresponde ao carácter anterior zero ou mais vezes. Assim, «zo*» corresponde a «z» ou a «zoo». |
+ | Corresponde ao carácter anterior uma ou mais vezes. Assim, «zo+» corresponde a «zoo», mas não a «z». |
? | Corresponde ao carácter anterior zero ou uma vez. Por exemplo, «a?ve?» corresponde a «ve» em «lever». |
. | Corresponde a qualquer carácter único, exceto ao carácter de nova linha. |
(padrão) | Procura o modèle e memoriza a correspondência. A subcadeia correspondente pode ser extraída da coleção Matches obtida, utilizando o Item [0]...[n]. Para encontrar correspondências com caracteres entre parênteses ( ), utilize "\(" ou "\)". |
x|y | Corresponde tanto a x como a y. Por exemplo, «z|foot» corresponde a «z» ou a «foot». «(z|f)oo» corresponde a «zoo» ou a «foo». |
{n} | n é um número inteiro não negativo. Corresponde exatamente a n vezes o carácter. Por exemplo, «o{2}» não corresponde a «o» em «Bob,», mas aos dois primeiros «o» em «fooooot». |
{n,} | n é um número inteiro não negativo. Corresponde a, pelo menos, n vezes o carácter. Por exemplo, «o{2,}» não corresponde ao «o» em «Bob», mas a todos os «o» em «fooooot». «o{1,}» equivale a «o+» e «o{0,}» equivale a «o*». |
{n,m} | m e n são números inteiros não negativos. Corresponde a, no mínimo, n e, no máximo, m vezes o carácter. Por exemplo, «o{1,3}» corresponde aos três primeiros «o» em «foooooot» e «o{0,1}» equivale a «o?». |
[xyz] | Conjunto de caracteres. Corresponde a um dos caracteres indicados. Por exemplo, «[abc]» corresponde a «a» em «plat». |
[^xyz] | Conjunto de caracteres negativo. Corresponde a qualquer caractere não indicado. Por exemplo, «[^abc]» corresponde a «p» em «plat». |
[a-z] | Intervalo de caracteres. Corresponde a qualquer caractere da série especificada. Por exemplo, «[a-z]» corresponde a qualquer letra minúscula entre «a» e «z». |
[^m-z] | Intervalo de caracteres negativo. Corresponde a qualquer caractere que não se encontre na série especificada. Por exemplo, «[^m-z]» corresponde a qualquer caractere que não se encontre entre «m» e «z». |
\b | Corresponde a um delimitador que representa uma palavra, ou seja, à posição entre uma palavra e um espaço. Por exemplo, «er\b» corresponde a «er» em «lever», mas não a «er» em «verbe». |
\B | Corresponde a um limite que não representa uma palavra. «en*t\B» corresponde a «ent» em «bien entendu». |
\d | Corresponde a um carácter que representa um algarismo. É equivalente a [0-9]. |
\D | Corresponde a um carácter que não representa um algarismo. É equivalente a [^0-9]. |
\f | Corresponde a um carácter de salto de página. |
\n | Corresponde a um carácter de nova linha. |
\r | Corresponde a um carácter de retorno de carro. |
\s | Corresponde a qualquer espaço em branco, incluindo espaço, tabulação, salto de página, etc. É equivalente a «[ \f\n\r\t\v]». |
\S | Corresponde a qualquer caractere de espaço não em branco. Equivale a «[^ \f\n\r\t\v]». |
\t | Corresponde a um carácter de tabulação. |
\v | Corresponde a um carácter de tabulação vertical. |
\w | Corresponde a qualquer carácter que represente uma palavra e inclua um sublinhado. É equivalente a «[A-Za-z0-9_]». |
\W | Corresponde a qualquer carácter que não represente uma palavra. É equivalente a «[^A-Za-z0-9_]». |
\num | Corresponde a num, em que num é um número inteiro positivo. Refere-se às correspondências guardadas. Por exemplo, «(.)\1» corresponde a dois caracteres idênticos consecutivos. |
|
Um elemento num modelo pode estar presente uma ou mais vezes. Vejamos alguns exemplos relacionados com o símbolo \d, que representa um algarismo:
modelo | significado |
\d | um algarismo |
\d? | 0 ou 1 dígito |
\d* | 0 ou mais dígitos |
\d+ | 1 ou mais dígitos |
\d{2} | 2 algarismos |
\d{3,} | pelo menos 3 algarismos |
\d{5,7} | entre 5 e 7 dígitos |
Imaginemos agora o modelo capaz de descrever o formato esperado para uma cadeia de caracteres:
cadeia procurada | modelo |
uma data no formato dd/mm/aa | \d{2}/\d{2}/\d{2} |
uma hora no formato hh:mm:ss | \d{2}:\d{2}:\d{2} |
um número inteiro sem sinal | \d+ |
uma sequência de espaços, que pode estar vazia | \s* |
um número inteiro sem sinal que pode ser precedido ou seguido de espaços | \s*\d+\s* |
um número inteiro que pode ser com sinal e precedido ou seguido de espaços | \s*[+|-]?\s*\d+\s* |
um número real sem sinal que pode ser precedido ou seguido de espaços | \s*\d+(.\d*)?\s* |
um número real que pode ser com sinal e precedido ou seguido de espaços | \s*[+|]?\s*\d+(.\d*)?\s* |
uma cadeia de caracteres que contenha a palavra «justo» | \bjusto\b |
É possível especificar onde se procura o padrão na cadeia:
padrão | significado |
^padrão | o padrão inicia a cadeia |
padrão$ | o modelo termina a cadeia |
^padrão$ | o modelo inicia e termina a cadeia |
padrão | o padrão é procurado em toda a cadeia, começando pelo início da mesma. |
cadeia procurada | padrão |
uma cadeia que termina com um ponto de exclamação | !$ |
uma cadeia que termina com um ponto | \.$ |
uma cadeia que começa com a sequência // | ^// |
uma cadeia que contém apenas uma palavra, eventualmente seguida ou precedida de espaços | ^\s*\w+\s*$ |
uma cadeia que contenha duas palavras, eventualmente seguidas ou precedidas de espaços | ^\s*\w+\s*\w+\s*$ |
uma cadeia que contenha a palavra «secret» | \bsecret\b |
Os subconjuntos de um padrão podem ser «recuperados». Assim, não só é possível verificar se uma cadeia corresponde a um padrão específico, como também é possível recuperar nessa cadeia os elementos correspondentes aos subconjuntos do padrão que foram colocados entre parênteses. Assim, se analisarmos uma cadeia que contenha uma data dd/mm/aa e, além disso, quisermos recuperar os elementos dd, mm, aa dessa data, utilizaremos o modelo (\d\d)/(\d\d)/(\d\d).
4.11.2. Verificar se uma cadeia corresponde a um modelo determinado
A classe Pattern permite verificar se uma cadeia corresponde a um modelo determinado. Para tal, utiliza-se o método estático
com: modèle: o modelo a verificar, chaine: a cadeia a comparar com o modelo. O resultado é o valor booleano true se a cadeia corresponder ao modelo, false caso contrário.
Eis um exemplo:
import java.io.*;
import java.util.regex.*;
// gestão de expressões regulares
public class regex1 {
public static void main(String[] args){
// uma expressão regular modelo
String modèle1="^\\s*\\d+\\s*$";
// comparar um exemplo com o modelo
String exemplaire1=" 123 ";
if (Pattern.matches(modèle1,exemplaire1)){
affiche("["+exemplaire1 + "] correspond au modèle ["+modèle1+"]");
}else{
affiche("["+exemplaire1 + "] ne correspond pas au modèle ["+modèle1+"]");
}//if
String exemplaire2=" 123a ";
if (Pattern.matches(modèle1,exemplaire2)){
affiche("["+exemplaire2 + "] correspond au modèle ["+modèle1+"]");
}else{
affiche("["+exemplaire2 + "] ne correspond pas au modèle ["+modèle1+"]");
}//if
}//main
public static void affiche(String msg){
System.out.println(msg);
}//exibe
}//classificar
e os resultados da execução:
Note-se que, no padrão "^\s*\d+\s*$", o carácter \ deve ser duplicado devido à interpretação específica que o Java faz deste carácter. Escreve-se, portanto: String padrão1="^\\s*\\d+\\s*$";
4.11.3. Encontrar todos os elementos de uma cadeia que correspondam a um padrão
Consideremos o padrão "\d+" e a cadeia " 123 456 789 ". Encontramos o padrão em três locais diferentes da cadeia. As classes Pattern e Matcher permitem recuperar as diferentes ocorrências de um padrão numa cadeia. A classe Pattern é a classe que gere as expressões regulares. Uma expressão regular utilizada mais do que uma vez tem de ser «compilada». Isto acelera as pesquisas do padrão nas cadeias de caracteres. O método estático compile realiza essa tarefa:
Recebe como parâmetro a cadeia do padrão e devolve um objeto Pattern. Para comparar o padrão de um objeto Pattern com uma cadeia de caracteres, utiliza-se a classe Matcher. Esta permite a comparação de um padrão com uma cadeia de caracteres. A partir de um objeto Pattern, é possível obter um objeto do tipo Matcher com o método matcher:
input é a cadeia de caracteres que deve ser comparada com o padrão.
Assim, para comparar o padrão «\d+» com a cadeia « 123 456 789 », é possível criar um objeto Matcher da seguinte forma:
A partir do objeto résultats anterior, será possível recuperar as diferentes ocorrências do padrão na cadeia. Para tal, utilizam-se os métodos suivantes da classe Matcher:
O método find procura na cadeia analisada a primeira ocorrência do padrão. Uma segunda chamada ao método find procurará a ocorrência seguinte. E assim sucessivamente. O método devolve true se encontrar o padrão; caso contrário, devolve false. A parte da cadeia correspondente à última ocorrência encontrada por find é obtida com o método group e a sua posição com o método start. Assim, se continuarmos com o exemplo anterior e quisermos apresentar todas as ocorrências do padrão «\d+» na cadeia « 123 456 789 », escreveremos:
while(résultats.find()){
System.out.println("séquence " + résultats.group() + " trouvée en position " + résultats.start());
}//while
O método reset permite reiniciar o objeto Matcher no início da cadeia comparada com o padrão. Assim, o método find voltará a encontrar a primeira ocorrência do padrão.
Eis um exemplo completo:
import java.io.*;
import java.util.regex.*;
// gestão de expressões regulares
public class regex2 {
public static void main(String[] args){
// várias ocorrências do padrão na instância
String modèle2="\\d+";
Pattern regex2=Pattern.compile(modèle2);
String exemplaire3=" 123 456 789";
// pesquisa de ocorrências do modelo no exemplar
Matcher matcher2=regex2.matcher(exemplaire3);
while(matcher2.find()){
affiche("séquence " + matcher2.group() + " trouvée en position " + matcher2.start());
}//enquanto
}//Main
public static void affiche(String msg){
System.out.println(msg);
}//exibe
}//classe
Resultados da execução:
Modèle=[\d+],exemplaire=[ 123 456 789 ]
Il y a 3 occurrences du modèle dans l'exemplaire
123 en position 2
456 en position 7
789 en position 12
4.11.4. Recuperar partes de um modelo
É possível «extrair» subconjuntos de um modelo. Assim, não só se pode verificar se uma cadeia de caracteres corresponde a um modelo específico, como também se pode extrair dessa cadeia os elementos correspondentes aos subconjuntos do modelo que foram colocados entre parênteses. Assim, se analisarmos uma cadeia de caracteres que contenha uma data dd/mm/aa e quisermos, além disso, extrair os elementos dd, mm e aa dessa data, utilizaremos o padrão (\d\d)/(\d\d)/(\d\d).
Vejamos o seguinte exemplo:
import java.io.*;
import java.util.regex.*;
// gestão de expressões regulares
public class regex3 {
public static void main(String[] args){
// captura de elementos no modelo
String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
Pattern regex3=Pattern.compile(modèle3);
String exemplaire4="Il est 18:05:49";
// verificação do modelo
Matcher résultat=regex3.matcher(exemplaire4);
if (résultat.find()){
// a instância corresponde ao modelo
affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
// são apresentados os grupos
for (int i=0;i<=résultat.groupCount();i++){
affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
}//for
}else{
// o exemplar não corresponde ao modelo
affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
}
}//Principal
public static void affiche(String msg){
System.out.println(msg);
}//exibe
}//classifica
A execução deste programa produz os seguintes resultados:
L'exemplaire [Il est 18:05:49] correspond au modèle [(\d\d):(\d\d):(\d\d)]
groupes[0]=[18:05:49] en position 7
groupes[1]=[18] en position 7
groupes[2]=[05] en position 10
groupes[3]=[49] en position 13
A novidade encontra-se na seguinte parte do código:
// verificação do modelo
Matcher résultat=regex3.matcher(exemplaire4);
if (résultat.find()){
// o exemplar corresponde ao modelo
affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
// são apresentados os grupos
for (int i=0;i<=résultat.groupCount();i++){
affiche("groupes["+i+"]=["+résultat.group(i)+"] en position "+résultat.start(i));
}//para
}else{
// o exemplar não corresponde ao modelo
affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
}
A cadeia exemplaire4 é comparada com o modelo regex3 através do método find. É então encontrada uma ocorrência do modelo regex3 na cadeia exemplaire4. Se o modelo incluir subconjuntos entre parênteses, estes estão disponíveis através de vários métodos da classe Matcher:
public int groupCount()
public String group(int group)
public int start(int group)
O método groupCount fornece o número de subconjuntos encontrados no modelo e o group(i) fornece o subconjunto n.º i. Este é encontrado na cadeia numa posição determinada pelo start(i). Assim, no exemplo:
L'exemplaire [Il est 18:05:49] correspond au modèle [(\d\d):(\d\d):(\d\d)]
groupes[0]=[18:05:49] en position 7
groupes[1]=[18] en position 7
groupes[2]=[05] en position 10
groupes[3]=[49] en position 13
A primeira chamada ao método find irá encontrar a cadeia 18:05:49 e criar automaticamente os três subconjuntos definidos pelos parênteses do modelo, respetivamente 18, 05 e 49.
4.11.5. Um programa de treino
Encontrar a expressão regular que nos permite verificar se uma cadeia corresponde a um determinado modelo é, por vezes, um verdadeiro desafio. O programa seguinte permite praticar. Solicita um modelo e uma cadeia e indica, em seguida, se a cadeia corresponde ou não ao modelo.
import java.io.*;
import java.util.regex.*;
// gestão de expressões regulares
public class regex4 {
public static void main(String[] args){
// dados
String modèle=null,chaine=null;
Pattern regex=null;
BufferedReader IN=null;
Matcher résultats=null;
int nbOccurrences=0;
// gestão de erros
try{
// solicita-se ao utilizador os modelos e os exemplares a comparar com este
while(true){
// fluxo de entrada
IN=new BufferedReader(new InputStreamReader(System.in));
// solicita-se o modelo
System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
modèle=IN.readLine();
// Concluído?
if(modèle.trim().toLowerCase().equals("fin")) break;
// cria-se a expressão regular
regex=Pattern.compile(modèle);
// solicita-se ao utilizador os exemplos a comparar com o modelo
while(true){
System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
chaine=IN.readLine();
// Concluído?
if(chaine.trim().toLowerCase().equals("fin")) break;
// criamos o objeto matcher
résultats=regex.matcher(chaine);
// procura-se as ocorrências do modelo
nbOccurrences=0;
while(résultats.find()){
// encontrou-se uma ocorrência
nbOccurrences++;
// exibe-se
System.out.println("J'ai trouvé la correspondance ["+résultats.group()
+"] en position "+résultats.start());
// exibição dos subelementos
if(résultats.groupCount()!=1){
for(int j=1;j<=résultats.groupCount();j++){
System.out.println("\tsous-élément ["+résultats.group(j)+"] en position "+
résultats.start(j));
}//para j
}//if
// cadeia seguinte
}//enquanto (résultats.find())
// foi encontrada pelo menos uma ocorrência?
if(nbOccurrences==0){
System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
}//if
// padrão seguinte
}//while(true)
}//while(true)
}catch(Exception ex){
// erro
System.err.println("Erreur : "+ex.getMessage());
// fim com erro
System.exit(1);
}//try-catch
// fim
System.exit(0);
}//Main
}//classe
Eis um exemplo de execução:
Tapez le modèle à tester ou fin pour arrêter :\d+
Tapez la chaîne à comparer au modèle [\d+] ou fin pour arrêter :123 456 789
J'ai trouvé la correspondance [123] en position 0
J'ai trouvé la correspondance [456] en position 4
J'ai trouvé la correspondance [789] en position 8
Tapez la chaîne à comparer au modèle [\d+] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :(\d\d):(\d\d)
Tapez la chaîne à comparer au modèle [(\d\d):(\d\d)] ou fin pour arrêter :14:15
abcd 17:18 xyzt
J'ai trouvé la correspondance [14:15] en position 0
sous-élément [14] en position 0
sous-élément [15] en position 3
J'ai trouvé la correspondance [17:18] en position 11
sous-élément [17] en position 11
sous-élément [18] en position 14
Tapez la chaîne à comparer au modèle [(\d\d):(\d\d)] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :^\s*\d+\s*$
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou fin pour arrêter : 1456
J'ai trouvé la correspondance [ 1456] en position 0
Tapez la chaîne à comparer au modèle [^\s*\d+\s*$] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :^\s*(\d+)\s*$
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :1456
J'ai trouvé la correspondance [1456] en position 0
sous-élément [1456] en position 0
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :abcd 1
456
Je n'ai pas trouvé de correspondances
Tapez la chaîne à comparer au modèle [^\s*(\d+)\s*$] ou fin pour arrêter :fin
Tapez le modèle à tester ou fin pour arrêter :fin
4.11.6. O método split da classe Pattern
Consideremos uma cadeia de caracteres composta por campos separados por uma cadeia separadora definida por uma expressão regular. Por exemplo, se os campos forem separados pelo caractere , precedido ou seguido por um número qualquer de espaços, a expressão regular que modela a cadeia separadora dos campos seria «\s*,\s*». O método split da classe Pattern permite-nos recuperar os campos numa matriz:
public String[] split(CharSequence input)
A cadeia input é decomposta em campos, estando estes separados por um separador que corresponde ao modelo do objeto Pattern atual. Para recuperar os campos de uma linha cujo separador de campos seja a vírgula precedida ou seguida por um número qualquer de espaços, escrever-se-á:
// uma linha
String ligne="abc ,, def , ghi";
// um modelo
Pattern modèle=Pattern.compile("\\s*,\\s*");
// decomposição de linha em campos
String[] champs=modèle.split(ligne);
É possível obter o mesmo resultado com o método split da classe String:
public String[] split(String regex)
Eis um programa de teste:
import java.io.*;
import java.util.regex.*;
// gestão de expressões regulares
public class split1 {
public static void main(String[] args){
// uma linha
String ligne="abc ,, def , ghi";
// um modelo
Pattern modèle=Pattern.compile("\\s*,\\s*");
// decomposição de linha em campos
String[] champs=modèle.split(ligne);
// visualização
for(int i=0;i<champs.length;i++){
System.out.println("champs["+i+"]=["+champs[i]+"]");
}//for
// outra forma de o fazer
champs=ligne.split("\\s*,\\s*");
// visualização
for(int i=0;i<champs.length;i++){
System.out.println("champs["+i+"]=["+champs[i]+"]");
}//para
}//Principal
}//classe
Resultados da execução:
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
champs[0]=[abc]
champs[1]=[]
champs[2]=[def]
champs[3]=[ghi]
4.12. Exercícios
4.12.1. Exercício 1
No Unix, os programas são frequentemente chamados da seguinte forma:
$ pg -o1 v1 v2 ... -o2 v3 v4 …
onde -oi representa uma opção e vi um valor associado a essa opção. Pretende-se criar uma classe «options» que permita analisar a cadeia de argumentos -o1 v1 v2 ... -o2 v3 v4 …, de modo a construir as seguintes entidades:
optionsValides | dicionário (Hashtable) cujas chaves são as opções oi válidas. O valor associado à chave oi é um vetor (Vector) cujos elementos são os valores v1 v2 … associados à opção -oi |
optionsInvalides | dicionário (Hashtable) cujas chaves são as opções oi inválidas. O valor associado à chave oi é um vetor (Vector) cujos elementos são os valores v1, v2… associados à opção -oi |
optionsSans | cadeia de caracteres (String) que apresenta a lista dos valores vi não associados a uma opção |
erreur | um número inteiro igual a 0 se não houver erros na linha de argumentos; caso contrário, outro valor: 1: existem parâmetros de chamada inválidos 2: existem opções inválidas 4: existem valores não associados a opções Se houver vários tipos de erros, estes valores são cumulativos. |
Um objeto «options» pode ser criado de 4 formas diferentes:
public options (String arguments, String optionsAcceptables)
arguments | a linha de argumentos -o1 v1 v2 ... -o2 v3 v4 … a analisar |
optionsAcceptables | a lista de opções «oi» aceitáveis |
Exemplo de chamada: options opt=new options("-u u1 u2 u3 -g g1 g2 -x","-u -g");
Aqui, os dois argumentos são cadeias de caracteres. Serão aceites os casos em que essas cadeias tenham sido divididas em palavras e colocadas numa matriz de cadeias de caracteres. Para tal, são necessários mais três construtores:
public options (String[] arguments, String optionsAcceptables)
public options (String arguments, String[] optionsAcceptables)
public options (String[] arguments, String[] optionsAcceptables)
A classe «options» apresentará a seguinte interface (acessores):
retorna a referência da tabela optionsValides criada aquando da criação do objeto «options»
retorna a referência à matriz optionsInvalides criada aquando da criação do objeto «options»
retorna a referência da cadeia optionsSans criada durante a criação do objeto «options»
retorna o valor do atributo «erro» criado durante a criação do objeto de opções
se não houver erros, apresenta os valores dos atributos optionsValides, optionsInvalides, optionsSans; caso contrário, apresenta o número do erro.
Eis um programa de exemplo:
import java.io.*;
//opções de importação;
public class test1{
public static void main (String[] arg){
// abertura do fluxo de entrada
String ligne;
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
affiche(e);
System.exit(1);
}
// leitura dos argumentos do construtor options(String, string)
String options=null;
String optionsAcceptables=null;
while(true){
System.out.print("Options : ");
try{
options=IN.readLine();
} catch (Exception e){
affiche(e);
System.exit(2);
}
if(options.length()==0) break;
System.out.print("Options acceptables: ");
try{
optionsAcceptables=IN.readLine();
} catch (Exception e){
affiche(e);
System.exit(2);
}
System.out.println(new options(options,optionsAcceptables));
}// fim do while
}//fim do main
public static void affiche(Exception e){
System.err.println("Erreur : "+e);
}
}//fim da classe
Alguns resultados:
C:\Serge\java\options>java test1
Options : 1 2 3 -a a1 a2 -b b1 -c c1 c2 c3 -b b2 b3
Options acceptables: -a -b
Erreur 6
Options valides :(-b,b1,b2,b3) (-a,a1,a2)
Options invalides : (-c,c1,c2,c3)
Sans options : 1 2 3
4.12.2. Exercício 2
Pretende-se criar uma classe «stringtovector» que permita transferir o conteúdo de um objeto String para um objeto Vector. Esta classe seria derivada da classe Vector:
e teria o seguinte código-fonte:
private void stringtovector(String S, String separateur, int[] tChampsVoulus,
boolean strict){
// cria um vetor com os campos da cadeia S
// esta é constituída por campos separados por separador
// se separador=nulo, a cadeia forma apenas um único campo
// só são desejados os campos cujos índices constam na tabela tChampsVoulus
// são os desejados. Os índices começam em 1
// se tChampsvoulus for nulo ou tiver tamanho nulo, são selecionados todos os campos
// se strict=verdadeiro, todos os campos desejados devem estar presentes
A classe teria o seguinte atributo privado:
Este atributo é definido pelo construtor anterior com os seguintes valores:
0: a construção decorreu sem problemas
4: faltam alguns campos obrigatórios, apesar de strict=true
A classe terá também dois métodos:
que devolve o valor do atributo privado erreur.
que exibe o valor do objeto na forma (erro, elemento 1, elemento 2, …) em que os elementos i são os elementos do vetor construído a partir da cadeia de caracteres.
Um programa de teste poderia ser o seguinte:
import java.io.*;
//import stringtovector;
public class essai2{
public static void main(String arg[]){
int[] T1={1,3};
System.out.println(new stringtovector("a : b : c :d:e",":",T1,true).identite());
int[] T2={1,3,7};
System.out.println(new stringtovector("a : b : c :d:e",":",T2,true).identite());
int [] T3={1,4,7};
System.out.println(new stringtovector("a : b : c :d:e",":",T3,false).identite());
System.out.println(new stringtovector("a : b : c :d:e","",T1,false).identite());
System.out.println(new stringtovector("a : b : c :d:e",null,T1,false).identite());
int[] T4={1};
System.out.println(new stringtovector("a : b : c :d:e","!",T4,true).identite());
int[] T5=null;
System.out.println(new stringtovector("a : b : c :d:e",":",T5,true).identite());
System.out.println(new stringtovector("a : b : c :d:e",null,T5,true).identite());
int[] T6=new int[0];
System.out.println(new stringtovector("a : b : c :d:e","",T6,true).identite());
int[] T7={1,3,4};
System.out.println(new stringtovector("a b c d e"," ",T6,true).identite());
}
}
Os resultados:
(0,a,c)
(4,a,c)
(0,a,d)
(0,a : b : c :d:e)
(0,a : b : c :d:e)
(0,a : b : c :d:e)
(0,a,b,c,d,e)
(0,a : b : c :d:e)
(0,a : b : c :d:e)
(0,a,b,c,d,e)
Algumas sugestões:
- Para dividir a cadeia S em campos, utilize o método split da classe String.
- Colocar os campos de S num dicionário D indexado pelo número do campo
- Recuperar do dicionário D apenas os campos cuja chave (índice) conste na tabela tChampsVoulus.
4.12.3. Exercício 3
Pretende-se adicionar à classe stringtovector o seguinte construtor:
public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
// cria um vetor com os campos da cadeia S
// esta é constituída por campos separados pelo separador
// se separador=nulo, a cadeia de caracteres forma apenas um único campo
// só são desejados os campos cujos índices se encontram em sChampsVoulus
// os índices começam em 1
// se sChampsvoulus for nulo ou "", são selecionados todos os campos
// se strict=verdadeiro, todos os campos pretendidos têm de estar presentes
A lista dos campos pretendidos encontra-se, portanto, numa cadeia de caracteres (String) em vez de um tabuleiro de inteiros (int[]). Ao atributo privado erreur da classe pode ser atribuído um novo valor:
2: a cadeia de índices dos campos pretendidos está incorreta
Eis um programa de exemplo:
import java.io.*;
//import stringtovector;
public class essai1{
public static void main(String arg[]){
String champs=null;
System.out.println(new stringtovector("a: b :c :d:e ",":","1 3",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","1 3 7",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","1 4 7",false).identite());
System.out.println(new stringtovector("a: b :c :d:e ","","1 3",false).identite());
System.out.println(new stringtovector("a: b :c :d:e ",null,"1 3",false).identite());
System.out.println(new stringtovector("a: b :c :d:e ","!","1",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":",champs,true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",null,champs,true).identite());
System.out.println(new stringtovector("a: b :c :d:e ","","",true).identite());
System.out.println(new stringtovector("a: b :c :d:e ",":","1 !",true).identite());
System.out.println(new stringtovector("a b c d e "," ","1 3",false).identite());
}
}
Alguns resultados:
(0,a,c)
(4,a,c)
(0,a,d)
(0,a: b :c :d:e)
(0,a: b :c :d:e)
(0,a: b :c :d:e)
(0,a,b,c,d,e)
(0,a,b,c,d,e)
(0,a: b :c :d:e)
(0,a: b :c :d:e)
(2)
(0,a,c)
Algumas sugestões:
- É necessário voltar ao caso do construtor anterior, transferindo os campos da cadeia sChampsVoulus para uma matriz de inteiros. Para tal, dividir sChampsVoulus em campos utilizando um objeto StringTokenizer, cujo atributo countTokens indicará o número de campos obtidos. Pode-se então criar uma matriz de inteiros com a dimensão adequada e preenchê-la com os campos obtidos.
- Para saber se um campo é um inteiro, utilize o método Integer.parseInt para converter o campo num inteiro e trate a exceção que será gerada caso essa conversão seja impossível.
4.12.4. Exercício 4
Pretende-se criar uma classe «filetovector» que permita transferir o conteúdo de um ficheiro de texto para um objeto Vector. Esta classe seria derivada da classe Vector:
e teria o seguinte construtor:
// --------------------- construtor
public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
// cria um vetor com as linhas do ficheiro de texto nomFichier
// as linhas são compostas por campos separados por separador
// se separador=nulo, a linha forma apenas um único campo
// só são desejados os campos cujos índices constam em tChampsVoulus
// os índices começam em 1
// se tChampsvoulus for nulo ou vazio, são selecionados todos os campos
// se strict=verdadeiro, todos os campos pretendidos devem estar presentes
// caso contrário, a linha não é guardada e o seu índice
// é colocado no vetor lignesErronees
// as linhas em branco são ignoradas
// assim como as linhas que começam por tagCommentaire, se tagCommentaire != null
A classe teria os seguintes atributos privados:
O atributo erreur é definido pelo construtor anterior com os seguintes valores:
0: a construção decorreu com sucesso
1: não foi possível abrir o ficheiro a processar
4: faltam alguns campos solicitados, apesar de strict=true
8: ocorreu um erro de E/S durante a processamento do ficheiro
O atributo lignesErronees é um vetor cujos elementos são os números das linhas com erros, na forma de cadeias de caracteres. Uma linha é considerada com erro se não conseguir fornecer os campos solicitados, quando strict=true.
A classe terá também dois métodos:
que devolve o valor do atributo privado erreur.
que apresenta o valor do objeto na forma (erro, elemento 1, elemento 2 …,(l1,l2,…)), em que os elementos i são os elementos do vetor construído a partir do ficheiro e li são os números das linhas com erros.
Eis um exemplo de teste:
import java.io.*;
//importar filetovector;
public class test2{
public static void main(String arg[]){
int[] T1={1,3};
System.out.println(new filetovector("data.txt",":",T1,false,"#").identite());
System.out.println(new filetovector("data.txt",":",T1,true,"#").identite());
System.out.println(new filetovector("data.txt","",T1,false,"#").identite());
System.out.println(new filetovector("data.txt",null,T1,false,"#").identite());
int[] T2=null;
System.out.println(new filetovector("data.txt",":",T2,false,"#").identite());
System.out.println(new filetovector("data.txt",":",T2,false,"").identite());
int[] T3=new int[0];
System.out.println(new filetovector("data.txt",":",T3,false,null).identite());
}
}
Resultados da execução:
[0,(0,a,c) (0,1,3) (0,azerty,cvf) (0,s)]
[4,(0,a,c) (0,1,3) (0,azerty,cvf),[5]]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# comentário) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# comentário) (0,azerty,1,cvf,fff,qqqq) (0,s)]
Algumas dicas
-
O ficheiro de texto é processado linha a linha. A linha é dividida em campos através da classe stringtovector, analisada anteriormente.
-
Os elementos do vetor criado a partir do ficheiro de texto são, portanto, objetos do tipo stringtovector.
-
O método identite de filetovector poderá recorrer ao método stringtovector.identite() para apresentar os seus elementos, bem como ao método Vector.toString() para apresentar os números das eventuais linhas com erros.
4.12.5. Exercício 5
Pretende-se adicionar à classe «filetovector» o seguinte construtor:
public filetovector(String nomFichier, String separateur, String sChampsVoulus,
boolean strict, String tagCommentaire){
// cria um vetor com as linhas do ficheiro de texto nomFichier
// as linhas são compostas por campos separados por separador
// se separador=null, a linha forma apenas um único campo
// só são desejados os campos cujos índices constam em tChampsVoulus
// os índices começam em 1
// se sChampsvoulus for nulo ou vazio, são selecionados todos os campos
// se strict=verdadeiro, todos os campos pretendidos devem estar presentes
// caso contrário, a linha não é memorizada e o seu índice
// é colocado no vetor lignesErronees
// as linhas em branco são ignoradas
// assim como as linhas que começam por tagCommentaire, se tagCommentaire != null
A lista de índices dos campos pretendidos encontra-se agora numa cadeia de caracteres (String), em vez de estar numa matriz de números inteiros.
O atributo privado erreur pode ter um valor adicional:
2: a cadeia de caracteres dos índices dos campos pretendidos está incorreta
Eis um exemplo de teste:
import java.io.*;
//import filetovector;
public class test1{
public static void main(String arg[]){
System.out.println(new filetovector("data.txt",":","1 3",false,"#").identite());
System.out.println(new filetovector("data.txt",":","1 3",true,"#").identite());
System.out.println(new filetovector("data.txt","","1 3",false,"#").identite());
System.out.println(new filetovector("data.txt",null," 1 3",false,"#").identite());
String S2=null;
System.out.println(new filetovector("data.txt",":",S2,false,"#").identite());
System.out.println(new filetovector("data.txt",":",S2,false,"").identite());
String S3="";
System.out.println(new filetovector("data.txt",":",S3,false,null).identite());
}
}
Os resultados:
[0,(0,a,c) (0,1,3) (0,azerty,cvf) (0,s)][4,(0,a,c) (0,1,3) (0,azerty,cvf),[5]]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a:b:c:d:e) (0,1 :2 : 3: 4: 5) (0,azerty : 1 : cvf : fff: qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# comentário) (0,azerty,1,cvf,fff,qqqq) (0,s)]
[0,(0,a,b,c,d,e) (0,1,2,3,4,5) (0,# comentário) (0,azerty,1,cvf,fff,qqqq) (0,s)]
Algumas dicas
- Vamos transformar a cadeia sChampsVoulus numa tabela de números inteiros tChampVoulus para voltarmos ao caso do construtor anterior.