sábado, 29 de marzo de 2008

Archivos I



Abajo

CLASE 8: Archivos.

Archivos

La necesidad de poder recuperar los datos en momentos posteriores a su creación, ya sea, por haberse cortado el suministro de la corriente eléctrica o en distintas corridas del programa o en distintos programas, por un lado, y por otro ante la imposibilidad de poder contar con todos los datos simultáneamente en la memoria interna debido a su limitación de espacio, son dos motivos que hacen de la necesidad de contar con este tipo de estructuras de datos.
El archivo es la única estructura de datos externa, es decir, ubicadas en un dispositivo externo, al cuál se los denomina memoria auxiliar, o memoria secundaria o memoria externa.
La desventaja principal de este tipo de estructura es el tiempo necesario para recuperar un dato, ya que estos tiempos se miden en milisegundos, esto es 10-3 segundos en comparación con el tiempo empleado para acceder a una posición en la memoria interna cuya unidad de medida es el nanosegundo, esto es 10-9 segundos; por lo tanto, al momento de requerir un dato desde un archivo, debemos tomar muy en cuenta esta última situación, tratando de minimizar estos tiempos.
Las componentes de un archivo se denominan registros, y en la mayoría de las situaciones, estas componentes serán de tipo registro. No obstante, en ciertas situaciones podrán ser de un tipo simple de datos como integer, word, char, boolean, longint o punteros, como así también de otro tipo estructurado de datos que se verán más adelante.
Un archivo es por lo tanto, una colección de registros que responden a una misma naturaleza, p.e. Artículos, Clientes, Proveedores, Empleados, Cuentas Contables, etc..

A continuación se presenta una clasificación de archivos de acuerdo a su función de uso:

Datos:

Maestros: Son archivos permanentes en el tiempo, es decir, no se eliminan luego de un proceso. Contienen todos los datos necesarios para el desarrollo de las actividades de una organización. Representan al mundo real. Con el transcurrir del tiempo deben ser actualizados. Dependiendo del momento en que se actualizó, da un grado de confiabilidad. Ejemplo de archivos maestros pueden ser, los Clientes, Proveedores, Empleado, Artículos, Cuentas Contables, etc.

Novedades o Transacciones: Son archivos transitorios, es decir, luego de ser procesados, no tiene sentido mantenerlos, por lo tanto son eliminados. Su cometido es generalmente la actualización de los archivos maestros. La eliminación se podrá realizar inmediatamente o bien luego de un período de tiempo, por ejemplo, después de una segunda actualización al maestro. No siempre existen estos archivos, esto depende del tipo de proceso que se lleve a cabo. Por ejemplo si el proceso es interactivo en tiempo real, esto es, en el momento en que se conoce la novedad se actualiza en el maestro, no existirá un archivo de novedades. También se podrán generar registros por cada novedad que se presente en un proceso en tiempo real interactivo para control. En cambio, si el proceso es en batch o por lotes, primero se capturan las novedades durante el transcurso de un tiempo, -un día, una semana, un mes- se los ordena bajo un cierto criterio.

Históricos: Son archivos cuyo uso generalmente son para fines estadísticos, por ejemplo, las ventas realizadas por mes de un año, el seguimiento de ciertos artículos más solicitados, procesos de períodos anteriores, etc..

Tablas: Son archivos de poco volumen, ya sea en cantidad de registros o con respecto a su longitud del mismo. A efectos de ganar velocidad durante el proceso, estos tipos de archivos pueden ser volcados a la memoria interna –RAM- para acelerar el proceso, debido a que acceder a un registro en un archivo la unidad de medida es el milisegundo, esto es, 10-3 seg., en cambio acceder a una ubicación en la memoria interna, la unidad de medida es el nanosegundo, es decir, 10-9 seg., razón por la cual se ve la enorme diferencia existente entre acceder a una u otra fuente. El volcado del archivo se realiza en una pasada secuencial, luego al requerir acceder a un dato se accede en la propia memoria interna. Según los procesos tal vez no sea necesario bajar todos los registros como así también bajar todos los campos del mismo. Por ejemplo un archivo que contenga los códigos de las provincias y un porcentaje que fijan las mismas por las ventas realizadas a esas provincias, un proceso que requiera esos datos, podrían ser volcados a la memoria principal, recorriendo secuencialmente de inicio a fin sobre este archivo, luego cada vez que se requiera averiguar el porcentaje de una provincia se accede a la posición de memoria interna. Más adelante se verá la manera de lograr este cometido con una estructura de datos estática que se estudiará posteriormente.

Índices: Son archivos que contienen 2 ó 3 campos generalmente, un campo denominado clave y un campo denominado referencia o dirección el tercer campo si existe es denominado estado. Estos archivos se encuentran ordenados por el campo clave. Su objetivo es indexar al archivo maestro u otros archivos de datos. Esto permite una búsqueda más eficiente y establece un orden lógico de esos datos. Podrán existir varios archivos de índices para un mismo archivo de datos y c/u. de ellos estará ordenado por el campo clave que corresponda. Para mayor información ver organización indexada.

Auxiliares: Son archivos que crea y elimina el programador y que son necesarios para mejorar la eficiencia de un proceso. Por ejemplo, un archivo de vendedores desordenado y un proceso que requiera acceder a los registros en forma reiterada o en distinto orden al que se grabaron los mismos. Está claro que al buscar un vendedor debemos recorrer el archivo secuencialmente y esto por cada vendedor que requiera el proceso, nada eficiente será el proceso, por lo cual debemos recurrir a alguna técnica que permita optimizar el proceso. Una de las técnicas que podrían emplearse es crear un archivo auxiliar de tal manera que ordene esos registros de alguna manera y permita un mayor acceso a los mismos. Por ejemplo si los vendedores están identificados con un Código de Vendedor de 1 a 999, se crean anticipadamente esos registros, luego al ir leyendo cada vendedor en el archivo original se lo ubica en la posición física en el archivo auxiliar en la posición indicada por el Código del vendedor leído. Posteriormente se realiza el proceso principal pero esta vez, al buscar un vendedor se accede en el auxiliar en la posición indicada por el código del vendedor, al leer el registro su valor indica la posición en el archivo original del vendedor para acceder a esa ubicación y leer los datos del mismo.

Informes o Reportes: Son archivos cuyo destino original era la impresora, pero debido a que ésta ya estaba ocupada por otro proceso, el sistema operativo lo redireccionó hacia otro dispositivo, es decir, un archivo en disco para que posteriormente cuando la impresora sea liberada y la prioridad le sea asignada ese archivo sea volcado a la impresora, una vez que la tarea se llevó a cabo, el mismo sistema operativo elimina ese archivo. La parte del sistema operativo que realiza este cometido es el S.P.O.O.L. (Simultaneous Peripherical Operation On Line), es decir, Operaciones Periféricas simultáneas En Linea, el cual redirecciona las salidas de los procesos a archivos en disco debido a que el destino original, la impresora, estaba ocupada y por ser un recurso no compartido debió ser enviado a otro destino, armando una cola de espera.

Seguridad: Son archivos que se realizaron copias de otros archivos y en caso de pérdida de uno de ellos poder recuperarlos con el otro. Esto se conoce como back-up.

Programas:

Fuentes: Son los archivos escritos en un lenguaje de computadora y de tipo texto. La extensión de estos archivos se corresponde con el lenguaje utilizado, por ejemplo, .Pas, .C, .Cob, .Bas, .Prg, etc. Estos archivos son creados utilizando un editor de texto, por ejemplo el Word pero tipo texto, el bloc de notas, no son los más apropiados, otra forma es utilizar el propio editor de texto incorporado en el paquete de software del lenguaje. Así por ejemplo el Turbo Pascal de Borland viene un entorno de trabajo denominado I.D.E. –Medio ambiente de Desarrollo Integrado- en el cual no solo podremos editar nuestro código fuente, sino además compilar, ejecutar, depurar, entre otros aspectos. Además nos facilita la escritura ya que las palabras reservadas se escriben resaltadas al resto de las otras palabras.

Objetos: Son archivos resultado del proceso de compilar el código fuente. El compilador es una aplicación, es decir, un programa ejecutable que toma como parámetro el código fuente y produce como salida un archivo o programa objeto, cuya extensión es .OBJ, el cual aún no puede ser ejecutado debido a que le faltan las librerías a que hace referencia. Cada lenguaje posee su propio compilador.

Ejecutables: Son archivos resultado del enlace con las librerías para que sean incorporadas al código máquina y pueda correr en forma autosuficiente. El link es una aplicación que toma como parámetro el código objeto y el resultado final es un archivo ejecutable .EXE.

Otros:

Documentos: Son archivos creados con un procesador de palabra, por ejemplo el Word.

Imágenes gráficas: Presentan diferentes formatos .GIF, .JPG, .BMP, etc. Son creados por software graficadores como el Paint, Corel, Harvard, PhotoShop, etc.

Audio: Archivos de sonido o música con extensiones como ser .MP3, .WAV, .MID, etc.

Miscelánea: Librerías dinámicas .DLL, Sistema .SYS, Dispositivos .DRV, etc..

Clasificación de lenguajes:

Los lenguajes de computadoras pueden ser clasificados en cuanto al mayor acercamiento hacia la máquina o hacia el usuario en:

Bajo nivel: Son lenguajes que se acercan más a la máquina que al usuario. Cada instrucción se traduce en una única instrucción de máquina, se dice entonces que la relación es 1 a 1; el lenguaje de bajo nivel es el assembler y la aplicación que lo convierte a código máquina se denomina ensamblador. La característica más emblemática es que los programas ejecutables son los más veloces. La desventaja es que es más compleja su programación. La extensión de los archivos de código fuente presentan la extensión .ASM. Por ejemplo sumar un valor a una variable, se escribiría:

Medio nivel: Son lenguajes que se encuentran en un punto intermedio entre los de bajo y alto nivel. Lenguaje como C o Forth entran dentro de esta categoría. La forma en como se escribe el código en algunos casos puede llevarlo a un nivel más bajo o más alto, por ejemplo acumular un valor en una variable podría escribirse en lenguaje C de varias maneras diferentes, pero una de ellas generará un código de máquina más eficiente que las otras. El siguiente ejemplo muestra esto último: sum = sum + 1; sum+= 1 o ++sum o sum++; en los dos últimos casos generará un código más eficiente.

Alto nivel: Los lenguajes de alto nivel se acercan más al usuario que a la máquina y los programas escritos en código fuente se asemejan al lenguaje natural. Estos programas corren más lentos que los de bajo nivel. Una sentencia suele ser convertida a varias instrucciones en código máquina. Lenguajes como Pascal, C, Cobol, Basic, Fortran, Modula, Ada, Prolog entre otros son de alto nivel. Por ejemplo, acumular un valor en una variable sería:
sum := sum + 1
Pascal
inc(sum)
Pascal
add 1 to sum
Cobol
sum = sum + 1
C
sum += 1
C
sum++
C
++sum
C

Otras clasificaciones de los lenguajes podrían realizarse en cuanto al objetivo en que fueron concebidos, así existen lenguajes de propósito general, como ser el BASIC, PASCAL; otros destinados a la gestión y administración contable como el COBOL, RPG aún otros con fines científicos como el FORTRAN y otros para el desarrollo de soft de base como el C.

Traductores

Los traductores pueden ser de dos tipos diferentes:

  • Intérpretes
  • Compiladores

En el primer caso los lenguajes intérpretes, la ejecución se realiza dentro de un entorno de trabajo del lenguaje, y se ejecuta desde alli, o por medio de una aplicación, es decir un módulo de tiempo de ejecución que toma como parámetro el código fuente, cada sentencia a ejecutar primero debe ser interpretada a su equivalente en código máquina, generalmente una sentencia se divide en varias instrucciones de máquina, luego se ejecuta, esto se repite por cada sentencia que deba ser ejecutada, aún en los casos en que una misma sentencia se ejecute más de una vez deben de realizarse esos pasos; no se genera ningún código objeto en disco. Este tipo de lenguajes es oportuno cuando se está desarrollando la aplicación, en la cual tendremos que ejecutar el programa varias veces para refinarlo en detalles, hasta que quede el definitivo, entonces debido a que no hay tiempo de espera para la compilación total del programa; se hace conveniente en esos momentos; pero no cuando el programa haya quedado terminado de corregir detalles. El tiempo de ejecución es mayor en un programa interpretado que si fuera compilado. La ventaja es que entre cada ejecución del programa en la etapa de depuración no debemos esperar por el proceso de compilación, en la que muchas veces se demora bastante tiempo.
Por otro lado un lenguaje compilado primero se compila todo el código fuente, creándose un código máquina y guardado en un archivo con extensión .OBJ, luego en un segundo proceso se le incorporan las librerías produciendo un código ejecutable y guardado en un archivo con extensión .EXE. En este momento podremos correr o ejecutar la aplicación o programa. El tiempo insumido será mucho menor a un programa interpretado, debido a que el código fuente fue traducido a código máquina con anterioridad y solamente el proceso se centra en ejecutarlo.
Existen lenguajes que son solamente interpretados y otros que son solamente compilados, pero también existen lenguajes que pueden correr con un intérprete y que además puedan ser compilados, una vez que se hayan depurados ciertos errores. Por ejemplo un programa realizado en lenguaje Basic -ciertas versiones- puede ser solo interpretado o si el programador lo desea compilado. Otros como la mayoría de los lenguajes sólo compilado, por ejemplo, COBOL, C, PASCAL, ALGOL, CLIPPER, etc.

Etapas de procesos de los archivos en el tiempo

A continuación se presenta otra clasificación de archivos de acuerdo a distintos procesos que podemos realizar:

  • Creación
  • Actualización:
  • Altas
  • Bajas
  • Modificaciones
  • Recuperación:
  • Consultas
  • Informes
  • Mantenimiento:
  • Estructuración
  • Organización



Modo de apertura de archivos

Entrada –Input-
Un archivo abierto como solo de entrada indica que solo se podrá leer, hacer un intento de escritura ocasionará en un error. En el diagrama de flujo el símbolo es el bloque del trapecio invertido, base menor hacia abajo.

Salida –Output-
Un archivo abierto como solo de salida indica que solo se podrá grabar, hacer un intento de lectura ocasionará en un error. En el diagrama de flujo el símbolo es el bloque del trapecio, base mayor hacia abajo.

Entrada / Salida -Input/Output-
Un archivo abierto en el modo de lectura-escritura indica que se podrán realizar ambas operaciones, es decir, leer y/o grabar indistintamente.

Modo de acceso a los registros

Secuencial: Se recorren los registros uno a continuación del otro, es decir, en forma adyacente o contigua. Para alcanzar el registro que ocupa la ubicación n debemos recorrer todos los registros que lo preceden. El tiempo empleado para accesar un registro n depende del lugar en que se encuentre ubicado el puntero al archivo.

Al azar: El tiempo empleado para accesar un registro no depende del lugar en que se encuentre ubicado el puntero al archivo, debido a que se accede al registro n directamente, vale decir que, el tiempo empleado para acceder a cualquier posición es el mismo desde el lugar en que se encuentre el puntero al archivo.

Por ejemplo, el control remoto de una TV vía satélite o por cable permite 2 tipos de accesos, uno secuencial, y otro al azar; en el primer caso, si se oprimen las teclas CH+ o CH- permite un acceso secuencial a los canales; por otro lado, si tipeamos un número, -con las teclas numéricas-, cambiamos directamente a ese canal. ¡Imaginarse, por ejemplo, el tiempo empleado de acceder del canal 182 al canal 534 en forma secuencial!.

Organizaciones de archivos

Secuencial: La organización secuencial es aquella en la cual los registros solo pueden ser accedidos en forma secuencial. Además la apertura del archivo solo se realiza exclusivamente para entrada o exclusivamente para salida, en el primer caso solo se lo puede leer y en el segundo caso solo se lo puede grabar. Tanto la lectura o grabación se lleva a cabo hacia delante y no se permite retroceder a una posición previa. Los medios de almacenamiento por naturaleza son las cintas magnéticas pero también pueden ser los discos. En esta organización los registros suelen ser sometidos a un ordenamiento establecido. Además los registros podrán estar agrupados, contenidos en un bloque, esta técnica se conoce como factor de bloqueo en donde se determina un valor n, siendo este valor n la cantidad de registros contenidos en un bloque, esta técnica es utilizada en archivos con acceso secuencial para lograr un menor tiempo de procesamiento cada vez que se acceda al dispositivo externo. Un bloque representa un registro físico, mientras que los n registros contenidos en él representan n registros lógicos. Este tipo de organización es oportuna cuando los registros deban ser leídos en el mismo orden en que fueron grabados y deban ser procesados la mayoría de ellos.

Indexada: La organización indexada presenta básicamente 2 archivos, uno de datos similar a la organización secuencial, es decir, los registros que se incorporan son agregados al final del archivo y normalmente no estarán ordenados físicamente. El otro archivo es el de índice y puede contener 2 ó 3 campos, a saber, un campo que contendrá la clave y que debe formar parte en el archivo de datos, pudiendo ser de cualquier tipo pero el tipo preferido es el de cadenas, debido a que en muchas oportunidades se suelen combinar los valores de dos o más campos por medio de la operación de concatenación. El segundo campo representa la referencia o dirección, y que indica en donde se encuentra el valor de esa clave en el archivo de datos. Por último en caso de existir el tercer campo establece un estado para informar si ese registro debe ser tenido en cuenta en los procesos ya que podría habérsele dado de baja, con un valor podría indicar que estará activo o caso contrario estará inactivo, on u off, verdadero o falso, 0 ó 1. En caso en que el registro no debe tomarse en cuenta es debido a que se ha realizado una baja lógica, es decir, el registro sigue ocupando un lugar físico en el medio externo. Las bajas físicas en estos casos se realiza cuando se vayan acumulando varias bajas lógicas, ya que este proceso requiere de un mayor tiempo de proceso.

A continuación se presentará un ejemplo con valores en el archivo de datos para construir el archivo de índices. El siguiente archivo de datos puede representar datos de Alumnos y a efectos de resumir, solo se mostrará por cada registro el campo NroLeg.

Archivo de datos: Alumnos.Dat
valores del campo Nro.Leg. x c/registro.
43 27 38 75 12 89 62 31 56 19 94 83 42 5 98
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
direcciones físicas de cada registro.

Archivo de índices: Alumnos.Idx

Para construir la tabla de índices se comenzará leyendo el primer valor del archivo, luego el siguiente y así sucesivamente, pero si vamos escribiendo en la tabla de más abajo notaremos que debemos insertar por cualquier lugar de la lista que vayamos armando y en el caso de escribirlo sobre papel es casi una tarea imposible; por esta razón vamos a implementar otro esquema gráfico que evitará la situación indicada anteriormente.
En el primer caso tendremos el siguiente problema:

CLV.......... REF
43, 27.......... 0, 1
43.............. 0

Luego de haber copiado el valor 43 y su referencia 0, al leer el siguiente dato vemos que el valor clave 27 es menor al ingresado previamente, si queremos mantener un orden ascendente deberíamos escribir este valor en una línea previa, pero sin antes borrar el valor 43 y su referencia para luego escribirlos más abajo. Notamos luego que al continuar con los próximos valores esta tarea se vería más complicada, razón por la cual adoptaremos otro criterio gráfico.

punto de entrada nodo raíz
Sí ClvNva menor a ValNodoVisitado
avanzar por rama izquierda sino avanzar por rama derecha



Nodos hojas son los nodos terminales
Un nuevo nodo se inserta siempre como nodo hoja o terminal.

El gráfico representa un árbol binario, el nodo raíz es el punto de entrada para incorporar un nuevo valor, por lo tanto por cada valor nuevo que ingresa se compara si es menor al valor del nodo actual, si es así se dirige a la izquierda, caso contrario hacia la derecha, en caso de no existir un nodo se crea uno nuevo, siempre como nodo hoja. Si seguimos este criterio, los valores irán ubicándose de la forma en que quedaron arriba. Esta forma depende de cómo se vayan conociendo los valores claves, ya que si esos mismos valores claves estuvieran acomodados de una manera diferente, diferente sería entonces la estructura que adoptaría el árbol.
Ahora vamos a volcar estos valores a nuestra tabla original, para ello debemos saber como se procede a recorrer el árbol, la forma de hacerlo podría ser in-orden IRD
[1], en pre-orden RID1 o en post-orden IDR1. Vamos a recorrer in-orden. En un árbol binario tenemos un sub-árbol izquierdo y un sub-árbol derecho, así el nodo con valor 27 es el nodo raíz del sub-árbol izquierdo, y el nodo con valor 75 es el nodo raíz del sub-árbol derecho, esta división en sub-árboles se puede continuar con los restantes nodos.
El recorrido se inicia por el nodo raíz, en este momento averiguamos si hay algún nodo hacia la izquierda, si es así descendemos un nivel, nuevamente hacemos lo mismo hasta llegar a un nivel en que no haya nodos a izquierda, entonces tomamos este valor en nodo raíz del sub-árbol y marcamos al nodo como visitado, en el ejemplo este valor es 5. Luego averiguamos si hay nodos a derecha, si es así descendemos un nivel y volvemos a aplicar el mismo criterio indicado anteriormente. En el ejemplo el siguiente valor a tomar es el 12 que se encuentra un nivel más arriba, debido a que no había nodos a derecha del nodo con valor 5. Luego el recorrido es hacia la derecha del nodo con valor 12, y así seguiremos avanzando y retrocediendo. Esta técnica es conocida como back-tracking.
A continuación se expondrá en el archivo de datos indicado anteriormente otro valor para cada registro, por ejemplo, el Apellido de cada alumno, para simplificar la escritura lo indicamos de la siguiente manera:

valores del campo Nro.Leg. x c/registro
MARTINEZ FERNÁNDEZ GONZALEZ PEREZ SUAREZ ALVAREZ LOPEZ
0 1 2 3 4 5 6
direcciones físicas de cada registro
valores del campo Nro.Leg. x c/registro
SOSA TORRES GARCÍA FERNÁNDEZ JUÁREZ VAZQUEZ BENITEZ RIOS
7 8 9 10 11 12 13 14
direcciones físicas de cada registro
punto de entrada nodo raíz
Sí ClvNva


Nodos hojas son los nodos terminales.
Un nuevo nodo se inserta siempre como nodo hoja o terminal.
Tabla: NroLeg Tabla: ApeNom

Conocido el valor de una clave, ¿qué método de búsqueda aplicar?. Si el método de búsqueda fuera secuencial comenzaríamos desde el primer valor en la tabla si es el que buscamos detenemos la búsqueda e indicaremos que el valor se encontró, sino puede que sea mayor el valor a buscar en ese caso seguiremos buscando con los próximos valores de la tabla hasta posiblemente encontrarlo o bien detenernos cuando aparezca un valor mayor al que buscamos o bien termine la tabla, en estos casos se informará que el valor no se encontró en la tabla.
Si el valor clave está entonces el siguiente paso será acceder con la referencia indicada en la tabla, al archivo de datos a esa misma posición, para obtener los datos requeridos.
Ahora bien, ¿será este método el secuencial el más adecuado cuando la tabla de índices se encuentra ordenada por la clave a buscar?. La respuesta es NO. Un mejor método es realizar una búsqueda binaria, aquella que parte en forma sucesiva por la mitad entre los valores mínimo y máximo de las direcciones de los registros extremos, es decir, en donde posiblemente se pueda encontrar el valor de la clave. Encontrado el punto medio entre los extremos se compara el valor a buscar con el valor de la posición de este punto medio, si se encontró se abandona la búsqueda, informando que se encontró el valor, sino puede suceder que sea mayor o menor, en cualquiera de los casos se acorta la tabla por su mitad, es decir, cambia el valor extremo menor por el de medio + 1 o bien cambia el valor extremo mayor por el de medio – 1, esto se repite hasta encontrar el valor a buscar, si es éste el caso se accede al archivo de datos en la dirección indicada por el campo ref. de la clave encontrada en la tabla de índices, o bien si el valor extremo menor se hizo mayor al valor extremo mayor, se abandona la búsqueda e informa que el valor clave no se encontró en la tabla.
En el árbol si buscamos el valor 31, debemos pasar por los siguientes nodos: 43, 27, 38 y 31.
Un árbol unialargado es aquel árbol en que crece solamente por una de sus ramas, izquierda o derecha para todo nodo.

Ejercicios:

¿Cuál sería la estructura de un árbol si las claves vienen ordenadas en forma ascendente?.
Idem anterior pero con las claves ordenadas en forma descendentes.

Las claves en una organización indexada pueden ser primarias o secundarias. Si la clave es primaria identifica unívocamente a un registro y su valor no puede repetirse en la tabla. En cambio si la clave es secundaria, puede que se repita o no.
Ejemplos de claves primarias y secundarias:

Claves Primarias: NroLeg en el archivo maestro de Empleados, CodArt en el archivo maestro de Artículos, etc.

Claves Secundarias: CodPos en al archivo maestro de Empleados o Clientes o Proveedores, notamos en este caso que diferentes empleados o clientes o proveedores pueden tener su domicilio bajo un mismo código postal, por lo tanto esta clave secundaria será con duplicación o repetición.
Para un mismo archivo podrán presentarse varias claves candidatas a ser clave primaria, la elección queda más justificada cuanto más compacta sea la misma.
Las claves también pueden ser simples o compuestas. Una clave simple es la que se forma con el valor de un solo campo; mientras que una clave compuesta se forma con la concatenación de dos o más valores de campos.
Los valores de las claves deben formar parte en el archivo de datos. Si la clave es de tipo cadena es mejor, ya que en los casos de una clave compuesta tal vez se requiera concatenar dos valores de campos diferentes. Si uno de los campos no es de tipo cadena, no hay problema, ya que se lo podrá convertir a cadena.

Relativa: La organización relativa se denomina así porque las posiciones que ocupan los registros dentro del archivo son direcciones relativas y no absolutas. Debido a que las posiciones de los registros del archivo se comienzan a contar desde el punto de inicio del archivo y no desde el punto de inicio del disco. La organización relativa está considerada como la organización de archivos con mayor velocidad de acceso a los registros generalmente, aunque no siempre será así, dependiendo de los distintos casos que se presenten, como se verá más adelante. Esta organización de archivo junto con la organización secuencial han sido las primeras organizaciones en los inicios de la informática, en este caso precisamente, porque se requería un acceso a los registros que no fuera necesariamente secuencial. En cambio la organización indexada surgió con posterioridad, hoy ampliamente utilizada esta última en las bases de datos. Para poder acceder a un registro n en una organización relativa debemos conocer el valor de una clave debiendo ser de tipo numérico, ya que las direcciones en el archivo son valores numéricas. No existe un archivo de índices como en la organización indexada, solo un archivo de datos. Este tipo de organización requiere la habilidad del programador para manejarlo adecuadamente. Esta habilidad estará emparentada con la experiencia lograda con los años de trabajo en diferentes proyectos que haya realizado en su vida profesional. No obstante, algunos detalles podrán mostrarse.

Existen dos tipos de direccionamiento en una organización relativa, a saber:

Direccionamiento directo: Es cuando se accede una sola vez al archivo a un registro direccionado, siendo ese el registro requerido.

Direccionamiento indirecto: Es cuando se accede más de una vez al archivo para localizar o establecer el valor de la clave en una ubicación en el archivo, debido a que ocurrieron colisiones.

A continuación se darán varios casos de estudio.

Caso 1: En una entrevista con un cliente, nos informa llevar el proceso de un archivo de artículos, máximo 100 y la forma de identificar a cada artículo, se combino en numerarlos de 1 a 100. Notamos aquí una relación 1 a 1 entre el código del artículo y la dirección que le debe corresponder en el archivo. Así conocida la clave de un artículo, digamos 23 le corresponde la dirección 23 en el archivo. Notamos entonces que el acceso a dicho registro tanto para leer como para grabar lo logramos con un solo acceso. Cuando se presenta esta situación el direccionamiento se denomina directo.

Caso 2: La situación con el cliente se presenta semejante al caso anterior pero los códigos de artículos deben ser entre 1001 y 1100. En este caso no podemos decir que conocida una clave con ese valor nos ubicamos en la dirección correspondiente en el archivo, ya que no existirán direcciones en el archivo con ninguno de esos valores claves. Pero no todo está perdido aún. Notamos enseguida que el intervalo de valores es igual a la cantidad de posibles artículos que podemos contar como máximo y que solo están desplazados 1000 posiciones, por lo tanto, si restamos ese valor a cualquier clave que conozcamos solucionamos el problema y obtendremos una dirección válida en el archivo, es decir, una dirección entre 1 y 100. Por ejemplo, si conocemos una clave cuyo valor fuera 1023, le restamos 1000, nos queda la dirección 23, siendo válida esta dirección para acceder en el archivo. Por lo tanto aplicando un cálculo a la clave hemos obtenido una dirección válida en el archivo. La relación 1 a 1 sigue existiendo para este caso, solo debemos realizar un cálculo para obtener una dirección válida en el archivo. El tipo de direccionamiento también es directo, ya que solo se requiere de un acceso para localizar un artículo.

Caso 3: La situación con el cliente se presenta semejante a los casos anteriores salvo que los códigos de artículos deben estar comprendidos entre 3427 y 7965. En este caso vemos que al igual del caso 2 con el valor de una clave no podemos ubicarnos en una dirección válida en el archivo, pero además notamos que el intervalo de valores de las claves es mucho mayor a la cantidad de artículos máximos que podemos tener. Por lo tanto esto trae aparejado un nuevo problema, el de las colisiones o sinónimos. Esto se produce cuando dos o más valores claves distintas generan una misma dirección en el archivo. Ahora bien, ¿cómo lograr obtener a partir de un valor clave una dirección válida en el archivo?. La técnica empleada se conoce como función de mapeo. Una función de mapeo ampliamente utilizada es la del método del resto, que consiste en tomar el valor clave k por un lado y el tamaño del archivo TA por otro, o bien por un número primo más cercano al tamaño del archivo; se realiza la división en donde k es el dividendo y TA o el número primo el divisor; el cociente entero es la dirección natural dn para acceder al archivo de datos. Si las direcciones en el archivo comenzaran desde uno en adelante, en estos casos se le suma 1 al resto.
Notamos que valores de claves distintas digamos ki distinto kj pueden ocasionar una misma dirección natural dn, produciéndose entonces una colisión o sinónimo. Este problema es solucionable existiendo distintos métodos para resolverlo. Uno de ellos sería buscar en los registros adyacentes un lugar libre y una vez localizado ubicar los datos allí, en caso de estar dando un alta. Si llegamos al final del archivo, debemos siguir buscando desde el inicio del mismo. Si lo que deseamos en buscar el valor de una clave primero accedemos a su dirección natural si es el dato que hay allí lo encontramos y listo, sino seguiremos buscando con el método empleado para solucionar colisiones, por ejemplo buscar en los registros adyacentes, así seguiremos hasta encontrarlo o bien detenernos en un punto en donde se asegura que ese valor clave no se encuentra en el archivo. Una solución a esto último sería agregar un campo adicional al registro que indique un estado del registro con un valor de V indicando que el registro está vacante, nunca fue utilizado por alguna clave, O indicando que el registro está ocupado, S indicando que el registro fue suprimido, es decir, se realizó una baja lógica. Al dar de alta si su dirección natural estuviera ocupada, la clave nueva se ubicará en el primer registro que esté como vacante o suprimido y se marca el estado con O de ocupado, siempre y cuando esa clave no estuviera en el archivo, sino sería alta existente. Al dar de baja una clave, si no es la de su dirección natural se seguirá buscando hasta que aparezca en el archivo, en ese caso se marca el estado con S de suprimido, siendo una baja lógica, o bien seguir buscando hasta que se encuentre un registro como vacante o se haya completado la vuelta, en estos casos sería una baja inexistente.
En este último caso el direcionamiento se denomina indirecto, debido a que encontrar una clave podrá necesitar realizar más de un acceso.

Caso 4: La situación con el cliente se presenta semejante a los casos anteriores salvo que los códigos de artículos son ahora alfanuméricos. En este caso vemos que al igual que en los casos 2 y 3 con el valor de una clave no podemos ubicarnos en una dirección válida en el archivo, pero además notamos que la clave no es numérica. ¿Qué podemos hacer en estos casos?. Pues bien debemos convertir la clave alfanumérica en numérica. Una forma de hacerlo sería tomar cada caracter de la clave y convertirla a su correspondiente código ASCII, al que luego le aplicamos una expresión algebraica, el resultado final será un número grande de tal manera que no será con seguridad una dirección válida en el archivo, pero no nos importa tanto esta situación, ya que el siguiente paso será aplicar una función de mapeo y que nos retorne una dirección válida en el archivo, como se mostrara en el caso 3.

Para utilizar archivos con organización relativa se requiere amplia experiencia, debido a que es la organización de archivos más compleja de todas porque requiere una exigencia por parte del programador en el diseño del archivo, las funciones de mapeo y la resolución de las colisiones.

Archivos en Pascal

El lenguaje Pascal clasifica a los archivos en:

  • Texto
  • Binarios con tipo
  • Binarios sin tipo



Los archivos de texto son exclusivamente de organización secuencial, debido a esto la única forma de accesar los registros es en forma secuencial y pueden ser abiertos de solo lectura o solo escritura, en ambos casos siempre se avanza hacia delante, no existiendo posibilidad de poder retroceder a una componente anterior. La única manera es cerrar el archivo y volver a abrirlo nuevamente.
Un archivo de tipo texto se lo define por medio del identificador text. Este es un archivo cuyas componentes esta estructurado en líneas, cada una de las cuales pueden tener diferentes longitudes. Cada línea está finalizada con una marca de fin de línea formado por la combinación del código ASCII 13 retorno de carro y el código ASCII 10 avance de línea y la función de Pascal eoln(NL) retorna un estado de verdadero si se leyó dicha marca. El fin de archivo está establecido por otra marca el código ASCII 26 o CTRL-Z y la función de Pascal eof(NL) retorna un estado de verdadero si se leyó dicha marca.
Los archivos de texto son más lentos que los archivos binarios ya que cada vez que se lea o grabe una componente los datos deberán ser convertidos de texto a binario o inversamente. Por ejemplo si tenemos un valor de tipo integer en la memoria interna al grabar en un archivo de texto se deberá convertir a caracteres 2 y 3; en cambio se se leyeran esos caracteres desde un archivo binario para volcarlo a la memoria interna deberá ser convertido al formato binario. Este proceso lo realiza automáticamente el proceso.
Un archivo de texto se lo puede crear por medio de un programa Pascal o bien con un editor de texto.
Si se tiene un archivo de texto se puede crear un archivo binario, simplemente leyendo del archivo de texto y grabar en el archivo binario.
Las lecturas de los datos desde un archivo de texto se realiza sobre variables del tipo de dato a leer y en forma separada.
La variable definida en la unidad Printer del Pascal lst es una variable de archivo de tipo text, es decir de tipo archivo de texto.

Los archivos binarios están estructurados en componentes denominadas registros. Si bien la forma más común es que las componentes de un archivo sean de tipo registro, hay otros casos, en que cuando cada componente sea un solo valor, hacerlas del mismo tipo de ese único valor, así por ejemplo, una componente podría ser de tipo integer, char, boolean, real, string, si cada componente fuera un solo valor en concordancia con algunos de los tipos indicados.
Se los denominan archivos binarios ya que reflejan la imagen de lo que hay en la memoria interna, es decir, no sufren ningún proceso de convertibilidad. Por ejemplo el valor entero 23 está representado en el archivo de la misma manera como se lo representa en la RAM, a saber como una serie de ceros y unos, que en este caso sería así 00000000 00010111 con 16 dígitos binarios, luego si editamos el archivo en un editor de archivos lo veremos con los caracteres cuyos códigos ASCII concuerdan con el ascii 0 y el ascii 23, vale decir, “↨”, las comillas es para limitar lo que se grabó.
Un archivo de tipo binario se lo define por medio de las palabras reservadas file of seguido de un identificador de tipo, es decir, file of tipo.
Un archivo binario solamente puede ser creado por medio de un programa en Pascal.
Si se tiene un archivo binario se lo puede crear como archivo de texto, simplemente leyendo desde el archivo binario y grabando en un archivo de texto.
Los datos a leer en un archivo binario se realizan en forma completa por cada componente.

A continuación se establece un tipo registro y un tipo archivo binario;

type
RegFec = record
aa : longint;
mm,
dd : byte
end;
Str20 = string[20];
RegAlu = record
NroLeg : longint;
ApeNom,
Domic,
Local : str20;
EstCiv : char;
Trabaja : boolean;
FecNac : RegFec;
Cuota : real
end;
ArcAlu = file of RegAlu;

var
Alumnos : ArcAlu;
RAlumno : RegAlu;

La variable Alumnos representa el nombre lógico NL del archivo.

Procedimientos y Funciones para archivos

En todos los casos indicados más abajo, las siguientes simbologías indican:

NL: Nombre lógico de archivo, es una variable de tipo archivo text, file of o file.
NF: Nombre físico de archivo, es una expresión de tipo cadena que contiene unidad de disco, ruta y nombre del archivo con el formato 8.3, según reglas del D.O.S.. Si se omiten la unidad de disco y la ruta, se toma el valor por defecto, esto es, la unidad y el directorio actualmente activo.
NR: Nombre de registro, es una variable de tipo registro que se corresponde con el tipo de cada componente del archivo asociado. En los casos en que las componentes del archivo sean de algún tipo simple de datos, NR podrá ser una variable del tipo de dato simple correspondiente con cada componente del archivo.

assign(NL, NF): Asigna o asocia el nombre físico con el nombre lógico, este último es el que se usará posteriormente cada vez que se refiera al archivo. El nombre físico podrá contener además del nombre y extensión, la unidad de disco y la ruta en donde se encuentra o destinará el archivo, si no se lo indica se tomará la unidad y directorio activo.
La impresora es un archivo de texto, por lo tanto se le puede asignar al nombre lógico un nombre físico siendo ese nombre físico LPT1 o LPT2 o LPT3.


Ejemplo:
assign(Alumnos,’Alumnos.Dat’);
assign(Impresora,’LPT1’); en donde; Impresora es una variable de tipo text.

reset(NL): Abre un archivo y ubica al puntero del archivo al comienzo del mismo. El archivo deberá existir, caso contrario generará un error en tiempo de ejecución y se detendrá la ejecución del programa, si no se ha atrapado dicho error por medio de la directiva al compilador {$I+}. Una vez abierto el archivo se podrá leer o grabar si el archivo es binario esto es, file of o file; y si la variable de sistema filemode estuviera con el valor dos; sólo se podrá leer, si estuviera con el valor cero o sólo se podrá grabar si estuviera con el valor uno; en cambio, si el archivo es de tipo text solo se podrá leer.

Ejemplo:
reset(Alumnos)

rewrite(NL): Abre un archivo nuevo y si existe lo destruye y lo crea vacío, en ambos casos ubica al puntero al archivo coincidiendo con eof(NL), es decir, con la marca de fin de archivo. El tamaño del archivo en este momento es cero. La variable de sistema filemode no se tomará en cuenta, ya que el modo de apertura para rewrite es siempre lectura y grabación. Un archivo abierto con rewrite primero se deben grabar algunos registros, luego posicionarnos sobre un registro previamente grabado, para luego poder leerlo, esto indica entonces que no hace falta tener que cerrar el archivo y abrirlo con reset para poder leerlo en el mismo proceso, solo basta hacer lo indicado anteriormente. Para un archivo de texto rewrite lo crea solo para grabar.

Ejemplo:
rewrite(Alumnos)

En archivo de texto existe una tercer posibilidad para abrir un archivo para agregar más líneas al mismo. La sentencia que abre un archivo sin destruirlo y que permite agregar más componentes al final del archivo es append(NL).
La impresora que es un archivo de salida se puede abrir con rewrite(NL), por ejemplo rewrite(Impresora);

Operaciones de lectura o grabación

read(NL, NR): Realiza la operación de lectura. El registro a leer es en donde se encuentra el puntero al archivo; “luego de la lectura, el puntero avanza a la próxima componente”. Al leer en un archivo binario se lee la componente completa, no hay posibilidad de leer una parte. La lectura de un registro en un archivo binario se realiza en forma directa desde el dispositivo de almacenamiento. Es decir, cada intento de leer se debe acceder al disco. En cambio en un archivo de texto se lee desde un buffer en la memoria interna, si hay algo para leer, sino, se accede al disco.
Intentar leer en una ubicación fuera del límite del archivo ocasionará en un error de lectura, indicado por el compilador pascal con el número 100. Estamos fuera de los límites del archivo cuando el puntero está más allá del tamaño del archivo, o bien cuando el puntero al archivo haya quedado posicionado antes de la dirección cero, hacer un intento de lectura en estos casos provocará el error anteriormente indicado.
En un archivo de texto readln luego de leer realiza un salto a la próxima línea.

Ejemplo:
read(Alumnos, rAlumno); rAlumno es la variable de tipo compatible con cada componente del tipo de archivo binario, no se podrá leer solamente una parte de dicha componente, es decir hacer read(Alumnos, rAlumnos.NroLeg) está mal.

write(NL, NR): Realiza la operación de grabación. El registro a grabar es en donde se encuentra el puntero al archivo en ese momento; “luego de la grabación, el puntero avanza a la próxima componente”. Al grabar en un archivo binario se graba la componente completa, no hay posibilidad de grabar una parte. La grabación en un archivo binario se realiza en forma directa hacia el dispositivo de almacenamiento. Es decir, por cada intento de grabar se debe acceder al disco. En cambio en un archivo de texto se graba hacia un buffer en la memoria interna, si se llena entonces es enviado al dispositivo externo, o sea, al disco. Si el puntero al archivo se encuentra sobre un registro existente, se actualizará con la información en el momento de realizar la grabación; en cambio si el puntero se encuentra coincidiendo con el fin de archivo, se genera un nuevo registro y el tamaño del archivo se expande una componente más. Ahora bien, ¿qué sucederá si se ubica el puntero al archivo mucho más allá de la marca de fin de archivo, digamos 5 posiciones más y se intenta grabar?. Se generan todos esos registros, solamente el registro que se graba tendrá valores conocidos, pero los registros intermedios contendrán basura, ya que no sabemos qué valores habrá allí, también en estos casos se expande el tamaño del archivo. Por lo tanto ubicar el puntero más allá del tamaño del archivo y grabar luego no generará ningún error en tiempo de ejecución. Pero, ¿qué sucederá si ubicamos el puntero al archivo antes de la primer componente, es decir, antes de la ubicación cero?, intentar grabar producirá un error en tiempo de ejecución.
Ejemplo:
write(Alumnos, rAlumnos); rAlumno es la variable de tipo compatible con cada componente del tipo de archivo binario, no se podrá grabar solamente una parte de dicha componente, es decir hacer write(Alumnos, rAlumnos.NroLeg) está mal.
En un archivo de texto writeln luego de grabar salta al comienzo de la próxima línea.
La impresora es un archivo de texto al cual podremos enviar informacón, el formato es write(NL, exp) o writeln(NL, exp). En el primer caso imprime el resultado de la expresión y permanece en la misma línea, en cambio en el segundo caso luego de imprimir avanza a la próxima línea.

Ejemplos:

write(Impresora, a,cad,’Mensaje: ‘,titulo,3.24:8:2);
write(lst,’Como estás ‘,Nom); en donde lst es una variable de tipo text definida en la unidad Printer, al cual debemos indicar en nuestro programa en la sección uses.
writeln(Impresora,’El total es: ‘,totVen:8:2);

Resumiendo: hacer read o write cuando el puntero está en una posición menor a cero está mal y generará un error en tiempo de ejecución; por otro lado si el puntero al archivo se encuentra más allá del tamaño del archivo, hacer read está mal, pero hacer write no es error.

close(NL): Cierra el archivo indicado en su argumento. Esta operación deberá realizarse cuando ya no debamos operar más con el archivo. Con los archivos de texto esta acción es muy importante ya que si no cerráramos el archivo perderíamos parte de la información debido a que si el buffer no se llenó, lo que quedó allí no sería enviado al disco. En cambio si cerramos el archivo, close antes de cerrarlo verificaría si hay algo pendiente de enviar desde el buffer, si es así se forzaría primero a enviar lo que hay allí al disco y luego se cerrará el archivo. En un archivo binario al no trabajar con el buffer esta situación no existe, no obstante, es aconsejable cerrar igualmente el archivo.
La impresora como archivo de texto debe ser cerrada, pero no si usamos la variable lst de la unidad printer, ya que la unidad se encarga de cerrarla.

Ejemplo:
close(Alumnos);
close(Impresora);

eof(NL): Es una función que retorna un valor de verdadero si el puntero está en fin de archivo, lo mismo si se encuentra en una posición menor a cero; caso contrario retorna falso. Esta función es común emplearla cuando leemos secuencialmente en un archivo, como condición en la cabecera de un ciclo indefinido, negando el valor retornado.
Ejemplo:

while not eof(Alumnos) do begin
read(Alumnos, rAlumno);
...
end;

filesize(NL): Función que retorna un valor longint que indica el tamaño del archivo en cantidad de registros.
Ejemplos:
writeln(fileSize(Alumnos);
for i:= 1 to fileSize(Alumnos) do begin
read(Alumnos, rAlumno);
...
end;

filepos(NL): Función que retorna un valor longint que indica la posición en que se encuentra el puntero en el archivo indicado por NL.

Ejemplo:
writeln(filePos(Alumnos));

seek(NL, dir): Ubica al puntero del archivo indicado por NL, en la dirección indicada por dir. El segundo parámetro puede ser una expresión. Se podrá ubicar el puntero dentro o fuera del tamaño del archivo, sin generar ningún error, inclusive antes de la posición cero.

Ejemplo:
seek(Alumnos, 3);
seek(Alumnos, fileSize(Alumnos)); la ubicación nueva coincide con eof.
seek(Alumnos, filepos(Alumnos));
seek(Alumnos, fileSize(Alumnos) – 1); la ubicación nueva coincide con el último
registro existente en el archivo.

truncate(NL): Sentencia que trunca o corta el archivo a partir en donde se encuentre el puntero al archivo. Actualiza el tamaño del archivo.
Si tomamos un archivo que contiene 15 registros y cambiamos el puntero al archivo a la ubicación 7, seek(Alumnos, 7); y luego hacemos truncate(Alumnos); se eliminarán todos los registros localizados desde la ubicación 7 hasta la ubicación 14 inclusives, es decir, ahora el tamaño del archivo se comprimió a 7 registros, desde la posición cero hasta la posición seis.

rename(NL, NVONF): Permite renombrar el archivo indicado por NL con otro nuevo nombre físico, la actualización se realiza en el directorio del disco.
Ejemplo:
rename(Alumnos, ‘Student.Dat’);

erase(NL): Elimina o borra el archivo indicado por NL, del directorio en el disco.

Ejemplo:
erase(Alumnos);

Archivo binario con 12 registros con direcciones [0; 11]



Si el puntero al archivo está ubicado en la marca de eof, realizar una operación de lectura producirá un error en tiempo de ejecución y se aborta el programa, lo mismo ocurrirá si está más allá de la marca de eof, pero grabar esta bién y se expande el tamaño del archivo una componente más y la marca se reubica después del registro grabado, coincidiendo con el puntero al archivo.
Si el puntero al archivo está ubicado en una dirección más alejada digamos 15 y se graba un nuevo registro, también se graban los registros 12 a 14 pero su contenido será desconocido.
Si el puntero al archivo está ubicado en una dirección menor a 0 realizar una operación de lectura o escritura producirá un error en tiempo de ejecución y se aborta el programa.
La función eof(f) retornará verdadero si el puntero al archivo se encuentra fuera de los límites del archivo, esto es, si está más allá de la marca eof o si se movió el puntero al archivo a una dirección menor a 0.


[1] IRD significa: Izquierda, Raíz, Derecha. RID significa: Raíz, Izquierda, Derecha. IDR significa: Izquierda, Derecha, raíz.








Anterior: Lenguaje Turbo Pascal

Siguiente: Archivos II

Arriba

1 comentario:

lalisaiacono dijo...

Microtouch Titanium trim reviews - TitaniumArts
Read more about our review, which is also the best way for titanium hair trimmer as seen on tv you graphite titanium babyliss pro to see what the different microtouch titanium components titanium exhaust tubing are in microtouch titanium knee replacement titanium trim.