Skip to content

4. Clases de uso habitual

En este capítulo presentamos una serie de clases de Java de uso habitual. Estas cuentan con numerosos atributos, métodos y constructores. En cada caso, solo presentamos una pequeña parte de las clases. Los detalles de las mismas están disponibles en la ayuda de Java que presentamos a continuación.

4.1. La documentación

Si ha instalado el JDK de Sun en la carpeta <jdk>, la documentación está disponible en la carpeta <jdk>\docs:

Image

A veces se dispone de un archivo jdk, pero sin documentación. Esta se puede encontrar en la página web de Sun: http://www.sun.com. En la carpeta docs hay un archivo index.html que es el punto de partida de la ayuda del JDK:

Image

Image

El enlace API & Language anterior da acceso a las clases de Java. El enlace Demos/Tutorials resulta especialmente útil para consultar ejemplos de programas en Java. Sigamos el enlace API & Language:

Image

Sigamos el enlace «Java 2 Platform» API:

Image

Esta página es el verdadero punto de partida de la documentación sobre las clases. Se puede crear un acceso directo a ella para acceder rápidamente. El URL se encuentra en <jdk>\docs\api\index.html. En ella se encuentran enlaces a los cientos de clases Java del JDK. Cuando se empieza, la principal dificultad es saber qué hacen estas diferentes clases. Por lo tanto, en un primer momento, esta ayuda solo resulta útil si se conoce el nombre de la clase sobre la que se desea obtener información. También se puede seguir la pista de los nombres de las clases, que normalmente indican la función de la clase.

Veamos un ejemplo y busquemos información sobre la clase Vector, que implementa una matriz dinámica. Basta con buscar en la lista de clases del panel de la izquierda el enlace de la clase Vector:

Image

y hacer clic en el enlace para ver la definición de la clase:

Image

Allí se encuentra

  • la jerarquía en la que se encuentra la clase, en este caso java.util.Vector
  • la lista de campos (atributos) de la clase
  • la lista de constructores
  • la lista de métodos

A continuación, presentamos diversas clases. Invitamos al lector a que compruebe sistemáticamente la definición completa de las clases utilizadas.

4.2. Las clases de prueba

Los ejemplos que siguen utilizan en ocasiones las clases personne y enseignant. A continuación recordamos su definición.

public class personne{
    // apellidos, nombre, edad
  private String prenom;
  private String nom;
  private int age;

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

   // constructor 2
  public personne(personne P){
    this.prenom=P.prenom;
    this.nom=P.nom;
    this.age=P.age;
  }

  // toString
  public String toString(){
    return "personne("+prenom+","+nom+","+age+")";
  }

  // accesorios
  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;
  }
}

La clase enseignant se deriva de la clase personne y se define de la siguiente manera:

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

    // constructor
  public enseignant(String P, String N, int age,int section){
    super(P,N,age);
    this.section=section;
  }
    // toString
  public String toString(){
    return "etudiant("+super.toString()+","+section+")";    
  }
}

También utilizaremos una clase etudiant derivada de la clase personne y definida de la siguiente manera:


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. La clase String

La clase String representa las cadenas de caracteres. Supongamos que nom es una variable de cadena de caracteres:

String nombre;

nom es una referencia a un objeto aún no inicializado. Se puede inicializar de dos maneras:

nombre = «caballo» o nombre = new String(«caballo»)

Ambos métodos son equivalentes. Si más adelante escribimos nombre=&quot;pez&quot;, nom pasará a hacer referencia a un nuevo objeto. El objeto anterior, *String(&quot;cheval&quot;),* se pierde y se recuperará el espacio de memoria que ocupaba.

La clase String cuenta con numerosos atributos y métodos. Estos son algunos de ellos:

public char charAt(int i)
devuelve el carácter i de la cadena, siendo el primer carácter el que tiene el índice 0. Así, String("caballo").charAt(3) es igual a 'v'
public int compareTo(cadena2)
chaine1.compareTo(cadena2) compara cadena1 con cadena2 y devuelve 0 si cadena1 = cadena2, 1 si cadena1 > cadena2 y -1 si cadena1 < cadena2
public boolean equals(Object anObject)
chaine1.equals(cadena2) devuelve «verdadero» si cadena1 = cadena2, «falso» en caso contrario
public String toLowerCase()
chaine1.toLowerCase() convierte cadena1 a minúsculas
public String toUpperCase()
chaine1.toUpperCase() convierte cadena1 a mayúsculas
public String trim()
chaine1.trim() elimina los espacios iniciales y finales de cadena1
public String substring(int beginIndex, int endIndex)
String("chapeau").subString(2,4) devuelve la cadena «ape»
public char[] toCharArray()
permite colocar los caracteres de la cadena en una matriz de caracteres
int length()
número de caracteres de la cadena
int indexOf(String cadena2)
devuelve la primera posición de chaine2 en la cadena actual o -1 si chaine2 no está presente
int indexOf(String cadena2, int startIndex)
devuelve la primera posición de chaine2 en la cadena actual o -1 si chaine2 no está presente. La búsqueda comienza a partir del carácter n.º startIndex.
int lastIndexOf(String cadena2)
devuelve la última posición de chaine2 en la cadena actual o -1 si chaine2 no está presente
boolean startsWith(String cadena2)
devuelve «verdadero» si la cadena actual comienza por chaine2
boolean endsWith(String cadena2)
devuelve «verdadero» si la cadena actual termina en chaine2
boolean matches(String regex)
devuelve «verdadero» si la cadena actual coincide con la expresión regular regex.
String[] split(String regex)
La cadena actual está compuesta por campos separados por una cadena de caracteres modelada por la expresión regular regex. El método split permite recuperar los campos en un array.
String replace(char oldChar, char newChar)
sustituye en la cadena actual el carácter oldChar por el carácter newChar.

A continuación se muestra un programa de ejemplo:

// importaciones
import java.io.*;

public class string1{
    // una clase de demostración
    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

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

y los resultados obtenidos:

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. La clase Vector

Un vector es un array dinámico cuyos elementos son referencias a objetos. Se trata, por tanto, de un array de objetos cuyo tamaño puede variar con el tiempo, algo que no es posible con los arrays estáticos que hemos visto hasta ahora. A continuación se muestran algunos campos, constructores y métodos de esta clase:

public Vector()
crea un vector vacío
public final int size()
número de elementos del vector
public final void addElement(Object obj)
añade el objeto al que hace referencia obj al vector
public final Object elementAt(int índice)
referencia al objeto n.º index del vector; los índices comienzan en 0
public final Enumeration elements()
el conjunto de elementos del vector en forma de enumeración
public final Object firstElement()
referencia al primer elemento del vector
public final Object lastElement()
referencia al último elemento del vector
public final boolean isEmpty()
devuelve «true» si el vector está vacío
public final void removeElementAt(int índice)
elimina el elemento con índice index
public final void removeAllElements()
vacía el vector de todos sus elementos
public final String toString()
devuelve una cadena de identificación del vector

Aquí tienes un programa de prueba:


// las clases importadas
import java.util.*;

public class test1{

// el programa principal main - estático - método de clase

  public static void main(String arg[]){

// creación de objetos (instancias de clases)
    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());

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

// un vector
    Vector V=new Vector();
    V.addElement(p);V.addElement(en);V.addElement(et);
    System.out.println("Taille du vecteur V = "+V.size());
    for(int i=0;i<V.size();i++){
      p2=(personne) V.elementAt(i);
      System.out.println("V["+i+"]="+p2.toString());
    }
} // fin de main
}// fin de la clase

Compilemos 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

Ejecutemos el archivo 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)

A partir de ahora, ya no repetiremos el proceso de compilación y ejecución de los programas de prueba. Basta con seguir los pasos descritos anteriormente.

4.5. La clase ArrayList

La clase ArrayList es análoga a la clase Vector. Solo difiere de ella de manera significativa cuando es utilizada simultáneamente por varios hilos de ejecución. Los métodos de sincronización de subprocesos para acceder a un Vector o a un ArrayList son diferentes. Aparte de este caso, se puede utilizar indistintamente uno u otro. A continuación se muestran algunos campos, constructores o métodos de esta clase:

ArrayList()
crea una lista vacía
int size()
número de elementos de la lista
void add(Object obj)
añade el objeto al que hace referencia obj a la lista
void add(int índice, Object obj)
añade el objeto al que hace referencia obj a la matriz en la posición index
Object get(int index)
referencia del objeto n.º index de la tabla —los índices comienzan en 0
boolean isEmpty()
devuelve «verdadero» si la matriz está vacía
void remove(int index)
elimina el elemento con índice index
void clear()
vacía el array de todos sus elementos
Object[] toArray()
convierte el array dinámico en un array clásico
String toString()
devuelve una cadena de identificación del array

Aquí tienes un programa de prueba:

// las clases importadas
import java.util.*;

public class test1{

// el programa principal main - estático - método de clase

  public static void main(String arg[]){

// creación de objetos (instancias de clases)
    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);

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

// un vector
    ArrayList personnes=new ArrayList();
    personnes.add(p);personnes.add(en);personnes.add(et);
    System.out.println("Nombre de personnes = "+personnes.size());
    for(int i=0;i<personnes.size();i++){
      p2=(personne) personnes.get(i);
      System.out.println("personnes["+i+"]="+p2);
    }
} // fin de main
}// fin de la clase

Los resultados obtenidos son los mismos que los anteriores.

4.6. La clase Arrays

La clase java.util.Arrays permite acceder a métodos estáticos que facilitan diversas operaciones con matrices, en particular la ordenación y la búsqueda de elementos. A continuación se muestran algunos de estos métodos:

static void sort(matriz)
ordena tableau utilizando para ello el orden implícito del tipo de datos del array, ya sean números o cadenas de caracteres.
static void sort (Object[] matriz, Comparator C)
ordena tableau utilizando la función de comparación C para comparar los elementos
static int binarySearch(matriz, elemento)
devuelve la posición de élément en tableau o un valor <0 en caso contrario. La matriz debe estar ordenada previamente.
static int binarySearch(Object[] matriz, Object elemento, Comparator C)
Lo mismo, pero utiliza la función de comparación C para comparar dos elementos de la matriz.

He aquí un primer ejemplo:

import java.util.*;

public class sort2 implements Comparator{

   // una clase privada interna
  private class personne{
    private String nom;
    private int age;
    public personne(String nom, int age){
      this.nom=nom; // nombre de la persona
      this.age=age; // su edad
    }
     // obtener la edad
    public int getAge(){
      return age;
    }
     // identidad de la persona
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // clase «persona»

   // fabricante
  public sort2() {
     // una tabla de personas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // ordenación de la tabla de personas
    Arrays.sort(amis,this);
     // verificación
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//creador

   // la función que compara personas
  public int compare(Object o1, Object o2){
    // debe devolver
     // -1 si o1 es «menor que» o2
     // 0 si o1 «es igual a» o2
     // +1 si o1 es «mayor 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

    // función de prueba
  public static void main(String[] arg){
    new sort2();
  }//mano

}//clase

Analicemos este programa. La función main crea un objeto *sort2*. El constructor de la clase *sort2* es el siguiente:

   // constructor
  public sort2() {
     // una tabla de personas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // ordenación de la tabla de personas
    Arrays.sort(amis,this);
     // verificación
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//creador

La matriz que se va a ordenar es una matriz de objetos personne. La clase personne está definida de forma privada (private) dentro de la clase sort2. El método estático sort de la clase Arrays no sabe cómo ordenar un array de objetos personne, por lo que aquí nos vemos obligados a utilizar la forma void sort(Object[] obj, Comparator C). Comparator es una interfaz que solo define un método:

    int compare(Object o1, Object o2)

y que debe devolver 0: si o1 = o2, -1: si o1 < o2, +1: si o1 > o2. En el prototipo void sort(Object[] obj, Comparator C), el segundo argumento C debe ser un objeto que implemente la interfaz Comparator. En el constructor sort2, se ha elegido el objeto actual this:

     // ordenación de la tabla de personas
    Arrays.sort(amis,this);

Esto nos obliga a hacer dos cosas:

  1. indicar que la clase sort2 implementa la interfaz Comparator
public class sort2 implements Comparator{
  1. Escribir la función compare en la clase sort2.

Esta es la siguiente:

   // la función que compara personas
  public int compare(Object o1, Object o2){
    // debe devolver
     // -1 si o1 es «menor que» o2
     // 0 si o1 es «igual  o2
     // +1 si o1 es «mayor 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 dos objetos personne, aquí se utiliza la edad (también se podría haber utilizado el nombre).

Los resultados de la ejecución son los siguientes:

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

Se podría haber procedido de otra forma para implementar la interfaz Comparator:

import java.util.*;

public class sort2 {

  // una clase privada interna
  private class personne{
…….
  }; // clase persona

   // constructor
  public sort2() {
     // un array de personas
    personne[] amis=new personne[]{new personne("tintin",100),new personne("milou",80),
        new personne("tournesol",40)};
     // ordenación del array de personas
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//compara
      }//clase
    );
     // verificación
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
  }//generador

   // la función que compara personas
  public int compare1(Object o1, Object o2){
    // debe devolver
     // -1 si o1 es «menor que» o2
     // 0 si o1 «es igual a» o2
     // +1 si o1 es «mayor 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
}//clase

La instrucción de ordenación ha quedado así:

     // ordenación de la tabla de personas
    Arrays.sort(amis,
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare1(o1,o2);
        }//comparar
      }//clase
    );

El segundo parámetro del método sort debe ser un objeto que implemente la interfaz Comparator. Aquí creamos dicho objeto mediante new java.util.Comparator(), y el texto que sigue a {…} define la clase de la que se crea un objeto. A esto se le denomina clase anónima, ya que no tiene nombre. En esta clase anónima, que debe implementar la interfaz Comparator, se define el método compare de dicha interfaz. Este método se limita a llamar al método compare1 de la clase sort2. Así pues, volvemos al caso anterior.

La clase sort2 ya no implementa la interfaz Comparator. Por lo tanto, su declaración queda así:

public class sort2 {

Ahora probamos el método binarySearch de la clase Arrays con el siguiente ejemplo:

import java.util.*;

public class sort4 {

  // una clase privada interna
  private class personne{
      // atributos
    private String nom;
    private int age;

        // constructor
    public personne(String nom, int age){
      this.nom=nom; // nombre de la persona
      this.age=age; // su edad
    }

     // obtener el nombre
    public String getNom(){
      return nom;
    }

     // obtener la edad
    public int getAge(){
      return age;
    }
     // identidad de la persona
    public String toString(){
      return ("["+nom+","+age+"]");
    }
  }; // clase de persona

   // creador
  public sort4() {
     // una tabla de personas
    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
      }//clase
      ;
    java.util.Comparator comparateur2=
        new java.util.Comparator(){
          public int compare(Object o1, Object o2){
            return compare2(o1,o2);
        }//compara
      }//clasifica
      ;          

     // ordenación de la tabla de personas
    Arrays.sort(amis,comparateur1);
    // verificación
    for(int i=0;i<3;i++)
      System.out.println(amis[i]);
    // búsquedas
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);
  }//generador

   // la función que compara personas
  public int compare1(Object o1, Object o2){
    // debe devolver
     // -1 si o1 es «menor que» o2
     // 0 si o1 «es igual a» o2
     // +1 si o1 es «mayor 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

   // la función que compara a una persona con un nombre
  public int compare2(Object o1, Object o2){
    // o1 es una persona
     // o2 es una cadena, el nombre «nombre2» de una persona
     // debe devolver
     // -1 si o1.nom es «menor que» nom2
     // 0 si o1.nom es «igual a» nom2
     // +1 si o1.nom es «mayor que» nombre2
    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){
       // busca a un amigo en la tabla 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");
  }//busca

  // mano
  public static void main(String[] arg){
    new sort4();
  }//mano
}//clase

En este caso, hemos procedido de forma ligeramente diferente a los ejemplos anteriores. Se han creado los dos objetos Comparator necesarios para los métodos sort y binarySearch, y se han asignado a las variables comparateur1 y comparateur2.

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

En el generador de sort4 se realiza dos veces una búsqueda dicotómica en la tabla amis:

     // búsquedas
    cherche("milou",amis,comparateur2);
    cherche("xx",amis,comparateur2);

El método cherche recibe todos los parámetros que necesita para llamar al método binarySearch:

    public void cherche(String ami,personne[] amis, Comparator comparateur){
       // buscar amigo en la tabla 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");
  }//busca

El método binarySearch funciona con el comparador comparateur2, que a su vez recurre al método compare2 de la clase sort4. El método rend devuelve la posición del nombre buscado en la tabla si existe, o un número <0 en caso contrario. El método compare2 sirve para comparar un objeto personne con un nombre de tipo String.

   // la función que compara a una persona con un nombre
  public int compare2(Object o1, Object o2){
    // o1 es una persona
     // o2 es una cadena, el nombre «nombre2» de una persona
     // debe devolver
     // -1 si o1.nom «es menor que» nombre2
     // 0 si o1.nom «es igual a» nombre2
     // +1 si o1.nom «mayor que» nombre2
    personne p1=(personne)o1;
    String nom1=p1.getNom();
    String nom2=(String)o2;
    return nom1.compareTo(nom2);
  }//compara2

A diferencia del método sort, el método binarySearch no recibe dos objetos personne, sino un objeto personne y un objeto String en ese orden. El primer parámetro es un elemento de la matriz amis; el segundo, el nombre de la persona buscada.

4.7. La clase Enumeration

Enumeration es una interfaz y no una clase. Tiene los siguientes métodos:

public abstract boolean hasMoreElements()
devuelve «true» si la enumeración aún tiene elementos
public abstract Object nextElement()
devuelve la referencia al siguiente elemento de la enumeración

¿Cómo se utiliza una enumeración? Por lo general, así:

    Enumeration e=…                // se obtiene un objeto de enumeración
    while(e.hasMoreElements()){
        // se procesa el elemento e.nextElement()
    }

He aquí un ejemplo:


// las clases importadas
import java.util.*;

public class test1{

// el programa principal main - estático - método de clase

  public static void main(String arg[]){

// creación de objetos (instancias de clases)
    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());

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

// un vector
    Vector V=new Vector();
    V.addElement(p);V.addElement(en);V.addElement(et);
    System.out.println("Taille du vecteur V = "+V.size());
    int i;
    for(i=0;i<V.size();i++){
      p2=(personne) V.elementAt(i);
      System.out.println("V["+i+"]="+p2.toString());
    }

// una enumeración
    Enumeration E=V.elements();
    i=0;
    while(E.hasMoreElements()){
      p2=(personne) E.nextElement();
      System.out.println("V["+i+"]="+p2.toString());
      i++;
    }
   }// fin de main
}//fin de clase

Se obtienen los siguientes 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. La clase Hashtable

La clase Hashtable permite implementar un diccionario. Podemos considerar un diccionario como una tabla de dos columnas:

clave
valor
clave1
valor1
clave2
valor2
..
...

Las claves son únicas, c.a.d, por lo que no puede haber dos claves idénticas. Los principales métodos y propiedades de la clase Hashtable son los siguientes:

public Hashtable()
constructor: crea un diccionario vacío
public int size()
número de elementos del diccionario; un elemento es un par (clave, valor)
public Object put(Object key, Object value)
añade el par (clave, valor) al diccionario
public Object get(Object key)
recupera el objeto asociado a la clave key o null si la clave key no existe
public boolean containsKey(Object key)
verdadero si la clave key existe en el diccionario
public boolean contains(Object value)
verdadero si el valor value existe en el diccionario
public Enumeration keys()
devuelve las claves del diccionario en forma de enumeración
public Object remove(Object key)
elimina el par (clave, valor) donde clave = key
public String toString()
identifica el diccionario

He aquí un ejemplo:


// las clases importadas
import java.util.*;

public class test1{

// el programa principal main - estático - método de clase

  public static void main(String arg[]){

// creación de objetos (instancias de clases)
    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());

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

// un diccionario
    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++;
    }
  }//fin de main
}//fin de clase

Los resultados obtenidos son los siguientes:

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. Los archivos de texto

4.9.1. Escribir

Para escribir en un archivo, es necesario disponer de un flujo de escritura. Para ello, se puede utilizar la clase FileWriter. Los constructores que se utilizan con más frecuencia son los siguientes:

FileWriter(String fileName)
crea el archivo con el nombre fileName; a continuación, se puede escribir en él; si ya existe un archivo con el mismo nombre, se sobrescribe
FileWriter(String fileName,
boolean append)
lo mismo; si ya existe un archivo con el mismo nombre, se puede utilizar abriéndolo en modo de adición (append=true)

La clase FileWriter ofrece varios métodos para escribir en un archivo, métodos heredados de la clase Writer. Para escribir en un archivo de texto, es preferible utilizar la clase PrintWriter, cuyos constructores más utilizados son los siguientes:

PrintWriter(Writer out)
El argumento es de tipo Writer o c.a.d. Un flujo de escritura (en un archivo, en la red, etc.).
PrintWriter(Writer out, boolean autoflush)
Lo mismo. El segundo argumento gestiona el almacenamiento en búfer de las líneas. Cuando es falso (su valor por defecto), las líneas escritas en el archivo pasan por un búfer en memoria. Cuando este se llena, se escribe en el archivo. Esto mejora el acceso al disco. Dicho esto, a veces este comportamiento no es deseable, especialmente cuando se escribe en la red.

Los métodos útiles de la clase PrintWriter son los siguientes:

void print(Type T)
escribe el dato T (String, int, …)
void println(Type T)
lo mismo, pero terminando con un carácter de fin de línea
void flush()
vacía el búfer si no se está en modo autoflush
void close()
cierra el flujo de escritura

Aquí tienes un programa que escribe unas cuantas líneas en un archivo de texto:

// importaciones
import java.io.*;

public class ecrire{
  public static void main(String[] arg){
    // apertura del archivo
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // escritura en el archivo
    try{
      fic.println("Jean,Dupont,27");
      fic.println("Pauline,Garcia,24");
      fic.println("Gilles,Dumond,56");
    } catch (Exception e){
      Erreur(e,3);
    }
     // cierre del archivo
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// fin de main

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

El archivo out obtenido tras la ejecución es el siguiente:

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

4.9.2. Leer

Para leer el contenido de un archivo, es necesario disponer de un flujo de lectura asociado al archivo. Para ello, se puede utilizar la clase FileReader y el siguiente constructor:

FileReader(String nombreArchivo)
abre un flujo de lectura a partir del archivo indicado. Lanza una excepción si la operación falla.

La clase FileReader dispone de varios métodos para leer un archivo, métodos heredados de la clase Reader. Para leer líneas de texto de un archivo de texto, es preferible utilizar la clase BufferedReader con el siguiente constructor:

BufferedReader(Reader in)
abre un flujo de lectura con búfer a partir de un flujo de entrada in. Este flujo de tipo Reader puede proceder del teclado, de un archivo, de la red, etc.

Los métodos útiles de la clase BufferedReader son los siguientes:

int read()
lee un carácter
String readLine()
lee una línea de texto
int read(char[] buffer, int offset, int tamaño)
lee taille caracteres del archivo y los coloca en el array buffer a partir de la posición offset.
void close()
cierra el flujo de lectura

A continuación se muestra un programa que lee el contenido del archivo creado anteriormente:

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

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

    // gestión de posibles errores
    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));
      }// fin del bucle «while»
    } catch (Exception e){
      Erreur(e,2);
    }

     // cierre del archivo
    try{
      IN.close();
    } catch (Exception e){
      Erreur(e,3);
    }
  }// fin de «main»

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

}// fin de clase

Al ejecutar el programa se obtienen los siguientes resultados:

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

4.9.3. Guardar un objeto «persona»

Aplicamos lo que acabamos de ver para dotar a la clase personne de un método que permita guardar en un archivo los atributos de una persona. Añadimos el método sauveAttributs a la definición de la clase personne:


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

Antes de definir la clase personne, no hay que olvidar importar el paquete java.io:

import java.io.*;

El método sauveAttributs recibe como único parámetro el flujo PrintWriter en el que debe escribir. Un programa de prueba podría ser el siguiente:

// importaciones
import java.io.*;
// importar persona;

public class sauver{
  public static void main(String[] arg){
    // apertura del archivo
    PrintWriter fic=null;
    try{
      fic=new PrintWriter(new FileWriter("out"));
    } catch (Exception e){
      Erreur(e,1);
    }
     // escritura en el archivo
    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);
    }
     // cierre del archivo
    try{
      fic.close();
    } catch (Exception e){
      Erreur(e,2);
    }
  }// fin de main

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

Compilemos y ejecutemos 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. Los archivos binarios

4.10.1. La clase RandomAccessFile

La clase RandomAccessFile permite gestionar archivos binarios, en particular aquellos con estructura fija, como los que se conocen en los lenguajes C/C++. A continuación se muestran algunos métodos y constructores útiles:

RandomAccessFile(String nombreArchivo, String modo)
constructor: abre el archivo indicado en el modo especificado. Este modo puede adoptar los siguientes valores:
r: apertura en lectura
rw: apertura en lectura y escritura
void writeTTT(TTT valor)
escribe valor en el archivo. TTT representa el tipo de valor. La representación en memoria de valor se escribe tal cual en el archivo. Así, encontramos writeBoolean, writeByte, writeInt, writeDouble, writeLong, writeFloat, ... Para escribir una cadena, se utiliza writeBytes(String cadena).
TTT readTTT()
lee y devuelve un valor de tipo TTT. Así, encontramos readBoolean, readByte, readInt, readDouble, readLong, readFloat, ... El método read() lee un byte.
long length()
tamaño del archivo en bytes
long getFilePointer()
posición actual del puntero de archivo
void seek(long pos)
sitúa el cursor del archivo en el byte pos

4.10.2. La clase «article»

Todos los ejemplos que siguen utilizarán la siguiente clase article:

     // la estructura del artículo
    private static class article{
         // se define la estructura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//clase de artículo

La clase Java article anterior será equivalente a la siguiente estructura article en C

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

De este modo, limitaremos el código a 4 caracteres y el nombre a 20.

4.10.3. Escribir un registro

El siguiente programa escribe un artículo en un archivo llamado «data»:

// clases importadas
import java.io.*;

public class test1{

// comprueba la escritura de una estructura (en el sentido del lenguaje C) en un archivo binario

     // la estructura del artículo
    private static class article{
         // se define la estructura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//clase «artículo»

    public static void main(String arg[]){

         // se define el archivo binario en el que se almacenarán los artículos
        RandomAccessFile fic=null;

         // se define un artículo
        article art=new article();
        art.code="a100";
        art.nom="velo";
        art.prix=1000.80;
        art.stockActuel=100;
        art.stockMinimum=10;

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

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

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

   // método de escritura
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // código
      fic.writeBytes(art.code);
       // el nombre 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);
       // el precio
      fic.writeDouble(art.prix);
       // las existencias
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// fin de escritura

   // ------------------------error
  public static void erreur(String msg, int exitCode){
          System.err.println(msg);
          System.exit(exitCode);
  }// fin del error
}// fin de clase

El siguiente programa nos permite comprobar que la ejecución se ha realizado correctamente.

4.10.4. Leer un registro

// clases importadas
import java.io.*;

public class test2{

// comprueba la escritura de una estructura (en el sentido de C) en un archivo binario

     // la estructura «article»
    private static class article{
         // se define la estructura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//clase «artículo»

    public static void main(String arg[]){

         // se define el archivo binario en el que se almacenarán los artículos
        RandomAccessFile fic=null;

       // se abre el archivo en modo de lectura
      try{
          fic=new RandomAccessFile("data","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data",1);
      }//try-catch

       // se lee el único artículo del archivo
    article art=new article();
      try{
          lire(fic,art);
      } catch (IOException E){
          erreur("Erreur lors de la lecture de l'enregistrement",2);
      }//try-catch

       // se muestra el registro leído
      affiche(art);

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

    // método de lectura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // lectura del código
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nombre
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // precio
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// fin de escritura

     // ---------------------visualizar
    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);
    }// fin de visualización

     // ------------------------error
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fin del error
}// fin de clase

Los resultados de la ejecución son los siguientes:

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

Se recupera correctamente el registro que había sido escrito por el programa de escritura.

4.10.5. Conversión de texto a binario

El siguiente programa es una ampliación del programa de escritura de un registro. Ahora escribimos varios registros en un archivo binario llamado data.bin. Los datos se extraen del siguiente archivo 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
// clases importadas
import java.io.*;
import java.util.*;

public class test3{

// archivo de texto --> archivo binario

     // la estructura del artículo
    private static class article{
         // se define la estructura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//clase «artículo»

    public static void main(String arg[]){

         // se define el archivo binario en el que se almacenarán los artículos
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","rw");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin",1);
        }

         // los datos se extraen de un archivo 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);
        }

       // archivo .txt --> archivo .bin
      String ligne=null;
      String[] champs=null;
      int numLigne=0;
      String champ=null;
    article art=new article();    // artículo que se va a crear
      try{
          while((ligne=dataTxt.readLine())!=null){
          // una línea de +
              numLigne++;
         // desglose en campos
        champs=ligne.split(":");
         // se necesitan 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);
         // apellidos, nombre
              art.nom=champs[1];
         // precio
              try{
                  art.prix=Double.parseDouble(champs[2]);
              } catch (Exception E){
                  erreur("Prix erroné en ligne "+numLigne+" du fichier data.txt",4);
              }
        // stock actual
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }
        // stock actual
              try{
                  art.stockActuel=Integer.parseInt(champs[3]);
              } catch (Exception E){
                  erreur("Stock actuel erroné en ligne "+ numLigne + " du fichier data.txt",5);
              }             
               // se escribe el registro
              try{
                  ecrire(dataBin,art);
              } catch (IOException E){
                  erreur("Erreur lors de l'écriture de l'enregistrement "+numLigne,7);
              }
               // se pasa a la línea siguiente
          }// fin del bucle «while»
      } catch (IOException E){
          erreur("Erreur lors de la lecture du fichier data.txt après la ligne "+numLigne,8);
      }        
        // se ha 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);
      }
  }// fin de main

   // método de escritura
  public static void ecrire(RandomAccessFile fic, article art) throws IOException{
        // código
      fic.writeBytes(art.code);
       // el nombre 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);
       // el precio
      fic.writeDouble(art.prix);
       // las existencias
      fic.writeInt(art.stockActuel);
      fic.writeInt(art.stockMinimum);
  }// fin de escritura

     // ------------------------error
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fin del error

}// fin de clase

El siguiente programa permite comprobar que este ha funcionado correctamente.

4.10.6. Conversión de binario a texto

El siguiente programa lee el contenido del archivo binario data.bin creado anteriormente y guarda su contenido en el archivo de texto data.text. Si todo va bien, el archivo data.text debería ser idéntico al archivo original data.txt.

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

public class test5{

// archivo de texto --> archivo binario

     // la estructura del artículo
    private static class article{
         // se define la estructura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//clase «artículo»

     // función principal
    public static void main(String arg[]){

         // se define el archivo binario en el que se almacenarán los artículos
        RandomAccessFile dataBin=null;
        try{
                dataBin=new RandomAccessFile("data.bin","r");
        } catch (Exception E){
                erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
        }

         // los datos se escriben en un archivo 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);
        }

      // archivo .bin --> archivo .text
    article art=new article();    // artículo que se va a crear

       // se procesa el archivo binario
      int numRecord=0;
      long l=0;    // tamaño del archivo
      try{
          l=dataBin.length();
      } catch (IOException e){
          erreur("Erreur lors du calcul de la longueur du fichier data.bin",2);
      }
      long pos=0;    // posición actual en el archivo
      try{
          pos=dataBin.getFilePointer();
      } catch (IOException e){
          erreur("Erreur lors de la lecture de la position courante dans data.bin",2);
      }

     // mientras no se haya superado el final del archivo
        while(pos<l){
            // leer el registro actual y procesarlo
            numRecord++;
            try{
                lire(dataBin,art);
            } catch (Exception e){
                erreur("Erreur lors de la lecture de l'enregistrement "+numRecord,2);
            }
            affiche(art);

             // escribir la línea de texto correspondiente en 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);
            }
        }// fin del bucle «while»

       // ya está
      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);
      }

  }// fin de main

     // método de lectura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
      // lectura de código
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nombre
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // precio
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// fin de escritura

     // ---------------------visualización
    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);
    }// fin de visualización

     // ------------------------error
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fin del error
}// fin de clase

A continuación se muestra un ejemplo de ejecución:

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. Acceso directo a los registros

Este último programa ilustra la posibilidad de acceder directamente a los registros de un archivo binario. Muestra el registro del archivo data.bin, cuyo número se le pasa como parámetro, siendo el primer registro el que lleva el número 1.

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

public class test6{

// archivo de texto --> archivo binario

     // la estructura del artículo
    private static class article{
         // se define la estructura
        public String code;
        public String nom;
        public double prix;
        public int stockActuel;
        public int stockMinimum;
  }//clase «artículo»

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

       // se comprueban los argumentos
      int nbArguments=args.length;
      String syntaxe="syntaxe : pg numéro_de_fiche";
      if(nbArguments!=1)
          erreur(syntaxe,20);
     // comprobación del n.º de ficha
      int numRecord=0;
      try{
          numRecord=Integer.parseInt(args[0]);
      } catch(Exception e){
          erreur(syntaxe+"\nNuméro de fiche incorrect",21);
      }

       // se abre el archivo binario en modo de lectura
    RandomAccessFile dataBin=null;
      try{
          dataBin=new RandomAccessFile("data.bin","r");
      } catch (Exception E){
          erreur("Impossible d'ouvrir le fichier data.bin en lecture",1);
      }

       // se selecciona la ficha deseada
      try{
          dataBin.seek((numRecord-1)*40);
      } catch (Exception e){
          erreur("La fiche "+numRecord+" n'existe pas",23);
      }

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

       // se muestra
      affiche(art);

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

  }// fin de main

    // método de lectura
    public static void lire(RandomAccessFile fic, article art) throws IOException{
       // código de lectura
        art.code="";
        for(int i=0;i<4;i++) art.code+=(char)fic.readByte();
    // nombre
        art.nom="";
        for(int i=0;i<20;i++) art.nom+=(char)fic.readByte();
        art.nom=art.nom.trim();
    // precio
        art.prix=fic.readDouble();
    // stocks
        art.stockActuel=fic.readInt();
        art.stockMinimum=fic.readInt();
    }// fin de escritura

     // ---------------------visualización
    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);
    }// fin de visualización

     // ------------------------error
    public static void erreur(String msg, int exitCode){
                System.err.println(msg);
                System.exit(exitCode);
    }// fin de error
}// fin de clase

Estos son algunos ejemplos de ejecución:

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 expresiones regulares

4.11.1. El paquete java.util.regex

El paquete java.util.regex permite el uso de expresiones regulares. Estas permiten comprobar el formato de una cadena de caracteres. De este modo, se puede verificar que una cadena que representa una fecha tenga efectivamente el formato dd/mm/aa. Para ello, se utiliza un patrón y se compara la cadena con dicho patrón. Así, en este ejemplo, j, m y a deben ser números. El patrón de un formato de fecha válido es, por tanto, «\d\d/\d\d/\d\d», donde el símbolo \d representa un dígito. Los símbolos que se pueden utilizar en un patrón son los siguientes (documentación de Microsoft):

Carácter
Descripción
\
Marca el carácter siguiente como carácter especial o literal. Por ejemplo, «n» corresponde al carácter «n». «\n» corresponde a un carácter de nueva línea. La secuencia «\\» corresponde a «\», mientras que «\»(» corresponde a «(».
^
Corresponde al inicio de la entrada.
$
Corresponde al final de la entrada.
*
Corresponde al carácter anterior cero o más veces. Así, «zo*» corresponde a «z» o a «zoo».
+
Corresponde al carácter anterior una o varias veces. Así, «zo+» corresponde a «zoo», pero no a «z».
?
Coincide con el carácter anterior cero o una vez. Por ejemplo, «a?ve?» coincide con «ve» en «lever».
.
Coincide con cualquier carácter único, excepto el carácter de nueva línea.
(patrón)
Busca «modèle» y memoriza la coincidencia. La subcadena correspondiente se puede extraer de la colección Matches obtenida, utilizando «Item [0]...[n]». Para encontrar coincidencias con caracteres entre paréntesis ( ), utiliza «\(» o «\)».
x|y
Coincide tanto con x como con y. Por ejemplo, «z|foot» coincide con «z» o con «foot». «(z|f)oo» coincide con «zoo» o con «foo».
{n}
n es un número entero no negativo. Se corresponde exactamente con n multiplicado por el carácter. Por ejemplo, «o{2}» no se corresponde con la «o» de «Bob», sino con las dos primeras «o» de «fooooot».
{n,}
n es un número entero no negativo. Corresponde al menos a n veces el carácter. Por ejemplo, «o{2,}» no coincide con la «o» de «Bob», sino con todas las «o» de «fooooot». «o{1,}» equivale a «o+» y «o{0,}» equivale a «o*».
{n,m}
m y n son números enteros no negativos. Corresponde a un número de veces del carácter que va desde, como mínimo, n hasta, como máximo, m. Por ejemplo, «o{1,3}» corresponde a las tres primeras «o» de «foooooot» y «o{0,1}» equivale a «o?».
[xyz]
Conjunto de caracteres. Coincide con uno de los caracteres indicados. Por ejemplo, «[abc]» coincide con la «a» de «plat».
[^xyz]
Conjunto de caracteres negativo. Corresponde a cualquier carácter no indicado. Por ejemplo, «[^abc]» corresponde a la «p» de «plat».
[a-z]
Rango de caracteres. Corresponde a cualquier carácter de la serie especificada. Por ejemplo, «[a-z]» corresponde a cualquier letra minúscula comprendida entre «a» y «z».
[^m-z]
Rango de caracteres negativo. Corresponde a cualquier carácter que no se encuentre en la serie especificada. Por ejemplo, «[^m-z]» corresponde a cualquier carácter que no se encuentre entre «m» y «z».
\b
Corresponde a un límite que representa una palabra; es decir, a la posición entre una palabra y un espacio. Por ejemplo, «er\b» corresponde a «er» en «lever», pero no a «er» en «verbe».
\B
Corresponde a un límite que no representa una palabra. «en*t\B» corresponde a «ent» en «bien entendu».
\d
Corresponde a un carácter que representa un número. Equivale a [0-9].
\D
Corresponde a un carácter que no representa un número. Equivale a [^0-9].
\f
Corresponde a un carácter de salto de página.
\n
Corresponde a un carácter de nueva línea.
\r
Equivale a un carácter de retorno de carro.
\s
Equivale a cualquier espacio en blanco, incluyendo el espacio, la tabulación, el salto de página, etc. Equivale a «[ \f\n\r\t\v]».
\S
Corresponde a cualquier carácter de espacio no en blanco. Equivale a «[^ \f\n\r\t\v]».
\t
Corresponde a un carácter de tabulación.
\v
Corresponde a un carácter de tabulación vertical.
\w
Corresponde a cualquier carácter que represente una palabra e incluya un guión bajo. Equivale a «[A-Za-z0-9_]».
\W
Corresponde a cualquier carácter que no represente una palabra. Equivale a «[^A-Za-z0-9_]».
\num
Corresponde a num, donde num es un número entero positivo. Hace referencia a las correspondencias almacenadas. Por ejemplo, «(.)\1» corresponde a dos caracteres idénticos consecutivos.
\n
Equivale a n, donde n es un valor de escape octal. Los valores de escape octales deben constar de 1, 2 o 3 dígitos. Por ejemplo, tanto «\11» como «\011» equivalen a un carácter de tabulación. «\0011» equivale a «\001» y «1». Los valores de escape octales no deben superar 256. Si fuera así, solo se tendrían en cuenta los dos primeros dígitos en la expresión. Permite utilizar los códigos ASCII en expresiones regulares.
\xn
Equivale a n, donde n es un valor de escape hexadecimal. Los valores de escape hexadecimales deben constar obligatoriamente de dos dígitos. Por ejemplo, «\x41» corresponde a «A». «\x041» equivale a «\x04» y «1». Permite utilizar los códigos ASCII en expresiones regulares.

Un elemento de una plantilla puede aparecer una o varias veces. Veamos algunos ejemplos relacionados con el símbolo \d, que representa un dígito:

plantilla
significado
\d
un dígito
\d?
0 o 1 dígito
\d*
0 o más dígitos
\d+
1 o más dígitos
\d{2}
2 dígitos
\d{3,}
al menos 3 dígitos
\d{5,7}
entre 5 y 7 dígitos

Imaginemos ahora el modelo capaz de describir el formato esperado para una cadena de caracteres:

cadena buscada
modelo
una fecha en formato dd/mm/aa
\d{2}/\d{2}/\d{2}
una hora en formato hh:mm:ss
\d{2}:\d{2}:\d{2}
un número entero sin signo
\d+
una secuencia de espacios, que puede estar vacía
\s*
un número entero sin signo que puede ir precedido o seguido de espacios
\s*\d+\s*
un número entero que puede ser con signo y estar precedido o seguido de espacios
\s*[+|-]?\s*\d+\s*
un número real sin signo que puede ir precedido o seguido de espacios
\s*\d+(.\d*)?\s*
un número real que puede ser con signo y estar precedido o seguido de espacios
\s*[+|]?\s*\d+(.\d*)?\s*
una cadena que contenga la palabra «justo»
\bjusto\b
  

Se puede especificar dónde se busca el patrón en la cadena:

patrón
significado
^patrón
el patrón comienza la cadena
patrón$
el patrón termina la cadena
^patrón$
el patrón comienza y termina la cadena
patrón
el patrón se busca en toda la cadena, empezando por el principio de la misma.
cadena buscada
patrón
una cadena que termina con un signo de exclamación
!$
una cadena que termina en un punto
\.$
una cadena que comienza con la secuencia //
^//
una cadena que contiene solo una palabra, seguida o precedida opcionalmente de espacios
^\s*\w+\s*$
una cadena que contenga dos palabras, que pueden ir precedidas o seguidas de espacios
^\s*\w+\s*\w+\s*$
una cadena que contenga la palabra «secret»
\bsecret\b

Los subconjuntos de un patrón pueden «extraerse». De este modo, no solo se puede comprobar si una cadena se ajusta a un patrón concreto, sino que también se pueden extraer de dicha cadena los elementos que corresponden a los subconjuntos del patrón que se han encerrado entre paréntesis. Así, si analizamos una cadena que contiene una fecha dd/mm/aa y, además, queremos extraer los elementos dd, mm y aa de dicha fecha, utilizaremos el patrón (\d\d)/(\d\d)/(\d\d).

4.11.2. Comprobar que una cadena se ajusta a un patrón determinado

La clase Pattern permite comprobar si una cadena se ajusta a un patrón determinado. Para ello, se utiliza el método estático

boolean Matches(String modèle, String chaine)

con: modèle: el patrón que se va a comprobar; chaine: la cadena que se va a comparar con el patrón. El resultado es el valor booleano true si la cadena coincide con el patrón, y false en caso contrario.

He aquí un ejemplo:

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

// gestión de expresiones regulares
public class regex1 {
  public static void main(String[] args){
    // una expresión regular de patrón
    String modèle1="^\\s*\\d+\\s*$";
    // comparar un ejemplo con el patrón
    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);
  }//muestra
}//clasifica

y los resultados de la ejecución:

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

Cabe señalar que, en el patrón «^\s*\d+\s*$», el carácter \ debe duplicarse debido a la interpretación particular que Java hace de este carácter. Por lo tanto, se escribe: String patrón1 = "^\\s*\\d+\\s*$";

4.11.3. Buscar todos los elementos de una cadena que coincidan con un patrón

Consideremos el patrón «\d+» y la cadena « 123 456 789 ». El patrón aparece en tres posiciones diferentes de la cadena. Las clases Pattern y Matcher permiten recuperar las distintas ocurrencias de un patrón en una cadena. La clase Pattern es la que gestiona las expresiones regulares. Una expresión regular que se utilice más de una vez debe «compilarse». Esto acelera las búsquedas del patrón en las cadenas. El método estático compile se encarga de ello:

public static Pattern compile(String regex)

Toma como parámetro la cadena del patrón y devuelve un objeto Pattern. Para comparar el patrón de un objeto Pattern con una cadena de caracteres se utiliza la clase Matcher. Esta permite comparar un patrón con una cadena de caracteres. A partir de un objeto Pattern, es posible obtener un objeto de tipo Matcher con el método matcher:

public Matcher matcher(CharSequence input)

input es la cadena de caracteres que debe compararse con el patrón.

Así, para comparar el patrón «\d+» con la cadena « 123 456 789 », se puede crear un objeto Matcher de la siguiente manera:

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

A partir del objeto résultats anterior, podremos recuperar las diferentes ocurrencias del patrón en la cadena. Para ello, utilizamos los métodos suivantes de la clase Matcher:

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

El método find busca en la cadena analizada la primera aparición del patrón. Una segunda llamada a find buscará la siguiente aparición. Y así sucesivamente. El método devuelve true si encuentra el patrón; de lo contrario, devuelve false. La parte de la cadena correspondiente a la última aparición encontrada por find se obtiene con el método group y su posición con el método start. Así, si continuamos con el ejemplo anterior y queremos mostrar todas las apariciones del patrón «\d+» en la cadena « 123 456 789 », escribiremos:

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

El método reset permite reiniciar el objeto Matcher al principio de la cadena comparada con el patrón. De este modo, el método find volverá a encontrar la primera aparición del patrón.

A continuación se muestra un ejemplo completo:

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

// gestión de expresiones regulares
public class regex2 {
  public static void main(String[] args){
    // varias ocurrencias del patrón en el ejemplo
    String modèle2="\\d+";
    Pattern regex2=Pattern.compile(modèle2);
    String exemplaire3="  123  456 789";
    // búsqueda de ocurrencias del patrón en el ejemplo
    Matcher matcher2=regex2.matcher(exemplaire3);
    while(matcher2.find()){
      affiche("séquence " + matcher2.group() + " trouvée en position " + matcher2.start());
    }//while
  }//Main

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

Resultados de la ejecución:

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. Extraer partes de un patrón

Se pueden «extraer» subconjuntos de un patrón. De este modo, no solo se puede comprobar si una cadena se ajusta a un patrón concreto, sino que también se pueden extraer de dicha cadena los elementos correspondientes a los subconjuntos del patrón que se han encerrado entre paréntesis. Así, si analizamos una cadena que contiene una fecha dd/mm/aa y, además, queremos extraer los elementos dd, mm y aa de dicha fecha, utilizaremos el patrón (\d\d)/(\d\d)/(\d\d).

Veamos el siguiente ejemplo:

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

// gestión de expresiones regulares
public class regex3 {
  public static void main(String[] args){
    // captura de elementos en la plantilla
    String modèle3="(\\d\\d):(\\d\\d):(\\d\\d)";
    Pattern regex3=Pattern.compile(modèle3);
    String exemplaire4="Il est 18:05:49";
    // verificación de la plantilla
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // el ejemplar se ajusta a la plantilla
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // se muestran los 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{
        // el ejemplar no se corresponde con la plantilla
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }
    }//Principal

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

Al ejecutar este programa se obtienen los siguientes 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

La novedad se encuentra en el siguiente fragmento de código:

    // verificación del modelo
    Matcher résultat=regex3.matcher(exemplaire4);
    if (résultat.find()){
      // el ejemplar se corresponde con el modelo
      affiche("L'exemplaire ["+exemplaire4+"] correspond au modèle ["+modèle3+"]");
      // se muestran los 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{
        // el ejemplar no coincide con el modelo
        affiche("L'exemplaire["+exemplaire4+" ne correspond pas au modèle ["+modèle3+"]");
      }

La cadena exemplaire4 se compara con el patrón regex3 mediante el método find. A continuación, se encuentra una ocurrencia del patrón regex3 en la cadena exemplaire4. Si el patrón incluye subconjuntos entre paréntesis, estos están disponibles mediante diversos métodos de la clase Matcher:


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

El método groupCount proporciona el número de subconjuntos encontrados en el modelo y group(i), el subconjunto n.º i. Este se encuentra en la cadena en una posición determinada por start(i). Así, en el ejemplo:

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

La primera llamada al método find encontrará la cadena 18:05:49 y creará automáticamente los tres subconjuntos definidos por los paréntesis de la plantilla, que son 18, 05 y 49, respectivamente.

4.11.5. Un programa de aprendizaje

Encontrar la expresión regular que nos permita comprobar si una cadena se ajusta a un determinado patrón puede suponer, en ocasiones, todo un reto. El siguiente programa permite practicar. Solicita un patrón y una cadena y, a continuación, indica si la cadena se ajusta o no al patrón.

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

// gestión de expresiones regulares
public class regex4 {
  public static void main(String[] args){

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

    // gestión de errores
    try{
      // se solicita al usuario las plantillas y los ejemplares que se van a comparar con este
      while(true){
        // flujo de entrada
        IN=new BufferedReader(new InputStreamReader(System.in));
        // se solicita la plantilla
        System.out.print("Tapez le modèle à tester ou fin pour arrêter :");
        modèle=IN.readLine();
        // ¿Terminado?
        if(modèle.trim().toLowerCase().equals("fin")) break;
        // se crea la expresión regular
        regex=Pattern.compile(modèle);
        // se le pide al usuario los ejemplares que se van a comparar con el modelo
        while(true){
          System.out.print("Tapez la chaîne à comparer au modèle ["+modèle+"] ou fin pour arrêter :");
          chaine=IN.readLine();
          // ¿Terminado?
          if(chaine.trim().toLowerCase().equals("fin")) break;
          // se crea el objeto «matcher»
          résultats=regex.matcher(chaine);
          // se buscan las ocurrencias del patrón
          nbOccurrences=0;
          while(résultats.find()){
            // se ha encontrado una coincidencia
            nbOccurrences++;
            // se muestra
            System.out.println("J'ai trouvé la correspondance ["+résultats.group()
            +"] en position "+résultats.start());
            // Visualización de los 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
            // cadena siguiente
          }//while(résultats.find())
          // ¿se ha encontrado al menos una aparición?
          if(nbOccurrences==0){
            System.out.println("Je n'ai pas trouvé de correspondance au modèle ["+modèle+"]");
          }//si
          // patrón siguiente
        }//while(true)
      }//while(true)
    }catch(Exception ex){
      // error
      System.err.println("Erreur : "+ex.getMessage());
      // fin con error
      System.exit(1);
    }//try-catch
    // fin
    System.exit(0);
  }//Main
}//clase

A continuación se muestra un ejemplo de ejecución:

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. El método split de la clase Pattern

Consideremos una cadena de caracteres compuesta por campos separados por una cadena separadora que se expresa mediante una expresión regular. Por ejemplo, si los campos están separados por el carácter , precedido o seguido de cualquier número de espacios, la expresión regular que modela la cadena separadora de los campos sería «\s*,\s*». El método split de la clase Pattern nos permite recuperar los campos en un array:


public String[] split(CharSequence input)

La cadena input se descompone en campos, separados por un separador que se ajusta al modelo del objeto Pattern actual. Para recuperar los campos de una línea cuyo separador de campos sea una coma precedida o seguida de cualquier número de espacios, se escribirá:

    // una línea
    String ligne="abc  ,, def  , ghi";
    // un modelo
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // descomposición de una línea en campos
    String[] champs=modèle.split(ligne);

Se puede obtener el mismo resultado con el método split de la clase String:


public String[] split(String regex)

A continuación se muestra un programa de prueba:

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

// gestión de expresiones regulares
public class split1 {
  public static void main(String[] args){
    // una línea
    String ligne="abc  ,, def  , ghi";
    // una plantilla
    Pattern modèle=Pattern.compile("\\s*,\\s*");
    // descomposición de una línea en campos
    String[] champs=modèle.split(ligne);
    // visualización
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
    // otra forma de hacerlo
    champs=ligne.split("\\s*,\\s*");
    // visualización
    for(int i=0;i<champs.length;i++){
      System.out.println("champs["+i+"]=["+champs[i]+"]");
    }//for
  }//Principal
}//clase

Resultados de la ejecución:

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

4.12. Ejercicios

4.12.1. Ejercicio 1

En Unix, los programas suelen ejecutarse de la siguiente manera:

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

donde -oi representa una opción y vi un valor asociado a dicha opción. Se desea crear una clase «options» que permita analizar la cadena de argumentos -o1 v1 v2 ... -o2 v3 v4 … con el fin de construir las siguientes entidades:

optionsValides
diccionario (Hashtable) cuyas claves son las opciones oi válidas. El valor asociado a la clave oi es un vector (Vector) cuyos elementos son los valores v1 v2 … asociados a la opción -oi
optionsInvalides
diccionario (tabla hash) cuyas claves son las opciones «oi» no válidas. El valor asociado a la clave «oi» es un vector (Vector) cuyos elementos son los valores v1, v2… asociados a la opción «-oi»
optionsSans
cadena (String) que proporciona la lista de valores vi no asociados a ninguna opción
erreur
entero igual a 0 si no hay errores en la línea de argumentos; en caso contrario, otro valor:
1: hay parámetros de llamada no válidos
2: hay opciones no válidas
4: hay valores no asociados a opciones
Si hay varios tipos de errores, estos valores se suman.

Un objeto «options» se puede crear de cuatro formas diferentes:

public options (String arguments, String optionsAcceptables)

arguments
la línea de argumentos -o1 v1 v2 ... -o2 v3 v4 … que se va a analizar
optionsAcceptables
la lista de opciones oi aceptables

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

En este caso, los dos argumentos son cadenas de caracteres. Se aceptarán los casos en los que estas cadenas se hayan dividido en palabras y se hayan almacenado en un array de cadenas de caracteres. Para ello se necesitan otros tres constructores:

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

La clase «options» tendrá la siguiente interfaz (métodos de acceso):

public Hashtable getOptionsValides()

devuelve la referencia de la tabla optionsValides creada al crear el objeto «options»

public Hashtable getOptionsInvalides()

devuelve la referencia de la matriz optionsInvalides creada al crear el objeto «options»

public String getOptionsSans()

devuelve la referencia de la cadena optionsSans generada al crear el objeto «opciones»

public int getErreur()

devuelve el valor del atributo «error» creado al crear el objeto de opciones

public String toString()

si no hay ningún error, muestra los valores de los atributos optionsValides, optionsInvalides, optionsSans; de lo contrario, muestra el número de error.

A continuación se muestra un programa de ejemplo:


import java.io.*;
//opciones de importación;

public class test1{
  
  
  public static void main (String[] arg){
    
    // apertura del flujo de entrada
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    // lectura de los argumentos del constructor 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));
    }// fin del bucle «while»
  }//fin de main


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

}//fin de la clase

Algunos 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. Ejercicio 2

Se desea crear una clase «stringtovector» que permita transferir el contenido de un objeto String a un objeto Vector. Esta clase derivaría de la clase Vector:

class stringtovector extends Vector

y tendría el siguiente código:


    private void stringtovector(String S, String separateur, int[] tChampsVoulus,
            boolean strict){
        
        // crea un vector con los campos de la cadena S
        // esta está formada por campos separados por «separador»
        // si separador=null, la cadena forma un único campo
        // solo se desean los campos cuyos índices se encuentran en la tabla tChampsVoulus 
        // son los deseados. Los índices comienzan en 1
        // si tChampsvoulus es nulo o tiene tamaño nulo, se toman todos los campos
        // si «strict» = «true», deben estar presentes todos los campos deseados

La clase tendría el siguiente atributo privado:

        private int erreur;

Este atributo lo establece el constructor anterior con los siguientes valores:

0: la construcción se ha realizado correctamente

4: faltan algunos campos requeridos, aunque strict=true

La clase también tendrá dos métodos:

    public int getErreur()

que devuelve el valor del atributo privado erreur.

    public String identite(){

que muestra el valor del objeto en el formato (error, elemento 1, elemento 2, …) donde los elementos i son los elementos del vector construido a partir de la cadena.

Un programa de prueba podría ser el siguiente:


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

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

Algunos consejos:

  1. Para dividir la cadena S en campos, utiliza el método split de la clase String.
  2. Colocar los campos de S en un diccionario D indexado por el número de campo
  3. Recuperar del diccionario D únicamente los campos cuya clave (índice) figure en la tabla tChampsVoulus.

4.12.3. Ejercicio 3

Se desea añadir a la clase stringtovector el siguiente constructor:


public stringtovector(String S, String separateur, String sChampsVoulus,boolean strict){
        
        // crea un vector con los campos de la cadena S
        // esta está formada por campos separados por «separador»
        // si «separador» = nulo, la cadena forma un único campo
        // solo se desean los campos cuyos índices estén en sChampsVoulus
        // los índices comienzan en 1
        // si sChampsvoulus = null o «», se toman todos los campos
        // si strict=verdadero, deben estar presentes todos los campos deseados

Por lo tanto, la lista de campos deseados se encuentra en una cadena (String) en lugar de en una matriz de enteros (int[]). Al atributo privado erreur de la clase se le puede asignar un nuevo valor:

2: la cadena de los índices de los campos deseados es incorrecta

A continuación se muestra un programa de ejemplo:


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

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

Algunos consejos:

  1. Hay que volver al caso del constructor anterior transfiriendo los campos de la cadena sChampsVoulus a una matriz de enteros. Para ello, hay que dividir sChampsVoulus en campos mediante un objeto StringTokenizer, cuyo atributo countTokens indicará el número de campos obtenidos. A continuación, se puede crear una matriz de enteros del tamaño adecuado y rellenarla con los campos obtenidos.
  2. Para saber si un campo es un entero, utilice el método Integer.parseInt para convertir el campo en un entero y gestione la excepción que se generará cuando esta conversión no sea posible.

4.12.4. Ejercicio 4

Se desea crear una clase «filetovector» que permita transferir el contenido de un archivo de texto a un objeto Vector. Esta clase derivaría de la clase Vector:

class filetovector extends Vector

y tendría el siguiente constructor:


    // --------------------- constructor 
    public filetovector(String nomFichier, String separateur, int [] tChampsVoulus,boolean strict, String tagCommentaire){
        
        // crea un vector con las líneas del archivo de texto nomFichier
        // las líneas están formadas por campos separados por un separador
        // si separador=null, la línea forma un único campo
        // solo se desean los campos cuyos índices se encuentren en tChampsVoulus
        // Los índices comienzan en 1
        // si tChampsvoulus es nulo o está vacío, se toman todos los campos
        // si «strict» = verdadero, deben estar presentes todos los campos deseados
        // si no es así, la línea no se almacena y su índice
        // se coloca en el vector lignesErronees
        // las líneas en blanco se ignoran
        // así como las líneas que comienzan por tagCommentaire si tagCommentaire != null

La clase tendría los siguientes atributos privados:

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

El atributo erreur es establecido por el constructor anterior con los siguientes valores:

0: la construcción se ha realizado correctamente

1: no se ha podido abrir el archivo que se iba a procesar

4: faltan algunos campos requeridos, aunque strict=true

8: se ha producido un error de E/S al procesar el archivo

El atributo lignesErronees es un vector cuyos elementos son los números de las líneas erróneas en forma de cadena de caracteres. Una línea se considera errónea si no puede proporcionar los campos solicitados cuando strict=true.

La clase también tendrá dos métodos:

    public int getErreur()

que devuelve el valor del atributo privado erreur.

    public String identite(){

que muestra el valor del objeto en el formato (error, elemento 1, elemento 2…, (l1, l2,…)), donde los elementos i son los elementos del vector construido a partir del archivo y li son los números de las líneas con errores.

A continuación se muestra un ejemplo de prueba:


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 de la ejecución:

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

Algunos consejos

  1. El archivo de texto se procesa línea por línea. La línea se divide en campos mediante la clase **stringtovector,** que ya hemos visto anteriormente.

  2. Los elementos del vector creado a partir del archivo de texto son, por lo tanto, objetos de tipo **stringtovector**.

  3. El método identite de filetovector podrá utilizar el método stringtovector.identite() para mostrar sus elementos, así como el método Vector.toString() para mostrar los números de las posibles líneas erróneas.

4.12.5. Ejercicio 5

Se desea añadir a la clase «filetovector» el siguiente constructor:


    public filetovector(String nomFichier, String separateur, String sChampsVoulus,
            boolean strict, String tagCommentaire){
        
        // crea un vector con las líneas del archivo de texto nomFichier
        // las líneas están formadas por campos separados por un separador
        // si separador=null, la línea forma un único campo
        // solo se desean los campos cuyos índices se encuentren en tChampsVoulus
        // Los índices comienzan en   1
        // si sChampsvoulus es nulo o está vacío, se toman todos los campos
        // si «strict» = verdadero, deben estar presentes todos los campos deseados
        // si no es así, la línea no se almacena y su índice
        // se coloca en el vector lignesErronees
        // las líneas en blanco se ignoran
        // así como las líneas que comienzan por tagCommentaire si tagCommentaire != null

La lista de índices de los campos deseados se encuentra ahora en una cadena de caracteres (String) en lugar de estar en una matriz de números enteros.

El atributo privado erreur puede tener un valor adicional:

2: la cadena de índices de los campos deseados es incorrecta

Aquí tienes un ejemplo de prueba:


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

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

Algunos consejos

  1. Convertiremos la cadena sChampsVoulus en una matriz de números enteros tChampVoulus para volver al caso del constructor anterior.