• No se han encontrado resultados

Entrada y salida de archivos

N/A
N/A
Protected

Academic year: 2021

Share "Entrada y salida de archivos"

Copied!
29
0
0

Texto completo

(1)

Entrada y salida de archivos

H. Tejeda Abril 2016

´ Indice

1. Introducci´on 1

2. Clases Path y Files 3

3. Organizaci´on de archivos, flujos, y b´ufers de datos 10

4. Clases entrada/salida 12

5. Archivos secuenciales de datos 17

6. Archivos de acceso aleatorio 21

7. Escritura de registros en archivos de acceso aleatorio 23

8. Lectura de registros en archivos de acceso aleatorio 27

Se da una introducci´on de como se guarda la informaci´on en un sistema de c´omputo, y los medios que este tiene para llevarlo a cabo. Despu´es se comentan algunas clases que pueden ser usadas para realizar algunas tareas con archivos.

1. Introducci´ on

Los elementos de datos pueden ser guardados en dos tipos de dispositivos de almacenamiento en un sistema de c´omputo:

Almacenamiento vol´atil es temporal; valores que son volatiles, como los que son guardados en variables, se pierden cuando la computadora deja de ser energizada. RAM (random

(2)

access memory) es el almacenamiento temporal dentro de una computadora. Tambi´en se le conoce como memoria de almacenamiento vol´atil.

Almacenamiento no vol´atil es almacenamiento permanente; no se pierde al desenergizar la computadora. Cuando se escribe un programa Java y se guarda en el disco, se est´a usando almacenamiento permanente.

Un archivo de computadora es una colecci´on de datos guardados en un dispositivo no vol´atil.

El archivo existe en dispositivos de almacenamiento permanente, tales como discos duros, discos ZIP, memorias USB, carretes de cinta magn´etica, discos compactos, etc.

Se pueden clasificar los archivos por la forma como estos guardan los datos:

Archivos de texto contienen datos que pueden ser le´ıdos por un editor de texto porque los datos han sido codificados usando un esquema como ASCII o Unicode. Algunos archivos de texto son archivos de datos como uno que tenga una n´omina con n´umero de empleado, nombre y salario; otros archivos son archivos de programa o archivos de aplicaci´on que guardan instrucciones de software.

Archivos binarios contienen datos que no han sido codificados como texto. Ejemplos de estos archivos son im´agenes, m´usica, archivos de programa compilados.

Los archivos tienen muchas caracter´ısticas com´unes—cada archivo tiene un tama˜no que indica el espacio que ocupa en una secci´on del disco u otro dispositivo de almacenamiento, y cada archivo tiene un nombre y un tiempo espec´ıfico de creaci´on.

Cuando se guarda un archivo permanente, este se puede poner en el directorio principal o ra´ız del dispositivo de almacenamiento. Para una mejor organizaci´on los sistemas de c´omputo organizan sus archivos en carpetas o directorios. Se pueden crear directorios dentro de otros para formar una jerarqu´ıa. La jerarqu´ıa de directorios en el cual reside un archivo es su camino. Por ejemplo, el siguiente es el camino completo para un archivo de Linux llamado nano, el cual est´a en el directorio bin, y este a su vez dentro de usr:

/usr/bin/nano

En el sistema operativo de las ventanas, la diagonal invertida (\) es el delimitador de camino.

En los sistemas operativos UNIX o variantes se usa una diagonal (/) como delimitador.

Cuando se trabaja con archivos en una aplicaci´on, se realizan con estos las siguientes tareas:

Determinar si un camino o archivo existe.

Abrir un archivo.

Escribir a un archivo.

Leer desde un archivo.

(3)

Cerrar un archivo.

Borrar un archivo.

Java da clases incorporadas que contienen m´etodos para realizar estas tareas.

2. Clases Path y Files

Se usa la clase Path para crear objetos que contengan informaci´on acerca de archivos y directorios, tal como su lugar, tama˜no, fecha de creaci´on, y si existen. Se usa la clase Files para realizar operaciones en archivos y directorios, como borrar, conocer sus atributos, creaci´on de flujos de entrada y salida.

Para usar las clases Path y Files se puede usar la siguiente sentencia al inicio del programa:

import java.nio.file.*;

El paquete nio (new input/output ) agrupa las clases desarrolladas bastante despu´es de las primeras versiones de Java. Las clases Path y Files son nuevas en Java 7; reemplazan la funcionalidad de la clase File usada en versiones m´as viejas de Java.

Crear un camino

Para crear un camino, primero se debe determinar el sistema de archivos por defecto en la compu- tadora anfitri´on usando una sentencia como la siguiente:

FileSystem fs = FileSystems.getDefault();

La sentencia crea un objeto FileSystem usando el m´etodo getDefault() de la clase FileSystems, se est´an usando dos clases diferentes. La clase FileSystem se usa para declarar el objeto y FileSys- tems es una clase que contiene m´etodos de f´abrica que ayudan en la creaci´on del objeto.

Despu´es de crear un objeto FileSystem, se puede definir una Path (ruta) usando el m´etodo getPath():

Path ruta = fs.getPath("/usr/bin/nano");

Si se tiene que usar la diagonal invertida (backslash) para construir la ruta esta deber´a escaparse en la sentencia donde se use. Otra forma de hacerlo ser´ıa concatenar cadenas con los nombres de directorios y archivos e intercalar llamadas al m´etodo de instancia getSeparator() con un objeto FileSystem:

Path rutaArchivo = fs.getPath(fs.getSeparator()+"usr"+fs.getSeparator()+

"bin"+fs.getSeparator()+nano);

(4)

Otra forma para crear un Path es usar la clase Paths. La clase Paths es una clase de ayuda que evita crea un objeto FileSystem. El m´etodo get() de Paths llama al m´etodo getPath() del sistema de archivos. Se puede crear un objeto Path usando la siguiente sentencia:

Path rutaArchivo = Paths.get("/usr/bin/nano");

Cuando una aplicaci´on declara un camino y se quiere usar la aplicaci´on con un archivo diferente, se podr´ıa cambiar solo el String pasado al m´etodo de instanciaci´on.

Cada Path es absoluto o relativo. Un camino absoluto es un camino completo; no necesita otra informaci´on para localizar un archivo. Un camino relativo depende del directorio de trabajo para la localizaci´on. Una ruta simple como ArchivoEjemplo.txt es relativo. Cuando se trabaja con un camino que contiene s´olo una archivo, se asume que el archivo est´a en el mismo directorio. De igual forma cuando se refiere a un camino relativo como datos/ArchivoEjemplo.txt, se asume que el directorio datos es un subdirectorio del directorio actual, y ArchivoEjemplo.txt se asume que est´a dentro de ese folder.

Recuperaci´on de informaci´on acerca de un camino

En el cuadro 1 se resumen varios m´etodos Path ´utiles. El m´etodo toString() de la clase Object es anulado; haciendo que devuelva un String de la representaci´on de Path. El m´etodo getFileName() devuelve la ´ultima sentencia en una lista de caminos; la mayor´ıa de las veces es un archivo, pero podr´ıa ser el nommbre de una carpeta.

M´etodo Descripci´on

String toString() Devuelve la representaci´on String del Path.

Path getFileName() Regresa el archivo o directorio dado por este Path; este es el

´

ultimo elemento en la secuencia de elementos nombre.

int getNameCount() Devuelve la cantidad de elementos nombre en el Path.

Path getName(int) Regresa el nombre en la posici´on del Path indicado por el par´ame- tro entero.

Cuadro 1: Algunos m´etodos de la clase Path

Un elemento de Path es accedido por un ´ındice. El elemento de la cima en la estructura directorio est´a localizado en el ´ındice cero; el elemento m´as bajo en la estructura es accedido por el m´etodo getName() y tiene un ´ındice que es uno menos que la cantidad de elementos en la lista. Para saber la cantidad de elementos en la lista usar el m´etodo getNameCount() y para recuperar el nombre en la posici´on indicada por el argumento usar getNameCount.

En el c´odigo 1 se tiene un programa que crea un Path y usa algunos de los m´etodos del cuadro 1.

(5)

1 import j a v a . n i o . f i l e . ∗ ;

2 public c l a s s DemoPath {

3 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

4 Path caminoArch = Paths . g e t ( ” / e t c / passwd ” ) ;

5 i n t c o n t a d o r = caminoArch . getNameCount ( ) ;

6 System . o ut . p r i n t l n ( ” El camino e s ” + caminoArch . t o S t r i n g ( ) ) ;

7 System . o ut . p r i n t l n ( ” El nombre d e l a r c h i v o e s ” +

8 caminoArch . getName ( c o n t a d o r − 1 ) ) ;

9 System . o ut . p r i n t l n ( ”Hay ” + c o n t a d o r +

10 ” e l e m e n t o s en e l camino d e l a r c h i v o ” ) ;

11 f o r ( i n t x = 0 ; x < c o n t a d o r ; ++x )

12 System . o ut . p r i n t l n ( ” \ t El em e nt o ” + x + ” e s ” +

13 caminoArch . getName ( x ) ) ;

14 }

15 }

C´odigo 1: La clase DemoPath.

Al ejecutar el programa DemoPath usando el archivo /etc/passwd se obtiene la siguiente salida:

$ java DemoPath

El camino es /etc/passwd

El nombre del archivo es passwd

Hay 2 elementos en el camino del archivo Elemento 0 es etc

Elemento 1 es passwd

Conversi´on de un camino relativo a absoluto

El m´etodo toAbsolutePath() convierte un camino relativo a uno absoluto. El c´odigo 2 muestra una aplicaci´on que pide al usuario un nombre de archivo y, si se puede, lo convierte a un camino absoluto.

1 import j a v a . u t i l . S c a n n e r ;

2 import j a v a . n i o . f i l e . ∗ ;

3 public c l a s s DemoPath2 {

4 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

5 S t r i n g nombre ;

6 S c a n n e r t e c l a d o = new S c a n n e r ( System . i n ) ;

7 System . o ut . p r i n t ( ” I n g r e s a r e l nombre de un a r c h i v o >> ” ) ;

8 nombre = t e c l a d o . n e x t L i n e ( ) ;

9 Path caminoDado = Paths . g e t ( nombre ) ;

10 Path caminoCompleto = caminoDado . t o A b s o l u t e P a t h ( ) ;

11 System . o ut . p r i n t l n ( ” El camino c o m p l e t o e s ” +

12 caminoCompleto . t o S t r i n g ( ) ) ;

13 }

14 }

C´odigo 2: La clase DemoPath2.

Cuando se ejecuta la aplicaci´on DemoPath2, el nombre ingresado podr´ıa representar un camino

(6)

relativo. Si la entrada representa un camino absoluto, no se modifica la entrada, caso contrario el programa crea un camino absoluto asignando al archivo el directorio actual. Enseguida se muestra una ejecuci´on del programa.

$ java DemoPath2

Ingresar el nombre de un archivo >> DemoPath.java El camino completo es /home/fismat/DemoPath.java

Revisar accesibilidad de un archivo

Para saber si un archivo existe y que el programa pueda accederlo como se requiera, se puede usar el m´etodo checkAccess(). La siguiente sentencia import permite el acceso a constantes que pueden ser usadas como argumentos al m´etodo:

import static java.nio.file.AccessMode.*;

Mediante un objeto Path, referido por archivo, la sintaxis para usar el m´etodo checkAccess() es la siguiente:

archivo.getFileSystem().provider().checkAccess();

Cualquiera de los siguientes argumentos puede ser usado con el m´etodo checkAccess():

Sin argumentos—Revisa que el archivo exista; como una alternativa, se puede sustituir el m´etodo Files.exists() y pasarle un argumento Path.

READ—Revisa que el archivo exista y que tenga permiso de lectura.

WRITE—Revisa que el archivo exista y que tenga permiso de escritura.

EXECUTE—Revisa que el archivo exista y que tenga permiso de ejecuci´on.

Nota. La caracter´ıstica static import de Java se logra cuando se pone la palabra reservada static entre import y el nombre del paquete siendo importado. Esta caracter´ıstica permite usar constantes static sin el nombre nombre de su clase. Por ejemplo, si se quita static de la sentencia import para java.nio.file.AccessMode, se deber´a referir a la constante READ por su nombre completo como AccessMode.READ; cuando se usa static, se puede referir a esta s´olo como READ.

Se pueden usar argumentos m´ultiples con el m´etodo checkAccess(), separados por comas. Si el archivo no puede ser accedido como se indica en la llamada del m´etodo, una IOException es lanzada. El c´odigo 3 muestra una aplicaci´on que declara un Path y revisa si el archivo puede ser le´ıdo y ejecutado. En la l´ınea 3 del c´odigo 3, se debe importar el paquete java.io.IOException porque una excepci´on podr´ıa ser instanciada y lanzada.

(7)

1 import j a v a . n i o . f i l e . ∗ ;

2 import s t a t i c j a v a . n i o . f i l e . AccessMode . ∗ ;

3 import j a v a . i o . I O E x c e p t i o n ;

4 public c l a s s DemoPath3 {

5 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

6 Path a r c h i v o = Paths . g e t ( ” / e t c / passwd ” ) ;

7 System . o ut . p r i n t l n ( ” Path e s ” + a r c h i v o . t o S t r i n g ( ) ) ;

8 try {

9 a r c h i v o . g e t F i l e S y s t e m ( ) . p r o v i d e r ( ) . c h e c k A c c e s s ( a r c h i v o ,READ,EXECUTE) ;

10 System . o ut . p r i n t l n ( ” El a r c h i v o puede s e r l e´ıdo y e j e c u t a d o ” ) ;

11 }

12 catch ( I O E x c e p t i o n e ) {

13 System . o ut . p r i n t l n ( ” El a r c h i v o no puede s e r usado po r l a a p l i c a c i ´on” ) ;

14 }

15 }

16 }

C´odigo 3: La clase DemoPath3.

A continuaci´on se muestra la salida del c´odigo 3 al ser ejecutado.

$ java DemoPath3 Path es /etc/passwd

El archivo no puede ser usado por la aplicaci´on

Nota. Un programa podr´ıa encontrar a un archivo utilizable, pero luego el archivo podr´ıa hacerse no utilizable antes de ser usado en una sentencia siguiente. Este tipo de error es llamado error TOCTTOU (Time Of Check To Time Of Use).

Borrar un camino

El m´etodo delete() de la clase Files acepta un par´ametro Path y borra el ´ultimo elemento (archivo o directorio) en un camino o lanza una excepci´on si falla el borrado. Por ejemplo:

Si se intenta borrar un archivo que no existe, una NoSuchFileException es lanzada.

Un directorio se puede borrar cuando est´a vac´ıo. Si se intenta borrar un directorio que contiene archivos, una DirectoryNotEmptyException es lanzada.

Si se intenta borrar un archivo pero no se tiene permiso, una SecurityException es lanzada.

Otros errores de entrada/salida provocan una IOException.

El c´odigo 4 muestra un programa que muestra un mensaje apropiado en cada uno de los escenarios precedentes despu´es de intentar borrar un archivo.

(8)

1 import j a v a . n i o . f i l e . ∗ ;

2 import j a v a . i o . I O E x c e p t i o n ;

3 public c l a s s DemoPath4 {

4 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

5 Path ca m in o A rc h iv o = Paths . g e t ( ” / e t c / passwd ” ) ;

6 try {

7 F i l e s . d e l e t e ( ca m in o Ar c h iv o ) ;

8 System . o ut . p r i n t l n ( ” El a r c h i v o o d i r e c t o r i o e s t ´a b o r r a d o ” ) ;

9 }

10 catch ( N o S u c h F i l e E x c e p t i o n e ) {

11 System . o ut . p r i n t l n ( ”No e x i s t e e l a r c h i v o o d i r e c t o r i o ” ) ;

12 }

13 catch ( D i r e c t o r y N o t E m p t y E x c e p t i o n e ) {

14 System . o ut . p r i n t l n ( ” El d i r e c t o r i o no e s t ´a vac´ıo ” ) ;

15 }

16 catch ( S e c u r i t y E x c e p t i o n e ) {

17 System . o ut . p r i n t l n ( ”No t i e n e p e r m i s o p a r a b o r r a r ” ) ;

18 }

19 catch ( I O E x c e p t i o n e ) {

20 System . o ut . p r i n t l n ( ” E x c e p t i o n IO” ) ;

21 }

22 }

23 }

C´odigo 4: La clase DemoPath4.

El m´etodo deleteIfExists() de la clase Files tambi´en puede ser usado para borrar un archivo, pero si el archivo no existe, no se lanza excepci´on.

Determinar atributos de archivo

El m´etodo readAttributes() de la clase Files se puede usar para recuperar informaci´on ´util de un archivo. Se deben dar dos argumentos al m´etodo, un objeto Path y BasicFileAttributes.class, y devuelve una instancia de la clase BasicFileAttributes. Se podr´ıa crear una instancia con una sentencia como la siguiente:

BasicFileAttributes atrib =

Files.readAttributes(caminoArchivo, BasicFileAttributes.class);

Despu´es de crear un objeto BasicFileAttributes, se dispone de un conjunto de m´etodos para recuperar informaci´on del archivo. Como, el m´etodo size() que da el tama˜no del archivo en bytes, los m´etodos creationTime() y lastModifiedTime() que dan fechas del archivo. El c´odigo 5 contiene un programa que usa estos m´etodos.

(9)

1 import j a v a . n i o . f i l e . ∗ ;

2 import j a v a . n i o . f i l e . a t t r i b u t e . ∗ ;

3 import j a v a . i o . I O E x c e p t i o n ;

4 public c l a s s DemoPath5 {

5 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

6 Path ca m in o A rc h iv o = Paths . g e t ( ” / e t c / passwd ” ) ;

7 try {

8 B a s i c F i l e A t t r i b u t e s a t r i b =

9 F i l e s . r e a d A t t r i b u t e s ( caminoArchivo , B a s i c F i l e A t t r i b u t e s . c l a s s ) ;

10 System . o ut . p r i n t l n ( ” Fecha de c r e a c i ´on : ” + a t r i b . c r e a t i o n T i m e ( ) ) ;

11 System . o ut . p r i n t l n ( ” ´U l t i m a m o d i f i c a c i ´on : ” +

12 a t r i b . l a s t M o d i f i e d T i m e ( ) ) ;

13 System . o ut . p r i n t l n ( ” Tama˜no : ” +a t r i b . s i z e ()+ ” b y t e s ” ) ;

14 }

15 catch ( I O E x c e p t i o n e ) {

16 System . o ut . p r i n t l n ( ” E x c e p c i ´on E/S” ) ;

17 }

18 }

19 }

C´odigo 5: La clase DemoPath5.

Los m´etodos de fecha en el programa DemoPath5 devuelven un objeto FileTime que es convertido a una String en la llamada al m´etodo println(). Los objetos FileTime son representados en el siguiente formato:

aaaa-mm-ddThh:mm:ss

El formato descrito se puede ver en la siguiente salida:

$ java DemoPath5

Fecha de creaci´on: 2013-10-23T15:16:20Z

´Ultima modificaci´on: 2013-10-23T15:16:20Z Tama~no: 5253 bytes

Para determinar la relaci´on de fechas entre dos archivos, se puede usar el m´etodo compareTo(). En el c´odigo 6 se muestra como se podr´ıan comparar las fechas de creaci´on de dos archivos. El m´etodo compareTo() regresa un valor menor que cero si el primer FileTime est´a antes que el argumento de FileTime. Regresar´a el m´etodo un valor mayor que cero si el primer FileTime es posterior al del argumento y cero si los valores de FileTime son los mismos.

(10)

1 import j a v a . n i o . f i l e . ∗ ;

2 import j a v a . n i o . f i l e . a t t r i b u t e . ∗ ;

3 import j a v a . i o . I O E x c e p t i o n ;

4 public c l a s s DemoPath6 {

5 public s t a t i c void ma i n ( S t r i n g [ ] a r g s ) {

6 Path a r c h 1 = Paths . g e t ( ” / e t c / passwd ” ) ;

7 Path a r c h 2 = Paths . g e t ( ” / e t c / h o s t s ” ) ;

8 try {

9 B a s i c F i l e A t t r i b u t e s a t t r 1 =

10 F i l e s . r e a d A t t r i b u t e s ( arch1 , B a s i c F i l e A t t r i b u t e s . c l a s s ) ;

11 B a s i c F i l e A t t r i b u t e s a t t r 2 =

12 F i l e s . r e a d A t t r i b u t e s ( arch2 , B a s i c F i l e A t t r i b u t e s . c l a s s ) ;

13 F i l e T i m e t i m e 1 = a t t r 1 . c r e a t i o n T i m e ( ) ;

14 F i l e T i m e t i m e 2 = a t t r 2 . c r e a t i o n T i m e ( ) ;

15 System . o ut . p r i n t l n ( ”La f e c h a de c r e a c i ´on de a r c h 1 e s : ” + t i m e 1 ) ;

16 System . o ut . p r i n t l n ( ”La f e c h a de c r e a c i ´on de a r c h 2 e s : ” + t i m e 2 ) ;

17 i f ( t i m e 1 . compareTo ( t i m e 2 ) < 0 )

18 System . o ut . p r i n t l n ( ” a r c h 1 f u e c r e a d o a n t e s que a r c h 2 ” ) ;

19 e l s e i f ( t i m e 1 . compareTo ( t i m e 2 ) > 0 )

20 System . o ut . p r i n t l n ( ” a r c h 1 f u e c r e a d o despu´e s que a r c h 2 ” ) ;

21 e l s e

22 System . o ut . p r i n t l n ( ” a r c h 1 y a r c h 2 f u e r o n c r e a d o s a l mismo tiempo ” ) ;

23 }

24 catch ( I O E x c e p t i o n e ) {

25 System . o ut . p r i n t l n ( ” E x c e p c i ´on E/S” ) ;

26 }

27 }

28 }

C´odigo 6: La clase DemoPath6.

Nota. Java soporta clases especializadas para atributos de archivos DOS y de Windows, como oculto, o s´olo lectura y atributos de archivos POSIX usados en sistemas como UNIX, donde los archivos tiene due˜no de grupo.

3. Organizaci´ on de archivos, flujos, y b´ ufers de datos

Se pueden guardar datos en variables dentro de un programa, pero es temporal el almacenamiento.

Por otra parte, cuando la aplicaci´on termina, las variables dejan de existir y los valores de datos se pierden. Las variables son guardadas en la memoria principal o primaria (RAM) de la computadora.

Para retener datos por alguna cantidad significativa de tiempo, se deben guardar los datos en un dispositivo de almacenamiento permanente, secundario, como un disco.

La pieza ´util m´as peque˜na de datos para la mayor´ıa de los usuarios es el car´acter. Un car´acter puede ser cualquier letra, n´umero, o s´ımbolos especiales (como signos de puntuaci´on) que forman los datos. Los caracteres est´an formados con bits (los ceros y los unos que representan los valores l´ogicos usados por el sistema de c´omputo), pero los usuarios no les importa si la representaci´on de alg´un car´acter es hecha con alguna combinaci´on de ceros y unos, o con alguna otra. Los usuarios est´an interesados con el significado que pueda tener alg´un car´acter, trat´andose de alguna letra

(11)

esta podr´ıa ser una inicial, alg´un c´odigo, etc. Un car´acter podr´ıa ser cualquier grupo de bits y no necesariamente representar una letra o un n´umero; por ejemplo, algunos “caracteres” producen un sonido o controlan el monitor. Tambi´en, los caracteres no son necesariamente creados con una sola tecla; por ejemplo, las secuencias escapadas son usadas para crea el car´acter ’\n’, el cual inicia una nueva l´ınea. En ocasiones, se puede pensar en un car´acter como una unidad de informaci´on en vez de un dato con una apariencia particular. Por ejemplo, el car´acter matem´atico π y la letra griega pi parecen iguales, pero tienen dos valores Unicode diferentes.

En las organizaciones que usan datos, estas agrupan los caracteres en campos. Un campo es un grupo de caracteres que tienen alg´un significado. Los caracteres J, u, a y n podr´ıan representar el nombre. Otros campos de datos podr´ıan representar elementos como los apellidos, n´umero de seguridad social, c´odigo postal, o salario.

Los campos est´an agrupados para formar registros. Un registro es una colecci´on de campos que contienen datos acerca de una entidad. Por ejemplo, el nombre, los apellidos, el n´umero de seguridad social, el c´odigo postal y el salario representan un registro de una persona. En las clases, vistas previamente, como Empleado o Estudiante, se pueden pensar que los datos guardados en cada una de estas son un registro. Estas clases contienen variables individuales que representan los campos de datos.

Los registros son agrupados para crear archivos. Archivos de datos estructurados consisten de registros relacionados entre ellos, tal como un archivo del personal de una empresa que contiene un registro por cada empleado. La cantidad de registros de un archivo puede ser de unos cuantos registros hasta de millones. Un archivo de datos puede ser usado como un archivo de acceso secuencial cuando cada registro es accedido uno despu´es de otro en el orden en el cual fueron guardados. La mayor´ıa de las veces, cada registro es guardado en orden usando el valor de alg´un campo, por ejemplo por el n´umero de seguridad social. Cuando los registros no son usados en secuencia, el archivo es usado como archivo de acceso aleatorio.

Cuando los registros est´an guardados en archivo de datos, sus campos pueden estar organizados en una l´ınea, o un car´acter puede ser usado para separarlos. Un archivo de valores separados por coma o CSV es uno en el cual cada valor en un registro est´a separado del siguiente por una coma.

Este formato es empleado en bases de datos y hojas de c´alculo.

Antes de que una aplicaci´on pueda usar un archivo de datos, esta deber´a abrir el archivo. Una aplicaci´on Java abre un archivo creando un objeto y asoci´andole un flujo de bytes. De igual manera, cuando se termina de usar un archivo, el programa deber´a cerrar el archivo para que ya no siga disponible para la aplicaci´on. Si se falla al cerrar un archivo de entrada, uno en el cual se est´an leyendo datos, usualmente no hay consecuencias; los datos todav´ıa existir´an en el archivo. Pero si se falla al cerrar un archivo de salida, uno en el cual se est´an escribiendo datos, los datos podr´ıan hacerse inaccesibles. Siempre se deber´a cerrar cada archivo que se abra, y se deber´a cerrar tan pronto como ya no se ocupe. Cuando se deja un archivo abierto sin raz´on, este consume recursos del sistema, y el rendimiento del sistema decrece. Podr´ıa ser tambi´en que otro programa este esperando para usar el archivo, como en una red de computadoras.

Java ve un archivo de registros como una serie de bytes. Cuando se realiza una operaci´on de entrada en una aplicaci´on, esto se puede ver como bytes yendo en el programa desde un dispositivo de entrada a trav´es de un flujo o stream, el cual funciona como una tuber´ıa o canal. Cuando se realiza salida, algunos bytes salen fuera de la aplicaci´on a trav´es de otro flujo a un dispositivo de salida. Un flujo es un objeto, y al igual que otros objetos, los flujos tienen datos y m´etodos. Los

(12)

m´etodos permiten realizar acciones como abrir, cerrar, leer, y escribir.

La mayor´ıa de los flujos van en una sola direcci´on, cada flujo es de entrada o salida. Se podr´ıan abrir varios flujos simult´aneamente en una aplicaci´on.

Las operaciones de entrada y salida son las m´as lentas en cualquier sistema de c´omputo por las limitaciones impuestas por el hardware. Por esa raz´on, los programadores profesionales frecuente- mente usan b´ufers. Un b´ufer es una localidad de memoria donde los bytes est´an guardados despu´es de ser l´ogicamente escritos pero antes de ser enviados al dispositivo. Usar un b´ufer para acumular entrada o salida antes de realizar el comando de E/S mejora el rendimiento del programa. Cuando se usa un b´ufer de salida, en ocasiones se vac´ıa antes de cerrarlo. El vaciar un b´ufer de memoria hace que se limpie cualquier byte que haya sido enviado a un b´ufer para salida pero que todav´ıa no ha sido sacado a un dispositivo de hardware.

4. Clases entrada/salida

Enseguida se muestra una relaci´on jer´arquica parcial de algunas de las clases Java usadas para operaciones de entrada y salida (E/S); este muestra que InputStream, OutputStream, y Reader son subclases de la clase Object. Estas tres subclases son abstractas teniendo m´etodos que deben ser anuladas en sus clases hijas. Las capacidades de estas clases est´an resumidas en cuadro 2.

Object

|

+--InputStream

| |

| +--FileInputStream

| |

| +--FilterInputStream

| |

| +--BufferedInputStream

|

+--OutputStream

| |

| +--FileOutputStream

| |

| +--FilterOutputStream

| |

| +--BufferedOutputStream

| |

| +--PrintStream

|

+--Reader

|

+--BufferedReder

|

+--BufferedWriter

(13)

Clase Descripci´on

InputStream Clase abstracta que contiene m´etodos para realizar entrada.

FileInputStream Hija de InputStream que tiene capacidad para leer de los archi- vos de disco.

FilterInputStream Contiene algunos otros flujos de entrada, los cuales son usados como la fuente base de datos, posiblemente transformando los datos, o agregando funcionalidad adicional.

BufferedInputStream Hija de FilterInputStream, que a su vez es hija de InputStream; BufferedInputStream maneja la entrada del dis- positivo de entrada est´andar (o defecto) de un sistema, general- mente el teclado.

OutputSream Clase abstracta que contiene m´etodos para realizar la salida.

FileOutputStream Hija de OutputStream que permite escribir a archivos de disco.

FilterOutputStream Es la superclase de todas las clases que filtran los flujos de salida.

Estos flujos est´an en la cima de alg´un flujo de salida ya existente (el flujo de salida subyacente) el cual lo usa como su drenaje b´asico de datos, pero posiblemente transformando los datos o proporcionando funcionalidad adicional.

BufferedOutputStream Hija de FilterOutputStream, la cual a su vez es hija de OutputStream; BufferedOutputStream maneja la salida del dis- positivo de salida est´andar (o defecto) de un sistema, usualmente el monitor.

PrintStream Hija de FilterOutputStream, la cual es a su vez hija de OutputStream; System.out es un objeto PrintStream.

Reader Clase abstracta para leer flujos de caracteres; los ´unicos m´eto- dos que una subclase debe implementar son read(char[], int, int) y close().

BufferedReader Lee texto de un flujo de caracteres de entrada, almacenando en el b´ufer caracteres para contar con lectura eficiente de caracteres, arreglos, y l´ıneas.

BufferedWriter Escribe texto a un flujo de caracteres de entrada, almacenan- do en el b´ufer caracteres para contar con escritura eficiente de caracteres, arreglos, y l´ıneas.

Cuadro 2: Descripci´on de las clases seleccionadas usadas para entrada y salida

(14)

La clase OutputStream puede ser usada para producir salida. El cuadro 3 muestra algunos de los m´etodos comunes de la clase. Se puede usar OutputStream para escribir todos los bytes de un arreglo o una parte. Cuando se termina de usar un OutputStream, se debe vaciar y cerrar.

M´etodo OutputStream Descripci´on

void close() Cierra el flujo de salida y libera cualquier recurso del sistema asociado con el flujo.

void flush() Vac´ıa el flujo de salida; si algunos bytes est´an en el buffer, estos son escritos.

void write(btye[] b) Escribe todos los bytes al flujo de salida del arreglo de byte especificado.

void write(btye[] b, int desp, int tam)

Escribe bytes al flujo de salida del arreglo especi- ficado de byte iniciando en la posici´on desp para una cantidad tam de caracteres.

Cuadro 3: Descripci´on de las clases seleccionadas usadas para entrada y salida

La clase System de Java contiene un objeto PrintStream llamado System.out, el cual ha sido usado con los m´etodos print() y println(). La clase System tambi´en define otro objeto PrintStream llamado System.err. La salida de ambos objetos PrintStream pueden ir al mismo dispositivo, como sucede habitualmente. System.err es reservado para mensajes de error y System.out es para salida v´alida. Estos se puede redirigir a otra localidad, como un archivo de disco o una impresora.

Se puede crear un objeto OutputStream propio y asignarlo a System.out, pero no se necesita hacer.

En el c´odigo 7 se muestra como se hace. Para esto la aplicaci´on SalidaAPantalla declara una String de calificaciones de letra permitidas en un curso. Luego, el m´etodo getBytes() convierte la String a un arreglo byte. Un objeto OutputStream es declarado, y System.out le es asignado en un bloque try. El m´etodo write() acepta el arreglo byte y lo manda al dispositivo de salida, y luego el flujo de salida es vaciado y cerrado.

1 import j a v a . i o . ∗ ;

2 public c l a s s S a l i d a A P a n t a l l a {

3 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

4 S t r i n g s = ”ABCDEF” ;

5 byte [ ] d a t o s = s . g e t B y t e s ( ) ;

6 OutputStream s a l i d a = n u l l ;

7 try {

8 s a l i d a = System . o u t ;

9 s a l i d a . w r i t e ( d a t o s ) ;

10 s a l i d a . f l u s h ( ) ;

11 s a l i d a . c l o s e ( ) ;

12 }

13 catch ( E x c e p t i o n e ) {

14 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

15 }

16 }

17 }

C´odigo 7: La clase SalidaAPantalla.

(15)

Escribir a un archivo

En vez de asignar el dispositivo de salida est´andar al OutputStream, como se hace en la aplicaci´on SalidaAPantalla, c´odigo 7, se puede hacer la asignaci´on a un archivo. Para realizarlo se construye un objeto BufferedOutputStream y se asigna a la referencia OutputStream. Si se quiere cambiar un dispositivo de salida de una aplicaci´on solo se requiere modificar este para asignar un nuevo objeto al OutputStream. Java permite asignar un archivo a un objeto Stream as´ı la pantalla de salida y el archivo de salida trabajan en la misma forma.

Se puede crear un archivo escribible usando el m´etodo newOutputStream() de la clase Files y pas´andole un argumento Path y una StandardOpenOption. El m´etodo crea un archivo si este todav´ıa no exist´ıa, abre el archivo para escritura, y regresa un OutputStream que puede ser usado para escribir bytes al archivo. El cuadro 4 muestra los argumentos StandardOpenOption que pueden usarse como el segundo argumento del m´etodo newOutputStream. Si no se indica alguna opci´on y el archivo no existe, se crea un archivo nuevo, y si el archivo existe, este se trunca, lo anterior es equivalente a usar las opciones CREATE y TRUNCATE EXISTING. Si se quiere agregar a un archivo existente, o crear el archivo si este no existe inicilmente usar las opciones CREATE y APPEND. Si se usa solo APPEND y el archivo no existe, se genera una excepci´on.

StandardOpenOption Descripci´on

WRITE Abre el archivo para escritura.

APPEND Agrega nuevos datos al final del archivo; usar esta opci´on con WRITE o CREATE.

TRUNCATE EXISTING Trunca el archivo existente a cero bytes as´ı el contenido del ar- chivo es reemplazado; usar esta opci´on con WRITE.

CREATE NEW Crea un nuevo archivo s´olo si este no existe; lanzando una ex- cepci´on si el archivo ya existe.

CREATE Abre un archivo si existe o crea uno nuevo si no existe.

DELETE ON CLOSE Borra el archivo cuando el flujo es cerrado; empleada con archivos temporales los existen mientras se ejecuta el programa.

Cuadro 4: Constantes StandardOpenOption seleccionadas.

La aplicaci´on SalidaAArchivo, c´odigo 8, escribe una String de bytes a un archivo. Las diferencias respecto a la aplicaci´on SalidaAPantalla, c´odigo 7, se resumen enseguida:

Se usan sentencias adicionales import, l´ıneas 2 y 3.

El nombre de la clase est´a cambiado, l´ınea 4.

Un Path es declarado para el archivo grados.txt, l´ınea 6.

En vez de asignar System.out a la referencia OutputStream, un objeto BufferedOutputStream es asignado, l´ınea 11.

(16)

1 import j a v a . i o . ∗ ;

2 import j a v a . n i o . f i l e . ∗ ;

3 import s t a t i c j a v a . n i o . f i l e . StandardOpenOption . ∗ ;

4 public c l a s s S a l i d a A A r c h i v o {

5 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

6 Path a r c h i v o = Paths . g e t ( ” g r a d o s . t x t ” ) ;

7 S t r i n g s = ”ABCDEF” ;

8 byte [ ] d a t o s = s . g e t B y t e s ( ) ;

9 OutputStream s a l i d a = n u l l ;

10 try {

11 s a l i d a = new

12 B u f f e r e d O u t p u t S t r e a m ( F i l e s . newOutputStream ( a r c h i v o , CREATE) ) ;

13 s a l i d a . w r i t e ( d a t o s ) ;

14 s a l i d a . f l u s h ( ) ;

15 s a l i d a . c l o s e ( ) ;

16 }

17 catch ( E x c e p t i o n e ) {

18 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

19 }

20 }

21 }

C´odigo 8: Aplicaci´on SalidaAArchivo.

Leer desde un archivo

Se usa un InputStream de forma similar a un OutputStream. Si se quiere, se puede crear un InputStream, asign´andole System.in, y usando el m´etodo read() con el objeto creado para recu- perar la entrada del teclado. Sin embargo, es m´as eficiente usar la clase Scanner para la entrada del teclado y mejor usar la clase InputStream para entrada de datos que han sido guardados en un archivo.

Se puede usar el m´etodo newInputStream() de la clase Files para abrir un archivo para lectura.

El m´etodo acepta un par´ametro Path y devuelve un flujo que puede leer bytes de un archivo. La aplicaci´on LeerArchivo, c´odigo 9, lee el archivo grados.txt que fue creado previamente. El Path es declarado, un InputStream es declarado usando el Path, y en la l´ınea 8 un flujo es asignado a la referencia InputStream.

Nota. Si se necesitan leer m´ultiples l´ıneas del archivo se podr´ıa usar un ciclo tal como el mostrado enseguida. El ciclo contin´uamente lee y muestra l´ıneas del archivo hasta que el m´etodo readLine() regresa null para indicar que no hay m´as datos disponibles.

while (s = lector.readLine() != null) System.out.println(s);

En la l´ınea 9 de la clase LeerArchivo, un BufferedReader es declarado. Un BufferedReader lee una l´ınea de texto de un flujo entrada-car´acter, almacenar caracteres en el b´ufer hace que la lectura sea m´as eficiente.

(17)

1 import j a v a . i o . ∗ ;

2 import j a v a . n i o . f i l e . ∗ ;

3 public c l a s s L e e r A r c h i v o {

4 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

5 Path a r c h i v o = Paths . g e t ( ” g r a d o s . t x t ” ) ;

6 I n p u t S t r e a m e n t r a d a = n u l l ;

7 try {

8 e n t r a d a = F i l e s . newInputStream ( a r c h i v o ) ;

9 B u f f e r e d R e a d e r l e c t o r = new B u f f e r e d R e a d e r (

10 new I np u t St r ea m R e ad e r ( e n t r a d a ) ) ;

11 S t r i n g s = n u l l ;

12 s = l e c t o r . r e a d L i n e ( ) ;

13 System . o ut . p r i n t l n ( s ) ;

14 l e c t o r . c l o s e ( ) ;

15 }

16 catch ( E x c e p t i o n e ) {

17 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

18 }

19 }

20 }

C´odigo 9: Clase LeerArchivo.

Cuando se usa la clase BufferedReader, se debe importar del paquete java.io en el programa.

La siguiente tabla muestra algunos m´etodos ´utiles de BufferedReader.

M´etodo BufferedReader Descripci´on

close() Cierra el flujo y cualquier recurso asociado con este.

read() Lee un s´olo car´acter.

read(char[] buffer, int de, int can)

Lee caracteres en una porci´on de un arreglo desde la posici´on de para can caracteres.

readLine() Lee una l´ınea de texto.

skip(long n) Salta el n´umero especificado de caracteres.

Cuadro 5: M´etodos BufferedReader.

5. Archivos secuenciales de datos

En ocasiones se quiere guardar algo m´as que un String en un archivo. Se podr´ıa tener un archivo de datos de registros personales que incluyan un n´umero de identificaci´on, un nombre, y un salario para cada empleado de una organizaci´on. La aplicaci´on EscribirArchivoEmpleado, c´odigo 10, lee n´umeros de identificaci´on, nombres y salarios desde el teclado y los manda, separados con comas, a un archivo.

1 import j a v a . n i o . f i l e . ∗ ;

2 import j a v a . i o . ∗ ;

3 import s t a t i c j a v a . n i o . f i l e . StandardOpenOption . ∗ ;

4 import j a v a . u t i l . S c a n n e r ;

5 public c l a s s E s c r i b i r A r c h i v o E m p l e a d o {

6 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

(18)

7 S c a n n e r e n t r a d a = new S c a n n e r ( System . i n ) ;

8 Path a r c h i v o = Paths . g e t ( ” e m p l e a d o s . t x t ” ) ;

9 S t r i n g s = ” ” ;

10 S t r i n g d e l i m i t a d o r = ” , ” ;

11 i n t i d ;

12 S t r i n g nombre ;

13 double s a l a r i o ;

14 f i n a l i n t SALIR = 9 9 9 ;

15 try {

16 OutputStream s a l i d a = new

17 B u f f e r e d O u t p u t S t r e a m ( F i l e s . newOutputStream ( a r c h i v o , CREATE) ) ;

18 B u f f e r e d W r i t e r e s c r i t o r = new

19 B u f f e r e d W r i t e r (new OutputStreamWriter ( s a l i d a ) ) ;

20 System . o ut . p r i n t ( ” I n g r e s a r n´umero i d e n t i f i c a d o r >> ” ) ;

21 i d = e n t r a d a . n e x t I n t ( ) ;

22 while ( i d != SALIR ) {

23 System . o ut . p r i n t ( ” I n g r e s a r nombre d e l empleado #” +

24 i d + ” >> ” ) ;

25 e n t r a d a . n e x t L i n e ( ) ;

26 nombre = e n t r a d a . n e x t L i n e ( ) ;

27 System . o ut . p r i n t ( ” I n g r e s a r s a l a r i o >> ” ) ;

28 s a l a r i o = e n t r a d a . n e x t D o u b l e ( ) ;

29 s = i d + d e l i m i t a d o r + nombre + d e l i m i t a d o r + s a l a r i o ;

30 e s c r i t o r . w r i t e ( s , 0 , s . l e n g t h ( ) ) ;

31 e s c r i t o r . newLine ( ) ;

32 System . o ut . p r i n t ( ” I n g r e s a r s i g u i e n t e n´umero i d e n t i f i c a d o r o ” +

33 SALIR + ” p a r a t e r m i n a r >> ” ) ;

34 i d = e n t r a d a . n e x t I n t ( ) ;

35 }

36 e s c r i t o r . c l o s e ( ) ;

37 }

38 catch ( E x c e p t i o n e ) {

39 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

40 }

41 }

42 }

C´odigo 10: La clase EscribirArchivoEmpleado.

En el c´odigo 10 del programa EscribirArchivoEmpleado en las l´ıneas 18—19 se crea un Buffered- Writer llamado escritor. La clase BufferedWriter es la contraparte de BufferedReader. Esta escribe texto a un flujo de salida, almancenando en el b´ufer los caracteres. La clase tiene tres m´etodos write() sobrecargados que proporcionan escritura eficiente de caracteres de un String, de arreglos char, y de un car´acter. El cuadro 6 contiene los m´etodos definidos en la clase BufferedWriter.

En la aplicaci´on EscribirArchivoEmpleado, los String de los datos de empleado son constru´ıdos dentro de un ciclo que se ejecuta mientras el usuario no ingrese el valor SALIR. Cuando el String est´a completo, con n´umero de identificaci´on, nombre y salario separados con comas, el String es enviado al escritor con lo indicado en la l´ınea 30. El m´etodo write() acepta el String desde la posici´on cero con su tama˜no completo.

Despu´es de que el String es escrito, se escribe enseguida el car´acter de nueva l´ınea del sistema.

Un archivo de datos podr´ıa no requerir un caracter de nueva l´ınea despu´es de cada registro, cada

(19)

M´etodo BufferedWriter Descripci´on

close() Cierra el flujo, vaci´andolo primero.

flush() Vac´ıa el flujo.

newline() Escribe un separador de l´ınea.

write(String s, int de, int can) Escribe un String desde la posici´on de para una longitud can.

write(char[] a, int de, int can) Escribe un arreglo char desde la posici´on de para una longitud can.

write(int c) Escribe un s´olo car´acter.

Cuadro 6: M´etodos BufferedWriter.

nuevo registro podr´ıa estar separado con una coma o cualquier otro car´acter ´unico que no fue usado como parte de los datos, el usar nueva l´ınea facilita la lectura del archivo. Como no todas las plataformas usan ’\n’ para separar las l´ıneas, la clase BufferedWriter contiene el m´etodo newLine() que usa el separador de l´ınea actual de la plataforma. Tambi´en se podr´ıa escribir el valor de System.getProperty("line.separator"), que es como lo hace el m´etodo newLine().

Cualquiera de los m´etodos de entrada o salida en el programa EscribirArchivoEmpleado podr´ıa lanzar una excepci´on, as´ı que este c´odigo es puesto en bloque try.

La aplicaci´on LeerArchivoEmpleado, c´odigo 11, lee el archivo empleado.txt creado por la aplicaci´on EscribirArchivoEmpleado. El programa declara un InputStream para el archivo, luego crea un BufferedReader usando el InputStream. La primera l´ınea es le´ıda en un String; mientras el m´etodo readLine() no devuelva null, el String es mostrado y una nueva l´ınea es le´ıda.

1 import j a v a . i o . ∗ ;

2 import j a v a . n i o . f i l e . ∗ ;

3 public c l a s s LeerArchivoEmpleado {

4 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

5 Path a r c h i v o = Paths . g e t ( ” e m p l e a d o s . t x t ” ) ;

6 S t r i n g s = ” ” ;

7 try {

8 I n p u t S t r e a m e n t r a d a = new

9 B u f f e r e d I n p u t S t r e a m ( F i l e s . newInputStream ( a r c h i v o ) ) ;

10 B u f f e r e d R e a d e r l e c t o r = new B u f f e r e d R e a d e r (

11 new I np u t St r ea m R e ad e r ( e n t r a d a ) ) ;

12 s = l e c t o r . r e a d L i n e ( ) ;

13 while ( s != n u l l ) {

14 System . o ut . p r i n t l n ( s ) ;

15 s = l e c t o r . r e a d L i n e ( ) ;

16 }

17 l e c t o r . c l o s e ( ) ;

18 }

19 catch ( E x c e p t i o n e ) {

20 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

21 }

22 }

23 }

C´odigo 11: La clase LeerArchivoEmpleado.

(20)

Otras aplicaciones podr´ıan no querer usar el archivo de datos s´olo como String como lo hace la aplicaci´on LeerArchivoEmpleado. La aplicaci´on LeerArchivoEmpleado2, c´odigo 12, tambi´en lee del archivo String pero las divide en campos usables. El m´etodo split() de la clase String acepta un argumento que identifica el delimitador de campos, para los ejemplos dados, una coma, y regresa un arreglo de String. Cada elemento del arreglo tiene un campo. Luego los m´etodos como parseInt() o parseDouble() pueden ser usados para obtener del String dividido sus respectivos tipos de datos.

1 import j a v a . i o . ∗ ;

2 import j a v a . n i o . f i l e . ∗ ;

3 public c l a s s LeerArchivoEmpleado2 {

4 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

5 Path a r c h i v o = Paths . g e t ( ” e m p l e a d o s . t x t ” ) ;

6 S t r i n g [ ] a r r e g l o = new S t r i n g [ 3 ] ;

7 S t r i n g s = ” ” ;

8 S t r i n g d e l i m i t a d o r = ” , ” ;

9 i n t i d ;

10 S t r i n g nombre ;

11 double s a l a r i o ;

12 double n e t o ;

13 f i n a l double HORAS SEMANA = 4 0 ;

14 double t o t a l = 0 . 0 ;

15 try {

16 I n p u t S t r e a m e n t r a d a = new

17 B u f f e r e d I n p u t S t r e a m ( F i l e s . newInputStream ( a r c h i v o ) ) ;

18 B u f f e r e d R e a d e r l e c t o r = new

19 B u f f e r e d R e a d e r (new I np u t St r e am R e ad e r ( e n t r a d a ) ) ;

20 s = l e c t o r . r e a d L i n e ( ) ;

21 while ( s != n u l l ) {

22 a r r e g l o = s . s p l i t ( d e l i m i t a d o r ) ;

23 i d = I n t e g e r . p a r s e I n t ( a r r e g l o [ 0 ] ) ;

24 nombre = a r r e g l o [ 1 ] ;

25 s a l a r i o = Double . p a r s e D o u b l e ( a r r e g l o [ 2 ] ) ;

26 n e t o = s a l a r i o ∗ HORAS SEMANA;

27 System . o ut . p r i n t l n ( ” i d #” + i d + ” ” + nombre +

28 $ ” + s a l a r i o + ” $ ” + n e t o ) ;

29 t o t a l += n e t o ;

30 s = l e c t o r . r e a d L i n e ( ) ;

31 }

32 l e c t o r . c l o s e ( ) ;

33 }

34 catch ( E x c e p t i o n e ) {

35 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

36 }

37 System . o ut . p r i n t l n ( ” El pago t o t a l de l a n´omina e s $ ” + t o t a l ) ;

38 }

39 }

C´odigo 12: La clase LeerArchivoEmpleado2.

Conforme cada registro es le´ıdo y dividido en la clase LeerArchivoEmpleado2, su campo salario es usado para calcular el pago bruto del empleado usando una jornada de 40 horas a la semana.

Luego es pago bruto es acumulado para producir el pago total de la n´omina despu´es de que todos

(21)

los datos han sido procesados.

6. Archivos de acceso aleatorio

Los negocios guardan datos en orden secuencial cuando usan los registros para procesamiento por lotes, lo cual involucra realizar las mismas tareas con muchos registros, uno despu´es de otro.

Por ejemplo, cuando una empresa genera recibos de pagos, los registros para el per´ıodo de pago son reunidos en un lote y los recibos son calculados e impresos en secuencia. No importa cual recibo sea generado primero porque los recibos son enviados por correo.

Un acceso secuencial es ineficiente para muchas aplicaciones. Estas aplicaciones, como aplicaciones en tiempo real, requieren que un registro sea accedido inmediatamente mientras un cliente espera.

Un programa en el cual el usuario hace peticiones directas es un programa interactivo. Un representante del servicio al cliente requiere archivos de acceso aleatorio, tambi´en llamados de acceso directo o de acceso instant´aneo, en vez de archivos de acceso secuencial. Ya que tomar´ıa mucho tiempo acceder los registros de los clientes si estos deben ser le´ıdos secuencialmente.

Se puede usar la clase FileChannel para crear archivos de acceso aleatorio. Un objeto canal de archivo es un medio para leer y escribir a un archivo. Un canal de archivo es buscable, es decir, se puede buscar por una localidad espec´ıfica del archivo y las operaciones pueden iniciar en cualquier posici´on especificada. El cuadro 7 muestra algunos m´etodos FileChannel.

M´etodo FileChannel Descripci´on FileChannel open(Path arch,

OpenOption... opciones)

Abre o crea un archivo, devolviendo un canal de archivo.

long position() Devuelve la posici´on del canal de archivo.

FileChannel position(long nuevaPosicion)

Pone la nueva posici´on del canal de archivo.

int read(ByteBuffer b´ufer) Lee una secuencia de bytes del canal en el b´ufer.

long size() Devuelve el tama˜no actual del archivo de canal.

int write(ByteBuffer b´ufer) Escribe una secuencia de bytes al canal desde el b´ufer.

Cuadro 7: M´etodos FileChannel.

Varios m´etodos del cuadro 7 usan un objeto ByteBuffer. Un ByteBuffer es un lugar para bytes que est´an esperando ser le´ıdos o escritos. Un arreglo de bytes puede ser envuelto, o empacado, en un ByteBuffer usando el m´etodo wrap() de ByteBuffer. Envolviendo un arreglo de bytes en un b´ufer hace que los cambios hechos al b´ufer tambi´en sean hechos al arreglo, y viceversa. Crear un FileChannel para escritura de datos aleatoria requiere crear un ByteBuffer y otros pasos:

Se puede usar el m´etodo newByteChannel() de la clase Files para obtener un ByteChannel para un Path. El m´etodo newByteChannel acepta argumentos Path y StandardOpenOption que indicar como el archivo ser´a abierto.

El ByteChannel regresado por el m´etodo newByteChannel() puede entonces ser convertido a un FileChannel usando una sentencia parecida a la siguiente:

(22)

FileChannel fc = (FileChannel)Files.newByteChannel(archivo, READ, WRITE);

Se puede crear un arreglo byte. El arreglo byte se puede construir desde un String usando el m´etodo getBytes() como sigue:

String s = "ABC";

byte[] datos = s.getBytes();

El arreglo byte puede ser envuelto en un ByteBuffer de esta forma:

ByteBuffer salida = ByteBuffer.wrap(datos);

Luego el ByteBuffer lleno puede ser escrito al FileChannel declarado como en la siguiente instrucci´on:

fc.write(salida);

Se puede revisar si el contenido de un ByteBuffer ha sido usado mediante el m´etodo has- Remaining().

Despu´es de escribir el contenido de un ByteBuffer, se puede escribir el mismo contenido del ByteBuffer nuevamente usando el m´etodo rewind() para reposicionar el ByteBuffer al inicio del b´ufer.

La aplicaci´on PruebaAccesoAleatorio, c´odigo 13 usa los pasos previos para declarar un archivo y escribir algunos bytes aleatoriamente en las posiciones 0, 22, y 12.

1 import j a v a . i o . ∗ ;

2 import j a v a . n i o . f i l e . ∗ ;

3 import j a v a . n i o . c h a n n e l s . F i l e C h a n n e l ;

4 import j a v a . n i o . B y t e B u f f e r ;

5 import s t a t i c j a v a . n i o . f i l e . StandardOpenOption . ∗ ;

6 public c l a s s P r u e b a A c c e s o A l e a t o r i o {

7 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

8 Path a r c h i v o = Paths . g e t ( ” numeros . t x t ” ) ;

9 c r e a r A r c h i v o ( a r c h i v o ) ;

10 S t r i n g s = ”ABC” ;

11 byte [ ] d a t o s = s . g e t B y t e s ( ) ;

12 B y t e B u f f e r s a l i d a = B y t e B u f f e r . wrap ( d a t o s ) ;

13 F i l e C h a n n e l f c = n u l l ;

14 try {

15 f c = ( F i l e C h a n n e l ) F i l e s . newByteChannel ( a r c h i v o , READ, WRITE ) ;

16 f c . p o s i t i o n ( 0 ) ;

17 while ( s a l i d a . hasRemaining ( ) )

18 f c . w r i t e ( s a l i d a ) ;

19 s a l i d a . r e w i n d ( ) ;

20 f c . p o s i t i o n ( 2 2 ) ;

21 while ( s a l i d a . hasRemaining ( ) )

22 f c . w r i t e ( s a l i d a ) ;

23 s a l i d a . r e w i n d ( ) ;

(23)

24 f c . p o s i t i o n ( 1 6 ) ;

25 while ( s a l i d a . hasRemaining ( ) )

26 f c . w r i t e ( s a l i d a ) ;

27 f c . c l o s e ( ) ;

28 }

29 catch ( E x c e p t i o n e ) {

30 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

31 }

32 }

33 public s t a t i c void c r e a r A r c h i v o ( Path a r c h i v o ) {

34 S t r i n g s = ” 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 ” ;

35 try {

36 OutputStream s a l i d a = new

37 B u f f e r e d O u t p u t S t r e a m ( F i l e s . newOutputStream ( a r c h i v o , CREATE) ) ;

38 s a l i d a . w r i t e ( s . g e t B y t e s ( ) ) ;

39 s a l i d a . f l u s h ( ) ;

40 s a l i d a . c l o s e ( ) ;

41 }

42 catch ( E x c e p t i o n e ) {

43 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

44 }

45 }

46 }

C´odigo 13: La clase PruebaAccesoAleatorio.

7. Escritura de registros en archivos de acceso aleatorio

La escritura de caracteres en localidades aleatorias de archivo de texto, como en la apliaci´on PruebaAccesoAleatorio, es de valor limitado. Cuando se guardan registros en un archivo, el ac- ceso es m´as ´util por registro, como acceder el quinto registro, en vez de recuperar el quinto byte.

Para lograr lo anterior, se debe multiplicar la posici´on del registro que se quiere acceder por el tama˜no del registro. Por ejemplo, si se guardan registros que son de tama˜no 50 bytes, el primer registro est´a en la posici´on cero, el segundo registro en la posici´on 50, el tercero en la posici´on 100, y as´ı sucesivamente. Es decir, se puede acceder el n-´esimo registro en un FileChannel llamado fc con la siguiente sentencia:

fc.position((n - 1) * 50);

Una aproximaci´on para escribir un archivo de acceso aleatorio es colocar los registros en el archivo usando un campo llave. Un campo llave es el campo en un registro que hace al registro ´unico del resto. Por ejemplo, suponer que se quiere guardar n´umeros identificador de empleado, apellidos, y salarios en un archivo de acceso aleatorio. En un archivo de empleados, varios registros podr´ıan tener el mismo apellido o salario, pero cada registro tiene un n´umero identificador de empleado, as´ı que este campo puede ser el campo llave.

El primer paso en la creaci´on del archivo empleado de acceso aleatorio es la creaci´on de un archivo que guarde los registros por defecto—por ejemplo, usando ceros para los n´umeros identificadores

(24)

y los salarios y blancos para los apellidos. Para este caso, suponer que cada n´umero identificador de empleado es de tres d´ıgitos. La aplicaci´on CrearArchivoVacioEmpleados, c´odigo 14, crea 1,000 registros del tipo descrito.

1 import j a v a . i o . ∗ ;

2 import j a v a . n i o . B y t e B u f f e r ;

3 import j a v a . n i o . f i l e . ∗ ;

4 import s t a t i c j a v a . n i o . f i l e . StandardOpenOption . ∗ ;

5 public c l a s s C r e a r A r c h i v o V a c i o E m p l e a d o s {

6 public s t a t i c void main ( S t r i n g [ ] a r g s ) {

7 Path a r c h i v o = Paths . g e t ( ” e m p l e a d o s a l e a t o r i o . t x t ” ) ;

8 S t r i n g s = ” 0 0 0 , , 0 0 0 . 0 0 ” +

9 System . g e t P r o p e r t y ( ” l i n e . s e p a r a t o r ” ) ;

10 f i n a l i n t NUM REGS = 1 0 0 0 ;

11 try {

12 OutputStream s a l i d a = new

13 B u f f e r e d O u t p u t S t r e a m ( F i l e s . newOutputStream ( a r c h i v o , CREATE) ) ;

14 B u f f e r e d W r i t e r e s c r i t o r = new

15 B u f f e r e d W r i t e r (new OutputStreamWriter ( s a l i d a ) ) ;

16 f o r ( i n t c o n t = 0 ; c o n t < NUM REGS; ++c o n t )

17 e s c r i t o r . w r i t e ( s , 0 , s . l e n g t h ( ) ) ;

18 e s c r i t o r . c l o s e ( ) ;

19 }

20 catch ( E x c e p t i o n e ) {

21 System . o ut . p r i n t l n ( ” Mensaje : ” + e ) ;

22 }

23 }

24 }

C´odigo 14: Aplicaci´on CrearArchivoVacioEmpleados.

En las l´ıneas 8—9 del c´odigo 14, un String que representa un registro por defecto es declarado.

El n´umero de empleado de tres d´ıgitos es puesto a ceros, el apellido consiste de veinte blancos, y el salario es 000.00, adem´as la cadena termina con el valor del separador de l´ınea del sistema. Un arreglo byte es construido del String y envuelto en un b´ufer. Luego un archivo es abierto en modo CREATE y un BufferedWriter es establecido.

En las l´ıneas 16—17 del c´odigo 14, un ciclo se ejecuta mil veces. Dentro del ciclo, la cadena por defecto empleado es pasada al m´etodo write() del objeto BufferedWriter.

Los campos por defecto en el archivo base de acceso aleatorio se pueden inicializar con los valores que sean m´as convenientes para la organizaci´on. El ´unico requisito en la inicializaci´on es que los registros por defecto sean reconocidos como tales.

Despu´es de crear el archivo por defecto base, se puede reemplazar cualquiera de sus registros con datos para un empleado actual. La aplicaci´on CrearUnRegistroAccesoAleatorio, c´odigo 15, crea un s´olo registro empleado definido en la sentencia de las l´ıneas 9—10. El registro es para empleado 002 con apellidos Silvano Aureoles y un salario de 987.65. En la l´ınea 11, el tama˜no de la cadena es asignado a TAM REG. En este caso el tama˜no es 32, ya que cada caracter, incluyendo las comas delimitadoras, y un byte para el valor del separador de l´ınea dado por System.getProperty().

Despu´es el FileChannel es establecido, el registro es escrito al archivo en la posici´on que inicia en dos veces el tama˜no del registro. El valor dos es codificado en esta aplicaci´on porque el n´umero identicador de empleados es 002.

Referencias

Documento similar

1.- Las sanciones impuestas a Servidores Públicos y particulares por la comisión de faltas administrativas en términos de la LGRA y la Ley de Responsabilidades

o esperar la resolución expresa&#34; (artículo 94 de la Ley de procedimiento administrativo). Luego si opta por esperar la resolución expresa, todo queda supeditado a que se

[ Además de compartir el catálogo de procedimientos y de tipos documentales, sólo deben cargarse datos que se puedan asociar con las bases de datos corporativa

(3) VEAMOS COMO AFECTA UN AUMENTO DEL TIPO IMPOSITIVO EFECTIVO A LA CURVA IS.. Partimos de una situación inicial donde el tipo impositivo efectivo era igual a t

En el caso de películas de coproducción minoritaria italiana, es frecuente que las películas se estrenaran antes en el mercado extranjero y después en el italiano: también

En su natal Caracas, donde se formó Bello como latinista, no pudo tener la oportunidad de aprender griego. Cuando nació, ya hacía 14 años que los jesuitas habían sido

Archivo Histórico de Sabadell Archivo Histórico de Sabadell Archivo Histórico de Sabadell Archivo Nacional de Cataluña Archivo Nacional de Cataluña Archivo Nacional de Cataluña