Lunes 30 de Agosto

Procesamiento de Datos Masivos - 2da. Parte

Objetivos: Mostrar como se pueden simplificar los problemas de manejo de archivos por medio de la definición de clases de apoyo para la representación de las líneas de los archivos.

Temas:


Definición de clases de apoyo

Manipular los campos de información mediante los decodificadores puede llegar a ser muy frágil. Es fácil equivocarse y olvidar un campo y se termina leyendo información errada. Por otra parte, aún cuando el programa funcione, más tarde puede ser necesario modificar la estructura del archivo agregando nuevos campos de información. Esto obliga a tener que cambiar todos los programas que usan ese archivo. Si se nos olvida cambiar uno, los resultados de ese programa estarán equivocados.

Por ello es conveniente definir para cada archivo una y sólo una clase que representa la información contenida en una línea del archivo. Para cada uno de los campos del archivo, la clase tiene una variable de instancia con su contenido. Los objetos se construyen a partir de un archivo o a partir de los valores de los campos. La clase suministra un método escribir para colocar el contenido del objeto en un archivo.

Por ejemplo, la clase Cuenta permite representar el contenido de una línea del archivo cuentas.dat:

    class Cuenta {
      String ci= ""; // Los campos de una línea
      String nombre= "";
      String cuenta= "";
      Cuenta(String ci, String nombre, String cuenta) {
        this.ci= ci;
        this.nombre= nombre;
        this.cuenta= cuenta;
      }
      // Lee del archivo los campos en el formato
      // ci:nombre:cuenta
      Cuenta(TextReader lect) {
        String lin= lect.readLine();
        if (!lect.eofReached()) {
          FieldParser decod= new FieldParser(lin, ":");
          ci= decod.readString();
          nombre= decod.readString();
          cuenta= decod.readString();
        }
      }
      // Escribe los campos en el formato ci:nombre:cuenta
      void escribir(TextWriter escr) {
        escr.println(ci+":"+nombre+":"+cuenta);
      }
    }
Todos los programas que usen archivos con estos mismos campos usarán esta clase. Si se agregan o quitan campos a estos archivos, basta cambiar la definición de la clase para que la mayoría de los programas que usan esta estructura de archivos ya puedan usar el nuevo formato. Solo será necesario cambiar los programas que usan los campos que fueron suprimidos o que requieren utilizar los que se hayan agregado.

Solución para el pareo de archivos

El problema consiste en leer secuencialmente los archivos cuentas.dat y saldos.dat y producir un archivo saldos-cuentas.dat que contenga las cuentas que aparecen en ambos archivos. La siguiente tabla muestra para cada archivo los campos que contiene y la clase que permite manipular sus líneas:

Archivo Campos Clase
cuentas.dat ci, nombre, cuenta Cuenta
saldos.dat cuenta, saldo Saldo
cuentas-saldos.dat ci, nombre, cuenta, saldo CuentaSaldo

El programa que realiza el pareo es:

    TextReader lect= new TextReader("cuentas.dat");
    TextReader saldosLect= new TextReader("saldos.dat");
    TextWriter escr= new TextWriter("cuentas-saldos.dat");
    Saldo sal= new Saldo(saldosLect);
    Cuenta cuen= new Cuenta(lect);
    while (!lect.eofReached() && ! saldosLect.eofReached()) {
      int cmp= compare(cuen.cuenta, sal.cuenta);
      if (cmp<0)
        cuen= new Cuenta(lect);
      else if (cmp>0)
        sal= new Saldo(saldosLect);
      else {
        CuentaSaldo cuenSal=
          new CuentaSaldo(cuen.ci, cuen.nombre,
                          cuen.cuenta, sal.saldo);
        cuen.escribir(escr);
        cuen= new Cuenta(lect);
        sal= new Saldo(saldosLect);
      }
    }
    escr.close();
    lect.close();
    saldosLect.close();

Eliminación de duplicados

No siempre los archivos contienen información consistente. Por ejemplo, es común encontrarse con archivos de cuentas en donde una persona aparece 2 o más veces. En algunas organizaciones esto no tiene sentido y es necesario programar herramientas que permitan detectar este tipo de situaciones.

El siguiente programa lee el archivo cuentas.dat y produce 2 archivos: uno contiene las personas que aparecen una sola vez (unicos.dat) y el otro almacena las que aparecen 2 o más veces (dups.dat). Por ejemplo si el archivo fuese:

cuentas2.dat
------------
10000:Pedro:100
20000:Diego:104
20000:Diego:104
30000:Juan:101
60000:Sutano:107
80000:Megano:109
80000:Megano:110
Los archivos producidos serán:

unicos.dat                dups.dat
----------                --------
10000:Pedro:100           20000:Diego:104
30000:Juan:101            20000:Diego:104
60000:Sutano:107          80000:Megano:109
                          80000:Megano:109
El programa supone que el archivo está ordenado por carnet de identidad. Más tarde examinaremos cómo es posible ordenar archivos cuando caben en memoria.

        TextReader lect= new TextReader("cuentas2.dat");
        TextWriter escr= new TextWriter("unicos.dat");
        TextWriter escrDup= new TextWriter("dups.dat");
        Cuenta cuen= new Cuenta(lect);
        while (!lect.eofReached()) {
          Cuenta sgte= new Cuenta(lect);
          if (compare(cuen.ci, sgte.ci)!=0)
            cuen.escribir(escr);
          else {
            cuen.escribir(escrDup);
            while(compare(cuen.ci, sgte.ci)==0) {
              sgte.escribir(escrDup);
              sgte= new Cuenta(lect);
            }
          }
          cuen= sgte;
        }
        lect.close();
        escr.close();
        escrDup.close();