Entrada y salida de archivos

Documentos relacionados
Sistemas Operativos. Curso 2016 Sistema de Archivos

Flujos (streams) Programación. Licenciatura Lingüística y Nuevas Tecnologias Nadjet Bouayad-Agha

Estructura de Datos: Archivos

Práctica 4: Herencia. Objetivos:

Ficheros y streams. Desde el punto de vista de Java, cada fichero no es más que una secuencia o flujo de bytes [stream].

PROGRAMACIÓN ORIENTADA A OBJETOS (L40629) Sabino Miranda-Jiménez

Unidad II. Fundamentos de programación en Java. Ing. José Luis Llamas Cárdenas

IIC1103 Introducción a la Programación. Ayudantía: Archivos

Programación Orientada a Objetos. Tema 7: Persistencia

Todo programa en 'C' consta de una o más funciones, una de las cuales se llama main.

Tema 4. Excepciones en Java

Ficheros conceptos. Manejo de ficheros en C. Apertura del fichero Función fopen: nombre del fichero. Apertura del fichero Función fopen

Tipos DataInputStream/DataOutputStream: L/E de datos de tipo simple y Cadenas (Strings) ObjectInputStream/ObjectOutputStream: para persistencia de obj

INTRODUCCIóN A LA PROGRAMACIóN APUNTES DE JAVA APUNTES DE JAVA

Desde los programas más simples escritos en un lenguaje de programación suelen realizar tres tareas en forma secuencial.

Entrada y Salida con Java

Elementos de un programa en C

TEMA 7: Ficheros. TEMA 7: Ficheros Concepto de fichero

Objetivos de la sesión. Aplicación de consola 7/30/11. Código con que se inicia un programa en Visual C# (aplicación de consola)

Sockets. Los sockets son un mecanismo de comunicación entre procesos que se utiliza en Internet.

ESCUELA DE INFORMÁTICA

TEMA 2. EL LENGUAJE C. ELEMENTOS BÁSICOS

Introduciendo datos desde el

Laboratorio de Arquitectura de Redes. Entrada y salida estándar

James Gosling, creador de Java

Unidad Didáctica 2. Elementos básicos del lenguaje Java Tipos, declaraciones, expresiones y asignaciones

Práctica III: Streams, Readers y Writers

Guía - Taller # 2 (JAVA)

Una base de datos de Access puede estar conformada por varios objetos, los más comunes son los siguientes:

Qué es un programa informático?

Tema 7.- Fundamentos de la Programación Orientada a Objetos

Informática Ingeniería en Electrónica y Automática Industrial

Constantes. Las constantes no cambian durante la ejecucion de un programa en C++, en C++ existen 4 tipos de constantes:

$0 Representa al parámetro cero o nombre del programa $1 Representa al parámetro uno $2 Representa al parámetro dos

Información de la lectura en un programa.

1.- FUNDAMENTOS FUNCIONAMIENTO GENÉRICO JAVA SOCKETS Creación de Streams de Entrada...7

Ejercicios de Programación Tema 7. Programación Orientada a Objetos

TEMA 3. CONCEPTOS FUNDAMENTALES DEL NIVEL DEL SISTEMA OPERATIVO. Definición y objetivos de un S.O

ATRIBUTOS DE LOS FICHEROS EN LINUX

1. Cuántas sentencias hay en la secuencia principal del siguiente programa?

Principios de Computadoras II

Excepciones y E/S Java y Servicios Web I Master en Ingeniería Matemática

GUIA No 5. CREACIÓN DE SubVI s

Definición de Memoria

4. Operadores Operador asignación

FUNDAMENTOS DE INFORMÁTICA

Estructura del Computador

Métodos, clases, y objetos

INF 473 Desarrollo de Aplicaciones en Java

Principios de Computadoras II

Estructuras básicas de la programación en Java

Usando el Sistema Operativo

Lenguaje de programación con JAVA

Sesión No. 10. Contextualización INFORMÁTICA 1. Nombre: Gestor de Base de Datos (Access)

Carlos Montenegro. Programación Orientada a Objetos Proyecto Curricular de Ingeniería de Sistemas

Programación en java. Estructuras algorítmicas

En este artículo vamos a conocer los tipos de datos que podemos manejar programando en C.

Lección 2: Creando una Aplicación en Java. 1. Estructura del archivo de una clase. 3. Definiendo clases fundamentos

Tema: Clases y Objetos en C#. Parte II.

SISTEMAS INFORMÁTICOS PROGRAMACION I - Contenidos Analíticos Ing. Alejandro Guzmán M. TEMA 2. Diseño de Algoritmos

Universidad Central de Venezuela. Facultad de Ciencias. Escuela de Computación. Proyecto #1 (Simulación de caché)

Práctica de laboratorio Uso de la Calculadora de Windows con direcciones de red

APUNTADORES. Un apuntador es un objeto que apunta a otro objeto. Es decir, una variable cuyo valor es la dirección de memoria de otra variable.

Universidad Nacional del Santa FACULTAD DE INGENIERIA E.A.P. Ingeniería de Sistemas e Informática NETBEANS 7.0 MI PRIMER PROYECTO

ESCUELA POLITÉCNICA SUPERIOR PRÁCTICA 2: EXPRESIONES, PRINTF Y SCANF

ESTRUCTURA BÁSICA DE UN ORDENADOR

DIAGRAMAS DE FLUJO ELEMENTOS E INSTRUCCIONES A USAR EN UN DIAGRAMA DE FLUJO

Streams y manejo de entrada y salida (Input & Output)

Práctica 3. Paso de parámetros entre subrutinas. 3. Consideraciones sobre el paso de parámetros

PROCEDIMIENTO PARA GENERAR LOS REQUERIMIENTOS DE SELLOS DIGITALES

La clase Integer y sus métodos. Los Operadores (concepto). Operadores Lógicos y a nivel de Bits. Operadores de desplazamiento. Concatenaciones. La Con

Fundamentos de JAVA. Angel Kuri Enero, /2/2006

Tema: Introducción al IDE de Microsoft Visual C#.

Sistema electrónico digital (binario) que procesa datos siguiendo unas instrucciones almacenadas en su memoria

Sistemas operativos. Tema 10: Sistemas de ficheros

Lo que necesitaremos para programar en Java, será un editor de texto o IDE y la JDK.

UNIDAD 1. COMPONENTES DEL COMPUTADOR

Procesamiento de documentos XML.

Instituto Tecnológico de Celaya

Variables. Una variable no es más que un nombre simbólico que identifica una dirección de memoria: vs.

ENIAC, Primer computador electrónico y su panel de conexiones

Ficha de Aprendizaje N 13

Manual de turbo pascal

Tema 14: Arreglos estáticos en C

C.I.F.: B Inscrita en el Registro Mercantil de Madrid, Tomo Libro 0, Folio 135, Sección 8. Hoja M Inscripción 1ª

Java Avanzado Facultad de Ingeniería. Escuela de computación.

Sistemas Operativos: Programación de Sistemas. Curso Oscar Déniz Suárez Alexis Quesada Arencibia Francisco J.

Conceptos a tratar. Fundamentos de la Programación Orientada a Objetos Ampliación sobre clases y objetos

Curso de Programación en C. Licenciatura, FCQeI. APUNTADORES.

Para leer la entrada de consola, lo primero que se hace es construir un Scanner que este asociado al flujo de entrada estándar System.

Conceptos básicos de bases de datos

Programación Orientada a Objetos (POO)

Tema: Uso del programa DFD

Computación II. Introducción a Visual Basic

INTRODUCCIÓN...9 CAPÍTULO 1. ELEMENTOS DE UN PROGRAMA INFORMÁTICO...11

Agradecimientos. Nota de los autores. 1 Problemas, algoritmos y programas 1

Enteros. Son los números que no contienen componentes fraccionarios y, por tanto, no contienen punto decimal.

TECNOLOGÍA DE REDES. Temario (Segunda Parte) 18/04/2008. Unidad 3. Introducción a los Routers (Segunda Parte)

Lenguaje C Elementos de un Programa. Ing. Hugo Fdo. Velasco Peña Universidad Nacional 2006

Expresiones y sentencias

Transcripción:

Entrada y salida de archivos H. Tejeda Abril 2016 Índice 1. Introducción 1 2. Clases Path y Files 3 3. Organización de archivos, flujos, y búfers 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ón de como se guarda la información en un sistema de cómputo, y los medios que este tiene para llevarlo a cabo. Después se comentan algunas clases que pueden ser usadas para realizar algunas tareas con archivos. 1. Introducción Los elementos de datos pueden ser guardados en dos tipos de dispositivos de almacenamiento en un sistema de cómputo: Almacenamiento volátil es temporal; valores que son volatiles, como los que son guardados en variables, se pierden cuando la computadora deja de ser energizada. RAM (random 1

access memory) es el almacenamiento temporal dentro de una computadora. También se le conoce como memoria de almacenamiento volátil. Almacenamiento no volátil es almacenamiento permanente; no se pierde al desenergizar la computadora. Cuando se escribe un programa Java y se guarda en el disco, se está usando almacenamiento permanente. Un archivo de computadora es una colección de datos guardados en un dispositivo no volátil. El archivo existe en dispositivos de almacenamiento permanente, tales como discos duros, discos ZIP, memorias USB, carretes de cinta magnética, 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ómina con número de empleado, nombre y salario; otros archivos son archivos de programa o archivos de aplicación que guardan instrucciones de software. Archivos binarios contienen datos que no han sido codificados como texto. Ejemplos de estos archivos son imágenes, música, archivos de programa compilados. Los archivos tienen muchas características comúnes cada archivo tiene un tamaño que indica el espacio que ocupa en una sección del disco u otro dispositivo de almacenamiento, y cada archivo tiene un nombre y un tiempo específico de creación. 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ón los sistemas de cómputo 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á 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ón, 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. 2

Cerrar un archivo. Borrar un archivo. Java da clases incorporadas que contienen métodos para realizar estas tareas. 2. Clases Path y Files Se usa la clase Path para crear objetos que contengan información acerca de archivos y directorios, tal como su lugar, tamaño, fecha de creación, y si existen. Se usa la clase Files para realizar operaciones en archivos y directorios, como borrar, conocer sus atributos, creación 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és 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ás viejas de Java. Crear un camino Para crear un camino, primero se debe determinar el sistema de archivos por defecto en la computadora anfitrión usando una sentencia como la siguiente: FileSystem fs = FileSystems.getDefault(); La sentencia crea un objeto FileSystem usando el método getdefault() de la clase FileSystems, se están usando dos clases diferentes. La clase FileSystem se usa para declarar el objeto y FileSystems es una clase que contiene métodos de fábrica que ayudan en la creación del objeto. Después de crear un objeto FileSystem, se puede definir una Path (ruta) usando el método getpath(): Path ruta = fs.getpath("/usr/bin/nano"); Si se tiene que usar la diagonal invertida (backslash) para construir la ruta esta deberá 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étodo de instancia getseparator() con un objeto FileSystem: Path rutaarchivo = fs.getpath(fs.getseparator()+"usr"+fs.getseparator()+ "bin"+fs.getseparator()+nano); 3

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étodo get() de Paths llama al método 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ón declara un camino y se quiere usar la aplicación con un archivo diferente, se podría cambiar solo el String pasado al método de instanciación. Cada Path es absoluto o relativo. Un camino absoluto es un camino completo; no necesita otra información para localizar un archivo. Un camino relativo depende del directorio de trabajo para la localización. Una ruta simple como ArchivoEjemplo.txt es relativo. Cuando se trabaja con un camino que contiene sólo una archivo, se asume que el archivo está 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á dentro de ese folder. Recuperación de información acerca de un camino En el cuadro 1 se resumen varios métodos Path útiles. El método tostring() de la clase Object es anulado; haciendo que devuelva un String de la representación de Path. El método getfilename() devuelve la última 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étodo String tostring() Path getfilename() int getnamecount() Path getname(int) Descripción Devuelve la representación String del Path. Regresa el archivo o directorio dado por este Path; este es el último elemento en la secuencia de elementos nombre. Devuelve la cantidad de elementos nombre en el Path. Regresa el nombre en la posición del Path indicado por el parámetro entero. Cuadro 1: Algunos métodos de la clase Path Un elemento de Path es accedido por un índice. El elemento de la cima en la estructura directorio está localizado en el índice cero; el elemento más bajo en la estructura es accedido por el método 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étodo getnamecount() y para recuperar el nombre en la posición indicada por el argumento usar getnamecount. En el código 1 se tiene un programa que crea un Path y usa algunos de los métodos del cuadro 1. 4

1 import java. nio. f i l e. ; 2 public class DemoPath { 3 public static void main ( S t r i n g [ ] args ) { 4 Path caminoarch = Paths. get ( / e t c /passwd ) ; 5 int contador = caminoarch. getnamecount ( ) ; 6 System. out. p r i n t l n ( El camino es + caminoarch. t o S t r i n g ( ) ) ; 7 System. out. p r i n t l n ( El nombre d e l a rchivo es + 8 caminoarch. getname ( contador 1)); 9 System. out. p r i n t l n ( Hay + contador + 10 elementos en e l camino d e l a rchivo ) ; 11 for ( int x = 0 ; x < contador ; ++x ) 12 System. out. p r i n t l n ( \ telemento + x + es + 13 caminoarch. getname ( x ) ) ; 14 } 15 } Código 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ón de un camino relativo a absoluto El método toabsolutepath() convierte un camino relativo a uno absoluto. El código 2 muestra una aplicación que pide al usuario un nombre de archivo y, si se puede, lo convierte a un camino absoluto. 1 import java. u t i l. Scanner ; 2 import java. nio. f i l e. ; 3 public class DemoPath2 { 4 public static void main ( S t r i n g [ ] args ) { 5 S t r i n g nombre ; 6 Scanner t e c l a d o = new Scanner ( System. in ) ; 7 System. out. p r i n t ( I n g r e s a r e l nombre de un archivo >> ) ; 8 nombre = t e c l a d o. nextline ( ) ; 9 Path caminodado = Paths. g e t ( nombre ) ; 10 Path caminocompleto = caminodado. toabsolutepath ( ) ; 11 System. out. p r i n t l n ( El camino completo es + 12 caminocompleto. t o S t r i n g ( ) ) ; 13 } 14 } Código 2: La clase DemoPath2. Cuando se ejecuta la aplicación DemoPath2, el nombre ingresado podría representar un camino 5

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ón 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étodo checkaccess(). La siguiente sentencia import permite el acceso a constantes que pueden ser usadas como argumentos al método: import static java.nio.file.accessmode.*; Mediante un objeto Path, referido por archivo, la sintaxis para usar el método checkaccess() es la siguiente: archivo.getfilesystem().provider().checkaccess(); Cualquiera de los siguientes argumentos puede ser usado con el método checkaccess(): Sin argumentos Revisa que el archivo exista; como una alternativa, se puede sustituir el método 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ón. 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á referir a la constante READ por su nombre completo como AccessMode.READ; cuando se usa static, se puede referir a esta sólo como READ. Se pueden usar argumentos múltiples con el método checkaccess(), separados por comas. Si el archivo no puede ser accedido como se indica en la llamada del método, una IOException es lanzada. El código 3 muestra una aplicación que declara un Path y revisa si el archivo puede ser leído y ejecutado. En la línea 3 del código 3, se debe importar el paquete java.io.ioexception porque una excepción podría ser instanciada y lanzada. 6

1 import java. nio. f i l e. ; 2 import static java. nio. f i l e. AccessMode. ; 3 import java. i o. IOException ; 4 public class DemoPath3 { 5 public static void main ( S t r i n g [ ] args ) { 6 Path a r c h i v o = Paths. get ( / e t c /passwd ) ; 7 System. out. p r i n t l n ( Path es + a rchivo. t o S t r i n g ( ) ) ; 8 try { 9 a r c h i v o. getfilesystem ( ). p r o v i d e r ( ). checkaccess ( archivo,read,execute) ; 10 System. out. p r i n t l n ( El archivo puede s e r l e ído y e j e c u t a d o ) ; 11 } 12 catch ( IOException e ) { 13 System. out. p r i n t l n ( El archivo no puede s e r usado por l a a p l i c a c i ón ) ; 14 } 15 } 16 } Código 3: La clase DemoPath3. A continuación se muestra la salida del código 3 al ser ejecutado. $ java DemoPath3 Path es /etc/passwd El archivo no puede ser usado por la aplicación 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étodo delete() de la clase Files acepta un parámetro Path y borra el último elemento (archivo o directorio) en un camino o lanza una excepción 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á 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ódigo 4 muestra un programa que muestra un mensaje apropiado en cada uno de los escenarios precedentes después de intentar borrar un archivo. 7

1 import java. nio. f i l e. ; 2 import java. i o. IOException ; 3 public class DemoPath4 { 4 public static void main ( S t r i n g [ ] args ) { 5 Path caminoarchivo = Paths. get ( / e t c /passwd ) ; 6 try { 7 F i l e s. d e l e t e ( caminoarchivo ) ; 8 System. out. p r i n t l n ( El archivo o d i r e c t o r i o e s t á borrado ) ; 9 } 10 catch ( NoSuchFileException e ) { 11 System. out. p r i n t l n ( No e x i s t e e l a rchivo o d i r e c t o r i o ) ; 12 } 13 catch ( DirectoryNotEmptyException e ) { 14 System. out. p r i n t l n ( El d i r e c t o r i o no e s t á vacío ) ; 15 } 16 catch ( SecurityException e ) { 17 System. out. p r i n t l n ( No t i e n e permiso para borrar ) ; 18 } 19 catch ( IOException e ) { 20 System. out. p r i n t l n ( Exception IO ) ; 21 } 22 } 23 } Código 4: La clase DemoPath4. El método deleteifexists() de la clase Files también puede ser usado para borrar un archivo, pero si el archivo no existe, no se lanza excepción. Determinar atributos de archivo El método readattributes() de la clase Files se puede usar para recuperar información útil de un archivo. Se deben dar dos argumentos al método, 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és de crear un objeto BasicFileAttributes, se dispone de un conjunto de métodos para recuperar información del archivo. Como, el método size() que da el tamaño del archivo en bytes, los métodos creationtime() y lastmodifiedtime() que dan fechas del archivo. El código 5 contiene un programa que usa estos métodos. 8

1 import java. nio. f i l e. ; 2 import java. nio. f i l e. a t t r i b u t e. ; 3 import java. i o. IOException ; 4 public class DemoPath5 { 5 public static void main ( S t r i n g [ ] args ) { 6 Path caminoarchivo = Paths. get ( / 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. class ) ; 10 System. out. p r i n t l n ( Fecha de c r e a c i ón : + a t r i b. creationtime ( ) ) ; 11 System. out. p r i n t l n ( Última m o d i f i c a c i ón : + 12 a t r i b. lastmodifiedtime ( ) ) ; 13 System. out. p r i n t l n ( Tamaño : +a t r i b. s i z e ()+ bytes ) ; 14 } 15 catch ( IOException e ) { 16 System. out. p r i n t l n ( Excepción E/S ) ; 17 } 18 } 19 } Código 5: La clase DemoPath5. Los métodos de fecha en el programa DemoPath5 devuelven un objeto FileTime que es convertido a una String en la llamada al método 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ón: 2013-10-23T15:16:20Z Última modificación: 2013-10-23T15:16:20Z Tama~no: 5253 bytes Para determinar la relación de fechas entre dos archivos, se puede usar el método compareto(). En el código 6 se muestra como se podrían comparar las fechas de creación de dos archivos. El método compareto() regresa un valor menor que cero si el primer FileTime está antes que el argumento de FileTime. Regresará el método un valor mayor que cero si el primer FileTime es posterior al del argumento y cero si los valores de FileTime son los mismos. 9

1 import java. nio. f i l e. ; 2 import java. nio. f i l e. a t t r i b u t e. ; 3 import java. i o. IOException ; 4 public class DemoPath6 { 5 public static void main ( S t r i n g [ ] args ) { 6 Path arch1 = Paths. get ( / e t c /passwd ) ; 7 Path arch2 = Paths. get ( / 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. class ) ; 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. class ) ; 13 FileTime time1 = a t t r 1. creationtime ( ) ; 14 FileTime time2 = a t t r 2. creationtime ( ) ; 15 System. out. p r i n t l n ( La fecha de c r e a c i ón de arch1 es : + time1 ) ; 16 System. out. p r i n t l n ( La fecha de c r e a c i ón de arch2 es : + time2 ) ; 17 i f ( time1. compareto ( time2 ) < 0) 18 System. out. p r i n t l n ( arch1 fue creado antes que arch2 ) ; 19 else i f ( time1. compareto ( time2 ) > 0) 20 System. out. p r i n t l n ( arch1 fue creado después que arch2 ) ; 21 else 22 System. out. p r i n t l n ( arch1 y arch2 fueron creados a l mismo tiempo ) ; 23 } 24 catch ( IOException e ) { 25 System. out. p r i n t l n ( Excepción E/S ) ; 26 } 27 } 28 } Código 6: La clase DemoPath6. Nota. Java soporta clases especializadas para atributos de archivos DOS y de Windows, como oculto, o sólo lectura y atributos de archivos POSIX usados en sistemas como UNIX, donde los archivos tiene dueño de grupo. 3. Organización de archivos, flujos, y búfers de datos Se pueden guardar datos en variables dentro de un programa, pero es temporal el almacenamiento. Por otra parte, cuando la aplicación 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 útil más pequeña de datos para la mayoría de los usuarios es el carácter. Un carácter puede ser cualquier letra, número, o símbolos especiales (como signos de puntuación) que forman los datos. Los caracteres están formados con bits (los ceros y los unos que representan los valores lógicos usados por el sistema de cómputo), pero los usuarios no les importa si la representación de algún carácter es hecha con alguna combinación de ceros y unos, o con alguna otra. Los usuarios están interesados con el significado que pueda tener algún carácter, tratándose de alguna letra 10

esta podría ser una inicial, algún código, etc. Un carácter podría ser cualquier grupo de bits y no necesariamente representar una letra o un número; por ejemplo, algunos caracteres producen un sonido o controlan el monitor. También, los caracteres no son necesariamente creados con una sola tecla; por ejemplo, las secuencias escapadas son usadas para crea el carácter \n, el cual inicia una nueva línea. En ocasiones, se puede pensar en un carácter como una unidad de información en vez de un dato con una apariencia particular. Por ejemplo, el carácter matemático π 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ún 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úmero de seguridad social, código postal, o salario. Los campos están agrupados para formar registros. Un registro es una colección de campos que contienen datos acerca de una entidad. Por ejemplo, el nombre, los apellidos, el número de seguridad social, el código 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és 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ún campo, por ejemplo por el número de seguridad social. Cuando los registros no son usados en secuencia, el archivo es usado como archivo de acceso aleatorio. Cuando los registros están guardados en archivo de datos, sus campos pueden estar organizados en una línea, o un carácter 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á separado del siguiente por una coma. Este formato es empleado en bases de datos y hojas de cálculo. Antes de que una aplicación pueda usar un archivo de datos, esta deberá abrir el archivo. Una aplicación Java abre un archivo creando un objeto y asociándole un flujo de bytes. De igual manera, cuando se termina de usar un archivo, el programa deberá cerrar el archivo para que ya no siga disponible para la aplicación. Si se falla al cerrar un archivo de entrada, uno en el cual se están leyendo datos, usualmente no hay consecuencias; los datos todavía existirán en el archivo. Pero si se falla al cerrar un archivo de salida, uno en el cual se están escribiendo datos, los datos podrían hacerse inaccesibles. Siempre se deberá cerrar cada archivo que se abra, y se deberá cerrar tan pronto como ya no se ocupe. Cuando se deja un archivo abierto sin razón, este consume recursos del sistema, y el rendimiento del sistema decrece. Podría ser también 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ón de entrada en una aplicación, esto se puede ver como bytes yendo en el programa desde un dispositivo de entrada a través 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ón a través 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étodos. Los 11

métodos permiten realizar acciones como abrir, cerrar, leer, y escribir. La mayoría de los flujos van en una sola dirección, cada flujo es de entrada o salida. Se podrían abrir varios flujos simultáneamente en una aplicación. Las operaciones de entrada y salida son las más lentas en cualquier sistema de cómputo por las limitaciones impuestas por el hardware. Por esa razón, los programadores profesionales frecuentemente usan búfers. Un búfer es una localidad de memoria donde los bytes están guardados después de ser lógicamente escritos pero antes de ser enviados al dispositivo. Usar un búfer para acumular entrada o salida antes de realizar el comando de E/S mejora el rendimiento del programa. Cuando se usa un búfer de salida, en ocasiones se vacía antes de cerrarlo. El vaciar un búfer de memoria hace que se limpie cualquier byte que haya sido enviado a un búfer para salida pero que todavía no ha sido sacado a un dispositivo de hardware. 4. Clases entrada/salida Enseguida se muestra una relación jerárquica 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étodos que deben ser anuladas en sus clases hijas. Las capacidades de estas clases están resumidas en cuadro 2. Object +--InputStream +--FileInputStream +--FilterInputStream +--BufferedInputStream +--OutputStream +--FileOutputStream +--FilterOutputStream +--BufferedOutputStream +--PrintStream +--Reader +--BufferedReder +--BufferedWriter 12

Clase InputStream FileInputStream FilterInputStream Descripción Clase abstracta que contiene métodos para realizar entrada. Hija de InputStream que tiene capacidad para leer de los archivos de disco. 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 dispositivo de entrada estándar (o defecto) de un sistema, generalmente el teclado. OutputSream FileOutputStream FilterOutputStream Clase abstracta que contiene métodos para realizar la salida. Hija de OutputStream que permite escribir a archivos de disco. Es la superclase de todas las clases que filtran los flujos de salida. Estos flujos están en la cima de algún flujo de salida ya existente (el flujo de salida subyacente) el cual lo usa como su drenaje básico 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 dispositivo de salida estándar (o defecto) de un sistema, usualmente el monitor. PrintStream Reader BufferedReader BufferedWriter Hija de FilterOutputStream, la cual es a su vez hija de OutputStream; System.out es un objeto PrintStream. Clase abstracta para leer flujos de caracteres; los únicos métodos que una subclase debe implementar son read(char[], int, int) y close(). Lee texto de un flujo de caracteres de entrada, almacenando en el búfer caracteres para contar con lectura eficiente de caracteres, arreglos, y líneas. Escribe texto a un flujo de caracteres de entrada, almacenando en el búfer caracteres para contar con escritura eficiente de caracteres, arreglos, y líneas. Cuadro 2: Descripción de las clases seleccionadas usadas para entrada y salida 13

La clase OutputStream puede ser usada para producir salida. El cuadro 3 muestra algunos de los métodos 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étodo OutputStream void close() void flush() void write(btye[] b) void write(btye[] b, int desp, int tam) Descripción Cierra el flujo de salida y libera cualquier recurso del sistema asociado con el flujo. Vacía el flujo de salida; si algunos bytes están en el buffer, estos son escritos. Escribe todos los bytes al flujo de salida del arreglo de byte especificado. Escribe bytes al flujo de salida del arreglo especificado de byte iniciando en la posición desp para una cantidad tam de caracteres. Cuadro 3: Descripción 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étodos print() y println(). La clase System también 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álida. 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ódigo 7 se muestra como se hace. Para esto la aplicación SalidaAPantalla declara una String de calificaciones de letra permitidas en un curso. Luego, el método 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étodo write() acepta el arreglo byte y lo manda al dispositivo de salida, y luego el flujo de salida es vaciado y cerrado. 1 import java. i o. ; 2 public class SalidaAPantalla { 3 public static void main ( S t r i n g [ ] args ) { 4 S t r i n g s = ABCDEF ; 5 byte [ ] datos = s. getbytes ( ) ; 6 OutputStream s a l i d a = null ; 7 try { 8 s a l i d a = System. out ; 9 s a l i d a. w r i t e ( datos ) ; 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 ( Exception e ) { 14 System. out. p r i n t l n ( Mensaje : + e ) ; 15 } 16 } 17 } Código 7: La clase SalidaAPantalla. 14

Escribir a un archivo En vez de asignar el dispositivo de salida estándar al OutputStream, como se hace en la aplicación SalidaAPantalla, código 7, se puede hacer la asignación 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ón 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étodo newoutputstream() de la clase Files y pasándole un argumento Path y una StandardOpenOption. El método 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étodo newoutputstream. Si no se indica alguna opción 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ón. StandardOpenOption WRITE APPEND TRUNCATE EXISTING CREATE NEW CREATE DELETE ON CLOSE Descripción Abre el archivo para escritura. Agrega nuevos datos al final del archivo; usar esta opción con WRITE o CREATE. Trunca el archivo existente a cero bytes así el contenido del archivo es reemplazado; usar esta opción con WRITE. Crea un nuevo archivo sólo si este no existe; lanzando una excepción si el archivo ya existe. Abre un archivo si existe o crea uno nuevo si no existe. 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ón SalidaAArchivo, código 8, escribe una String de bytes a un archivo. Las diferencias respecto a la aplicación SalidaAPantalla, código 7, se resumen enseguida: Se usan sentencias adicionales import, líneas 2 y 3. El nombre de la clase está 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. 15

1 import java. i o. ; 2 import java. nio. f i l e. ; 3 import static java. nio. f i l e. StandardOpenOption. ; 4 public class SalidaAArchivo { 5 public static void main ( S t r i n g [ ] args ) { 6 Path a r c h i v o = Paths. get ( grados. txt ) ; 7 S t r i n g s = ABCDEF ; 8 byte [ ] datos = s. getbytes ( ) ; 9 OutputStream s a l i d a = null ; 10 try { 11 s a l i d a = new 12 BufferedOutputStream ( F i l e s. newoutputstream ( archivo, CREATE) ) ; 13 s a l i d a. w r i t e ( datos ) ; 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 ( Exception e ) { 18 System. out. p r i n t l n ( Mensaje : + e ) ; 19 } 20 } 21 } Código 8: Aplicación SalidaAArchivo. Leer desde un archivo Se usa un InputStream de forma similar a un OutputStream. Si se quiere, se puede crear un InputStream, asignándole System.in, y usando el método read() con el objeto creado para recuperar la entrada del teclado. Sin embargo, es más 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étodo newinputstream() de la clase Files para abrir un archivo para lectura. El método acepta un parámetro Path y devuelve un flujo que puede leer bytes de un archivo. La aplicación LeerArchivo, código 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últiples ĺıneas del archivo se podría usar un ciclo tal como el mostrado enseguida. El ciclo continúamente lee y muestra ĺıneas del archivo hasta que el método readline() regresa null para indicar que no hay más 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ácter, almacenar caracteres en el búfer hace que la lectura sea más eficiente. 16

1 import java. i o. ; 2 import java. nio. f i l e. ; 3 public class LeerArchivo { 4 public static void main ( S t r i n g [ ] args ) { 5 Path a r c h i v o = Paths. get ( grados. txt ) ; 6 InputStream entrada = null ; 7 try { 8 entrada = F i l e s. newinputstream ( archivo ) ; 9 BufferedReader l e c t o r = new BufferedReader ( 10 new InputStreamReader ( entrada ) ) ; 11 S t r i n g s = null ; 12 s = l e c t o r. readline ( ) ; 13 System. out. p r i n t l n ( s ) ; 14 l e c t o r. c l o s e ( ) ; 15 } 16 catch ( Exception e ) { 17 System. out. p r i n t l n ( Mensaje : + e ) ; 18 } 19 } 20 } Código 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étodos útiles de BufferedReader. Método BufferedReader close() read() read(char[] buffer, int de, int can) readline() skip(long n) Descripción Cierra el flujo y cualquier recurso asociado con este. Lee un sólo carácter. Lee caracteres en una porción de un arreglo desde la posición de para can caracteres. Lee una línea de texto. Salta el número especificado de caracteres. Cuadro 5: Métodos BufferedReader. 5. Archivos secuenciales de datos En ocasiones se quiere guardar algo más que un String en un archivo. Se podría tener un archivo de datos de registros personales que incluyan un número de identificación, un nombre, y un salario para cada empleado de una organización. La aplicación EscribirArchivoEmpleado, código 10, lee números de identificación, nombres y salarios desde el teclado y los manda, separados con comas, a un archivo. 1 import java. nio. f i l e. ; 2 import java. i o. ; 3 import static java. nio. f i l e. StandardOpenOption. ; 4 import java. u t i l. Scanner ; 5 public class EscribirArchivoEmpleado { 6 public static void main ( S t r i n g [ ] args ) { 17

7 Scanner entrada = new Scanner ( System. in ) ; 8 Path a r c h i v o = Paths. get ( empleados. txt ) ; 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 int id ; 12 S t r i n g nombre ; 13 double s a l a r i o ; 14 f i n a l int SALIR = 999; 15 try { 16 OutputStream s a l i d a = new 17 BufferedOutputStream ( F i l e s. newoutputstream ( archivo, CREATE) ) ; 18 BufferedWriter e s c r i t o r = new 19 BufferedWriter (new OutputStreamWriter ( s a l i d a ) ) ; 20 System. out. p r i n t ( I n g r e s a r número i d e n t i f i c a d o r >> ) ; 21 id = entrada. nextint ( ) ; 22 while ( id!= SALIR) { 23 System. out. p r i n t ( I n g r e s a r nombre d e l empleado # + 24 id + >> ) ; 25 entrada. nextline ( ) ; 26 nombre = entrada. nextline ( ) ; 27 System. out. 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 = entrada. nextdouble ( ) ; 29 s = id + 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 ength ( ) ) ; 31 e s c r i t o r. newline ( ) ; 32 System. out. p r i n t ( I n g r e s a r s i g u i e n t e número i d e n t i f i c a d o r o + 33 SALIR + para terminar >> ) ; 34 id = entrada. nextint ( ) ; 35 } 36 e s c r i t o r. c l o s e ( ) ; 37 } 38 catch ( Exception e ) { 39 System. out. p r i n t l n ( Mensaje : + e ) ; 40 } 41 } 42 } Código 10: La clase EscribirArchivoEmpleado. En el código 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úfer los caracteres. La clase tiene tres métodos write() sobrecargados que proporcionan escritura eficiente de caracteres de un String, de arreglos char, y de un carácter. El cuadro 6 contiene los métodos definidos en la clase BufferedWriter. En la aplicación 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á completo, con número de identificación, nombre y salario separados con comas, el String es enviado al escritor con lo indicado en la línea 30. El método write() acepta el String desde la posición cero con su tamaño completo. Después de que el String es escrito, se escribe enseguida el carácter de nueva línea del sistema. Un archivo de datos podría no requerir un caracter de nueva línea después de cada registro, cada 18

Método BufferedWriter close() flush() newline() write(string s, int de, int can) write(char[] a, int de, int can) write(int c) Descripción Cierra el flujo, vaciándolo primero. Vacía el flujo. Escribe un separador de línea. Escribe un String desde la posición de para una longitud can. Escribe un arreglo char desde la posición de para una longitud can. Escribe un sólo carácter. Cuadro 6: Métodos BufferedWriter. nuevo registro podría estar separado con una coma o cualquier otro carácter único 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étodo newline() que usa el separador de línea actual de la plataforma. También se podría escribir el valor de System.getProperty("line.separator"), que es como lo hace el método newline(). Cualquiera de los métodos de entrada o salida en el programa EscribirArchivoEmpleado podría lanzar una excepción, así que este código es puesto en bloque try. La aplicación LeerArchivoEmpleado, código 11, lee el archivo empleado.txt creado por la aplicación 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étodo readline() no devuelva null, el String es mostrado y una nueva línea es leída. 1 import java. i o. ; 2 import java. nio. f i l e. ; 3 public c l a s s LeerArchivoEmpleado { 4 public static void main ( S t r i n g [ ] args ) { 5 Path a r c h i v o = Paths. get ( empleados. txt ) ; 6 S t r i n g s = ; 7 try { 8 InputStream entrada = new 9 BufferedInputStream ( F i l e s. newinputstream ( a rchivo ) ) ; 10 BufferedReader l e c t o r = new BufferedReader ( 11 new InputStreamReader ( entrada ) ) ; 12 s = l e c t o r. readline ( ) ; 13 while ( s!= null ) { 14 System. out. p r i n t l n ( s ) ; 15 s = l e c t o r. readline ( ) ; 16 } 17 l e c t o r. c l o s e ( ) ; 18 } 19 catch ( Exception e ) { 20 System. out. p r i n t l n ( Mensaje : + e ) ; 21 } 22 } 23 } Código 11: La clase LeerArchivoEmpleado. 19

Otras aplicaciones podrían no querer usar el archivo de datos sólo como String como lo hace la aplicación LeerArchivoEmpleado. La aplicación LeerArchivoEmpleado2, código 12, también lee del archivo String pero las divide en campos usables. El método 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étodos como parseint() o parsedouble() pueden ser usados para obtener del String dividido sus respectivos tipos de datos. 1 import java. i o. ; 2 import java. nio. f i l e. ; 3 public c l a s s LeerArchivoEmpleado2 { 4 public static void main ( S t r i n g [ ] args ) { 5 Path a r c h i v o = Paths. get ( empleados. txt ) ; 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 int id ; 10 S t r i n g nombre ; 11 double s a l a r i o ; 12 double neto ; 13 f i n a l double HORAS SEMANA = 4 0 ; 14 double t o t a l =0.0; 15 try { 16 InputStream entrada = new 17 BufferedInputStream ( F i l e s. newinputstream ( a rchivo ) ) ; 18 BufferedReader l e c t o r = new 19 BufferedReader (new InputStreamReader ( entrada ) ) ; 20 s = l e c t o r. readline ( ) ; 21 while ( s!= null ) { 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 id = 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. parsedouble ( a r r e g l o [ 2 ] ) ; 26 neto = s a l a r i o HORAS SEMANA; 27 System. out. p r i n t l n ( id # + id + + nombre + 28 $ + s a l a r i o + $ + neto ) ; 29 t o t a l += neto ; 30 s = l e c t o r. readline ( ) ; 31 } 32 l e c t o r. c l o s e ( ) ; 33 } 34 catch ( Exception e ) { 35 System. out. p r i n t l n ( Mensaje : + e ) ; 36 } 37 System. out. p r i n t l n ( El pago t o t a l de l a nómina es $ + t o t a l ) ; 38 } 39 } Código 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ómina después de que todos 20

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és 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én llamados de acceso directo o de acceso instantáneo, 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ón especificada. El cuadro 7 muestra algunos métodos FileChannel. Método FileChannel FileChannel open(path arch, OpenOption... opciones) long position() FileChannel position(long nuevaposicion) int read(bytebuffer búfer) long size() int write(bytebuffer búfer) Descripción Abre o crea un archivo, devolviendo un canal de archivo. Devuelve la posición del canal de archivo. Pone la nueva posición del canal de archivo. Lee una secuencia de bytes del canal en el búfer. Devuelve el tamaño actual del archivo de canal. Escribe una secuencia de bytes al canal desde el búfer. Cuadro 7: Métodos FileChannel. Varios métodos del cuadro 7 usan un objeto ByteBuffer. Un ByteBuffer es un lugar para bytes que están esperando ser leídos o escritos. Un arreglo de bytes puede ser envuelto, o empacado, en un ByteBuffer usando el método wrap() de ByteBuffer. Envolviendo un arreglo de bytes en un búfer hace que los cambios hechos al búfer también 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étodo newbytechannel() de la clase Files para obtener un ByteChannel para un Path. El método newbytechannel acepta argumentos Path y StandardOpenOption que indicar como el archivo será abierto. El ByteChannel regresado por el método newbytechannel() puede entonces ser convertido a un FileChannel usando una sentencia parecida a la siguiente: 21

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étodo 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ón: fc.write(salida); Se puede revisar si el contenido de un ByteBuffer ha sido usado mediante el método has- Remaining(). Después de escribir el contenido de un ByteBuffer, se puede escribir el mismo contenido del ByteBuffer nuevamente usando el método rewind() para reposicionar el ByteBuffer al inicio del búfer. La aplicación PruebaAccesoAleatorio, código 13 usa los pasos previos para declarar un archivo y escribir algunos bytes aleatoriamente en las posiciones 0, 22, y 12. 1 import java. i o. ; 2 import java. nio. f i l e. ; 3 import java. nio. channels. FileChannel ; 4 import java. nio. ByteBuffer ; 5 import static java. nio. f i l e. StandardOpenOption. ; 6 public class PruebaAccesoAleatorio { 7 public static void main ( S t r i n g [ ] args ) { 8 Path a r c h i v o = Paths. get ( numeros. txt ) ; 9 creararchivo ( a r c h i v o ) ; 10 S t r i n g s = ABC ; 11 byte [ ] datos = s. getbytes ( ) ; 12 ByteBuffer s a l i d a = ByteBuffer. wrap ( datos ) ; 13 FileChannel f c = null ; 14 try { 15 f c = ( FileChannel ) F i l e s. newbytechannel ( archivo, 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. rewind ( ) ; 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. rewind ( ) ; 22

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 ( Exception e ) { 30 System. out. p r i n t l n ( Mensaje : + e ) ; 31 } 32 } 33 public static void creararchivo ( Path a rchivo ) { 34 S t r i n g s = 0123456789012345678901234567890123456789 ; 35 try { 36 OutputStream s a l i d a = new 37 BufferedOutputStream ( F i l e s. newoutputstream ( archivo, CREATE) ) ; 38 s a l i d a. w r i t e ( s. getbytes ( ) ) ; 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 ( Exception e ) { 43 System. out. p r i n t l n ( Mensaje : + e ) ; 44 } 45 } 46 } Código 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ón PruebaAccesoAleatorio, es de valor limitado. Cuando se guardan registros en un archivo, el acceso es más útil por registro, como acceder el quinto registro, en vez de recuperar el quinto byte. Para lograr lo anterior, se debe multiplicar la posición del registro que se quiere acceder por el tamaño del registro. Por ejemplo, si se guardan registros que son de tamaño 50 bytes, el primer registro está en la posición cero, el segundo registro en la posición 50, el tercero en la posición 100, y así sucesivamente. Es decir, se puede acceder el n-ésimo registro en un FileChannel llamado fc con la siguiente sentencia: fc.position((n - 1) * 50); Una aproximación 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 único del resto. Por ejemplo, suponer que se quiere guardar números 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úmero identificador de empleado, así que este campo puede ser el campo llave. El primer paso en la creación del archivo empleado de acceso aleatorio es la creación de un archivo que guarde los registros por defecto por ejemplo, usando ceros para los números identificadores 23

y los salarios y blancos para los apellidos. Para este caso, suponer que cada número identificador de empleado es de tres dígitos. La aplicación CrearArchivoVacioEmpleados, código 14, crea 1,000 registros del tipo descrito. 1 import java. i o. ; 2 import java. nio. ByteBuffer ; 3 import java. nio. f i l e. ; 4 import static java. nio. f i l e. StandardOpenOption. ; 5 public c l a s s CrearArchivoVacioEmpleados { 6 public static void main ( S t r i n g [ ] args ) { 7 Path a r c h i v o = Paths. get ( e m p l e a d o s a l e a t o r i o. txt ) ; 8 S t r i n g s = 000,, 0 0 0. 0 0 + 9 System. getproperty ( l i n e. s e p a r a t o r ) ; 10 f i n a l int NUM REGS = 1000; 11 try { 12 OutputStream s a l i d a = new 13 BufferedOutputStream ( F i l e s. newoutputstream ( archivo, CREATE) ) ; 14 BufferedWriter e s c r i t o r = new 15 BufferedWriter (new OutputStreamWriter ( s a l i d a ) ) ; 16 for ( int cont = 0 ; cont < NUM REGS; ++cont ) 17 e s c r i t o r. w r i t e ( s, 0, s. l ength ( ) ) ; 18 e s c r i t o r. c l o s e ( ) ; 19 } 20 catch ( Exception e ) { 21 System. out. p r i n t l n ( Mensaje : + e ) ; 22 } 23 } 24 } Código 14: Aplicación CrearArchivoVacioEmpleados. En las líneas 8 9 del código 14, un String que representa un registro por defecto es declarado. El número de empleado de tres dígitos es puesto a ceros, el apellido consiste de veinte blancos, y el salario es 000.00, además 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úfer. Luego un archivo es abierto en modo CREATE y un BufferedWriter es establecido. En las líneas 16 17 del código 14, un ciclo se ejecuta mil veces. Dentro del ciclo, la cadena por defecto empleado es pasada al método write() del objeto BufferedWriter. Los campos por defecto en el archivo base de acceso aleatorio se pueden inicializar con los valores que sean más convenientes para la organización. El único requisito en la inicialización es que los registros por defecto sean reconocidos como tales. Después de crear el archivo por defecto base, se puede reemplazar cualquiera de sus registros con datos para un empleado actual. La aplicación CrearUnRegistroAccesoAleatorio, código 15, crea un sólo 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ño de la cadena es asignado a TAM REG. En este caso el tamaño 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és el FileChannel es establecido, el registro es escrito al archivo en la posición que inicia en dos veces el tamaño del registro. El valor dos es codificado en esta aplicación porque el número identicador de empleados es 002. 24