Skip to content

1 Introducción al Manejo de Ficheros

Introducción

Un fichero o archivo es un conjunto de bits almacenados en un dispositivo. Los ficheros son no volátiles, es decir, que los datos que almacenados permanecen en el dispositivo incluso cuando se apaga el ordenador.

Los ficheros tienen un nombre y se ubican en directorios o carpetas, Dicho nombre ha de ser único y no se debe repetir en el mismo directorio.

Por lo general, los ficheros cuenta con diferentes extensiones, que por lo general suele ser 3 letras y nos permiten saber el tipo de fichero.

Un fichero está formado por un conjunto de registros o líneas y cada registro por un conjunto de campos relacionados, por ejemplo, un fichero de empleados puede contener datos de los empleados de una empresa, un fichero de texto puede contener líneas de texto correspondientes a líneas impresas en una hoja de papel.

Clases asociadas a las operaciones de Gestión de Ficheros

El paquete java.io contiene las clases para manejar la entrada/salida en Java, por tanto, necesitaremos importar dicho paquete cuando trabajemos con ficheros.

La clase File proporciona un conjunto de utilidades relacionadas con los ficheros que nos van a proporcionar información acerca de los mimos (su nombre, atributos, directorios, etc)

Para crear un objeto de tipo File se puede usar cualquiera de estos tres constructores.

  • File(String directoryFile). Usa como argumento el directorio del fichero en forma de cadena.
  • File(String directorio, String nombreFichero). Usa como argumento el directorio padre (directorio que contiene el fichero) del fichero y su nombre, ambas en forma de cadena.
  • File(File directorio, String fichero). Usa como argumento el directorio padre en forma de tipo File y el nombre del fichero en cadena.

En Linux se utiliza como prefijo de una ruta absoluta "/", En Microsoft Windows, el prefijo de un nombre de ruta consiste en la letra de la unidad seguida de ":" y seguida por "\\" si la ruta es absoluta

//Windows
File ficherol = new File("C:\\EJERCICIOS\\UNil\\ejemplol.txt");
//Linux
File ficherol = new File("/home/ejercicios/unil/ejemplol.txt");
String directorio= "C:/EJERCICIOS/UNil";
File fichero2 = new File(directorio, "emplo2.txt");
File direc = new File(directorio);
File fichero3 = new File(directorio);

Algunos de los métodos más importantes de la clase File son los siguientes:

Método Función
String[] list() Devuelve un array de String con los nombre de los ficheros y directorios asociados al objeto File.
File[] listFiles() Devuelve un array de objetos File conteniendo de los ficheros que estén dentro del directorio representado por el objeto File.
String getName() Devuelve el nombre del fichero o directorio.
String getPath() Devuelve la ruta relativa
String getAbsolutePath() Devuelve la ruta absoluta
boolean exists() Devuelve true si el fichero o directorio existe.
boolean canWrite() Devuelve true si el fichero se puede escribir.
boolean canRead() Devuelve true si el fichero se puede leer.
boolean isFile() Devuelve true si el objeto File corresponde a un fichero normal.
boolean isDirectory() Devuelve true si el objeto File corresponde a un directorio.
long legth() Devuelve el tamaño del fichero en bytes.
boolean mkdir() Crea un directorio con el nombre indicado en la creación del objeto File. Solo se creará si no existe.
boolean renameTo(File nuevoNombre) Renombra el fichero representando por el objeto File asignándole nuevo nombre.
boolean delete() Borra el fichero o directorio asociado al objeto File.
boolean createNewFile() Crea un nuevo fichero, vacío, asociado File si y solo si no existe un fichero con ese nombre
String getParent() Devuelve el nombre del directorio padre, o null si no existe.
public class ListDir {
    public void show() {
        String dir = "."; // directorio actual
        File f = new File(dir);
        String[] files = f.list();
        System.out.printf("Ficheros en el directorio actual: %d \n",
                files != null ? files.length : 0);

        for (String archivo : files) {
            File f2 = new File(f, archivo);
            System.out.printf("Nombre: %s, es fichero?: %b, es directorio?: %b \n",
                    archivo, f2.isFile(), f2.isDirectory());
        }
    }

    public static void main(String[] args) {
        new ListDir().show();
    }
}

En el siguiente ejemplo mostramos información referente a un fichero:

public class FileInfo {
    public void show() {
        System.out.println("INFORMACIÓN SOBRE EL FICHERO");
        File f = new   File("src/exercises/bloque_iv/tema_14/FileInfo.java");
        if(f.exists()){
            System.out.println("Nombre del fichero          : " + f.getName());
            System.out.println("Ruta            : " + f.getPath());
            System.out.println("Ruta Absoluta             : " + f.getAbsolutePath());
            System.out.println("Se puede leer             : " + f.canRead());
            System.out.println("Se puede escribir           : " + f.canWrite());
            System.out.println("Tamaño               : " + f.length());
            System.out.println("Es un directorio         : " + f.isDirectory());
            System.out.println("Es un fichero             : " + f.isFile());
            System.out.println("Nombre del directorio padre :" + f.getParent());
        }
    }

    public static void main(String[] args) {
        new FileInfo().show();
    }
}

Por último, vemos un ejemplo para crear directorios y ficheros y renombrarlos:

public class RenameFile {
    public void show() {
        File d = new File("DIR");
        File f1 = new File(d,"file1.txt");
        File f2 = new File(d,"file2.txt");
        File f3 = new File("DIR/file3.txt");
        File renameFile = new File(d, "file1_change.txt");
        boolean success;

        success = d.mkdir();

        System.out.printf("La carpeta %s%s ha sido creada con éxito\n", d.getName(), success ? "" : "no");

        try{
            success = f1.createNewFile();
            System.out.printf("Fichero %s%s ha sido creado con éxito\n", f1.getName(), success ? "" : "no");

            success = f2.createNewFile();
            System.out.printf("Fichero %s%s ha sido creado con éxito\n", f2.getName(), success ? "" : "no");
        } catch (IOException e) {
            System.err.println("Error de entrada y salida");
        }

        success = f1.renameTo(renameFile);
        System.out.printf("%s ha cambiado el fichero %s a %s\n", success ? "Se" : "No se", 
                f1.getName(), f3.getName());

        try {
            success = f3.createNewFile() ;
            System.out.printf("Fichero %s%s ha sido creado con éxito\n", f3.getName(), success ? "" : "no");
        } catch (IOException ioe) {
            System.err.println("Error de entrada y salida");
        }

    }

    public static void main(String[] args) {
        new RenameFile().show();
    }
}

El método createNewFile() puede lanzar la excepción IOException, por ello se introduce en un bloque try-catch.

Por último, vamos a ver un ejemplo de como eliminar un fichero:

public class DeleteFile {
    public void show() {
        File f = new File("DIR/file3.txt");
        boolean success = f.delete();

        System.out.printf("%s ha eliminado correctamente el fichero %s\n", success ? "Se" : "No",
                f.getName());
    }

    public static void main(String[] args) {
        new DeleteFile().show();
    }
}

Flujos o Streams: Tipos

El sistema de entrada/salida en Java presenta una gran cantidad de clases que se implementan el paquete java.io. Usa la abstracción del flujo (stream) para tratar la comunicación de información entre una fuente y un destino.

Cualquier programa que tenga que obtener información de cualquier fuente necesita abrir in stream, si necesita evitar información abrirá un stream y se escribirá la información en serie. Se define dos tipos de flujos:

  • Flujos de bytes (8 bits): Realizan operaciones de entradas y salidas de bytes y su uso está orientado a la lectura/escritura de datos binarios. Todas las clases de flujos de bytes descienden de las clases InputStream y OutputStream, cada vez una de estas clases tienen varias subclases que controlan las diferencias entre los distintos dispositivos de entrada/salida que se pueden utilizar.
  • Flujos de caracteres (16 bits): Realizan operaciones de entradas y salidas de caracteres. El flujo de caracteres viene gobernado por las clases Reader y Writer.

Flujos de Bytes

La clase InputStream representa las clases que producen entradas de distintas fuentes, estas fuentes pueden ser: un array de bytes, un objeto de String, un fichero, una "tubería", una secuencia de otros flujos, otras fuentes.

La clase InputFileStream es el flujo de entrada hacia un fichero, y se usa para leer la información de un fichero.

Los tipos de OutputStream incluyen clases que deciden dónde irá la salida: a un array de bytes. un fichero o una tubería.

La clase OutputFileStream es el flujo de salida hacia un fichero, y se usa para enviar información a un fichero.

FileInputStream y FileOutputStream manipulan los flujos de bytes provenientes o dirigidos en disco.

Flujo de Caracteres

Las clases Reader y Writer manejan flujos de caracteres Unicode. Hay ocasiones en las que hay que usar las clases que manejan bytes en combinación con las clases que manejas caracteres. Para lograr esto hay clases puentes: InputStreamReader que convierte un InputStream en un Reader y OutputStreamWriter que convierte un OutputStream en un Writer.

Las clases de flujos de caracteres más importantes son:

  • Para acceso a ficheros, lectura y escritura de caracteres en ficheros: FileReader y FileWriter.
  • Para acceso a caracteres, leen y escribe un flujo de caracteres en un array de caracteres: CharArrayReader y CharArrayWriter.
  • Para bufferización de datos: BufferedReader y BufferedWriter, se utilizan para evitar que cada lectura o escritura acceda directamente al fichero, ya que utilizan un buffer intermedio entre la memoria y el stream.

Formas de Acceso a un fichero

Hay dos formas de acceso a la información almacenada en un fichero: acceso secuencial y acceso directo o aleatorio:

  • Acceso secuencial: los datos o registros se leen y se escriben en orden, del mismo modo que se hace en una antigua cinta de audio (cassette). Si se quiere acceder a un dato o un registro que está hacia la mitad del fichero es necesario leer antes todos los anteriores. La escritura de datos se hará a partir del último dato escrito, no es posible hacer inserciones entre los datos que ya hay escritos.
  • Acceso directo o aleatorio: permite acceder directamente a un dato o registro sin necesidad de leer los anteriores y se puede acceder a la información en cualquier orden. Los datos están almacenados en registros de tamaño conocido, nos podemos mover de un registro a otro de forma aleatoria para leerlos o modificarlos, como uno de los CDs actuales.

En Java el acceso secuencial más común en ficheros puede ser binario o a caracteres. Para el acceso binario: se usan las clases FileInputStream y FileOutputStream; para el acceso a caracteres (texto) se usan las clases FileReader y FileWriter. En el acceso aleatorio se utiliza la clase RandomAccessFile.