Manexo de ficheiros

De MediaWiki
Revisión del 14:48 28 feb 2022 de Joselv (discusión | contribuciones) (Página creada con «= Os ficheiros = Na programación adoitan usarse variables para almacenar información que queremos tratar durante a execución: os datos de entrada, resultados e valores i…»)
(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Ir a la navegación Ir a la búsqueda

Os ficheiros

Na programación adoitan usarse variables para almacenar información que queremos tratar durante a execución: os datos de entrada, resultados e valores intermedios.

Con todo, a información almacenada nas variables é volatil, desaparendo cando remata a execución do programa. Cando queremos almacenar de xeito permanente, fálase de persistencia, sendo dúas as formás máis habituales de facelo: organizado a información nun ou varios ficheiros nun sistema de almacenamento persistente facendo uso das facilidades que nos permiten as bases de datos

Neste capítulo introduciremos a primeira das opcións; e para iso, comezaremos abordando a súa definición desde o punto de vista de máis baixo nivel:

Un ficheiro é un conxunto de bits almacenado nun dispositivo accesible a través dunha ruta (pathname) que o identifica.

Básicamente, unha secuecia de 1s e 0s que son almacenados fóra da memoria volátil do equipo (nun disco duro ou nun DVD, por exemplo).

Desde un nivel máis alto, e para o que nos interesa desde o punto de vista da programación, esta é a información que debemos ter en conta. http://ocw.udl.cat/enginyeria-i-arquitectura/programacio-2/continguts-1/4-manejo-bai81sico-de-archivos-en-java.pdf

Tipos de fichero segundo a súa estrutura de almacenamento

Podemos clasificalos atendendo a súa estrutura de almacenamento

Por contido

Podemos atopar os seguintes tipos básicos de ficheiros:

Texto plano Almacenan secuencias de caracteres correspondentes a unha codificación determinada (ASCII, Unicode, EBCDIC, etc.). Son lexibles mediante un software de edición de texto coma o Bloc de Notas de Windows o os editores Vi ou nano de Linux.

Exemplos:

os ficheiros de texto con extensión .txt
os .csv de valores separados por comas
os .htm y .html correspondentes a páginas web
os de linguaxes de marcas .xml o .rss.
O sistema de codificación de caracteres máis popular é o código ASCII (American Standard Code for Information Interchange, código estándar estadounidense para intercambio de información), que define 256 caracteres distintos (todas as combinacións de 8 bits,dous elevado a 8 posibilidades). Algúns deles, chamados caracteres de control, encarganse de definir accións coma o borrado, o salto de liña ou o tabulador e non representan símbolos concretos.
Pequeno xogo
Busca en internet unha táboa de códigos ascii de 8 bits. Observa as seguintes características:
Os 32 primeros caracteres, chamados caracteres non imprimibles, utilizábanse tradicionalmente para o control de transmisións.
A distancia entre maiúsculas e minúsculas é exactamente 32 caracteres.
Hai caracteres que son numéricos, e que o seu valor ascii é o resultado de sumarlle 48. Por exemplo, 6+48=54. 54 é o código ascii do caracter 6.

Binarios Conteñen información codificada en binario para o seu procesamento por parte de aplicacións. O seu contido resulta ilexible nun editor de texto.

Exemplos: executables (.exe) documentos de aplicacións (.pdf, .docx, .xlsx, .pptx) fiicheiros de imaxe, audio ou vídeo (.jpg, .gif, .mp3, .avi, .mkv) librerías de sistema (.dll).

Por organización interna

Cando se utilizan ficheiros de texto plano para almacenar información podense clasificar dacordo a súa organización interna:

Secuenciais

A información escríbese en posicións físicamente contiguas. Para acceder a un dato hay que recorrer todos os anteriores.

Exemplo de ficheiro secuencial con información sobre clientes:

00789521T#Alba#Blanco#González#613524647$11111111H#Xosé Ramon#García#Pemán#613423824$38546998X#Xurxo#Cazás#Otero#666332245$09653801B#Antón#Resines#Pardo#619335241%

Por cada contacto estructurouse a información en cinco datos independeentes: NIF, nome, primer apelido, segundo apelido e número de teléfono. Neste caso o programador decidiu utilizar o grade (#) como separador de datos, o dólar ($) como separador de contactos e o tanto por cento (%) coma marca de fin de ficheiro.

De acceso directo ou aleatorio Cada liña de contido organízase cuns tamaños fixos de dato. Pódese acceder directamente ó principio de cada liña.

Nesta ocasión cada contacto ocupa unha liña do ficheiro (o final de cada unha o sistema operativo incluirá un ou dos caracteres de salto de liña invisibles para o usuario), e cada dato utiliza un número de caracteres fixo, aínda que non o ocupe totalmente (no exemplo reservanse 15 caracteres para o nome, ainda que no caso de Alba só se utilicen catro).

Coma todos os clientes ocupan o mesmo espacio no fichero, podemos acceder fácilmente a calquiera deles multiplicando a posición na que se atopa menos una polo número de caracteres que mide cada liña. Por exemplo, se o fichero foi creado nun sistema Windows e queremos acceder o terceiro cliente, teremos que restar un á súa posición (3 − 1 = 2) e multiplicar o valor resultante pola lonxitude da liña (63 caracteres máis os dous caracteres de salto de liña que inclúe o sistema operativo, é decir, 65). Como 2 × 65 = 130, a información do terceiro cliente atoparase na posición 131.

A contrapartida a esta facilidad de posicionamiento é que o tamaño do ficheiro crece considerablemente respecto á súa versión secuencial.

Indexados

Xeralmente nun ficheiro de acceso aleatorio a información almacénase na orde en que se da de alta. Aínda que se conseguira introducir a información de acordo a algún criterio de ordenación concreto, nalgunhas ocasións é útil poder ordeala por varios criterios distintos. No exemplo anterior é posible que necesitemos un listado de clientes ordenado por NIF e outro polo apelido. Para dar solución a este problema creouse a organización indexada, que consiste na existencia de un ou varios archivos adxuntos que ordenan o dato (chamado clave) polo que se desexa ordear o ficheiro e o relacionan coa localización da liña correspondente.

Por tipos de soporte de almacenamento

De acordo á organización física dos datos, diferenciamos entre dous tipos de soportes

Secuenciais

Para acceder a un dato hai que recorrer todo o contido do soporte previo ó devandito dato (por exemplo, as cintas magnéticas).

Direccionables

Pódese acceder directamente a un dato sen ter que recorrer todos os anteriores (por exemplo un disco duro).

Nun soporte direccionable pódese implementar un acceso secuencial, directo ou indexado, mentras que nun soporte secuencial só se poderá implementar un acceso secuencial. Por xerarquía de memorias


https://es.wikipedia.org/wiki/Jerarqu%C3%ADa_de_memoria#/media/Archivo:Jerarquia_memoria.png

- Intro de ficheros teórica (algo light es de segundo), tipos (temporales),jerarquía de los datos,codificaciones, flujos y accesos.Y tratamiento de excepciones:

Cuestións relevantes a ter en conta Codificación

Tratamento dos saltos de liña Un fin de liña pode estar indicado por: o caracter line-feed (‘\n’), utlizado en contornas tipo Unix o caracter carriage-return (‘\r’), utilizado en versións antguas de MacOs o caracter carriage-return seguido de line-feed, en contornas Windows É preciso ter en conta isto para manipular ficheiros de xeito uniforme entre sistemas operativos.

E/S con fluxos

En Java defínese a abstracción de stream (fluxo) para tratar a comunicación de información entre o programa e o exterior. Entre unha fonte e un destino fluye unha secuencia de datos: os fluxos, que actúan coma interfaz co dispositivo ou clase asociada. Así permiten: Operación independente do tipo de datos e do dispositivo Maior flexibilidade (redirección, combinación, ….) Diversidade de dispositivos (ficheiro, pantallas, teclado, rede, …) Diversidade de formas de comunicación (con modo de acceso secuencial ou aleatorio, e información intercambiada que pode ser binaria, caracteres ou liñas)

Hai unha certa analoxía entre o funcionamento en Unix e en Java.



Unix Java Entrada estándar Habitualmente teclado System.in Saída estándar Habitualmente a consola System.out Saída de erro Habitualmente a consola System.err


En Java accedese á E/S estándar a través dos campos estáticos da clase java.lang.System indicados.

Utilización dos fluxos Lectura 1. Abrir un fluxo a una fuente de datos (creación del objeto stream) • Teclado • Fichero • Socket remoto 2. Mientras existan datos disponibles • Leer datos 3. Cerrar el fluxo (método close) Escritura 1. Abrir un fluxo a una fuente de datos (creación del objeto stream) • Pantalla • Ficheiro • Socket local 2. Mentres existan datos disponibles • Escribir datos 3. Cerrar o fluxo (método close)

A ter en conta: o sistema xa se encarga de abrir e pechar os fluxos estándar un fallo en calquera punto produce a excepción IOException Para o que estamos a tratar, serán de intere os File streams para escribir e ler datos en ficheiros

Exemplo de entrada de texto desde un ficheiro

try {
 BufferedReader reader =
 new BufferedReader(new
 FileReader(“nomeficheiro"));
 String linea = reader.readLine();
 while(linea != null) {
  // procesar o texto da liña
  linea = reader.readLine();
 }
reader.close();
 }
 catch(FileNotFoundException e) {
 // non se atopou o ficheiro
 }
 catch(IOException e) {
 // algo foi mal ó ler ou pechar o ficheiro
 }

De xeito xeral, pódese comprender o funcionamento do código… pero para mellorar o seu entendemento, explicaremos qué son as excepcións e como facer uso das mesmas. https://www.fdi.ucm.es/profesor/jpavon/poo/2.13.EntradaySalida.pdf

Xestión de erros e excepcións

Non debe ser o tratamento de excepcións o tema central das explicacións para a xestión de ficheiros. Con todo, xa que o seu tratamento será preciso, faremos unha breve exposición simplificada das diversas posilibadaes para capturas os erros.

try-catch

O código que manipule ficheiros poderá ter a seguinte estrutura

try{
   //Código que abre e trata o ficheiro
} catch (IOException ex){
   //Código que trata o erro 
}

https://docs.oracle.com/javase/tutorial/essential/exceptions/try.html https://docs.oracle.com/javase/tutorial/essential/exceptions/catch.html

throws

Se no canto de tratar o erro nos queremos indicar que o noso método pode lanzar excepcións:

public int methodThatCanThrow(params) throws IOException{
//Código que trata ficheiros pero non atrapa IOException
}

finally

O bloque finally contén código importante que será executado indistintamente de que salte unha excepción ou non. No caso de ficheiros, será habitual incluir neste bloque o peche do stream, para asegurar que todas os ficheiros abertos son pechados apropiadamente; e así, os datos dos ficheiros non se poderán corromper.

import java.io.*;

public class SimpleExceptionHandling {

public static void main(String[] args) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream("C:\\UnFicheiroTemporal");

} catch (FileNotFoundException e) { System.out.println("Aconteceu a excepción :: " + e.getMessage()); System.out.println("Necesitamos pechar FileStream!"); } finally { System.out.println("Afortunadamente pecharase neste bloque"); try { if (fileInputStream != null) { fileInputStream.close(); } } catch (IOException e) { } } }

}

Saída:

Aconteceu a expeción :: C:\UnFicheiroTemporal (The system cannot find the file specified)
Necesitamos pechar FileStream!
Afortunadamente pecharase neste bloque 

E importante remarcar que o finally tamén se executaría no caso de non terse producida a excepción.

https://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html https://www.geeksforgeeks.org/why-you-need-to-close-the-java-streams-in-finally-block/

try-with-resources

Mecanismo que automáticamente pecha recursos (pode ser máis de un) cando se acabou o seu traballo con eles.

Por exemplo:

private static void printFile() throws IOException {
   try(FileInputStream input = new FileInputStream("ficheiro.txt")) {
       int data = input.read();
       while(data != -1){
           System.out.print((char) data);
           data = input.read();
       }
   }
}

Neste exemplo ábrese un FileInputStream dentro dun bloque a try-with-resources block, lee datos do FileInputStream, e pechao automaticamente cando a execución sae do bloque. Isto último e posible porque FileInputStream implementa a interface java.lang.AutoCloseable.

https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html http://tutorials.jenkov.com/java-exception-handling/try-with-resources.html

Exercicio para adaptar código para a xestión correcta de recursos e excepcións.

Un apartado →https://jesusfernandeztoledo.com/ejercicios-resueltos-ficheros-en-java/ Otro apartado de adaptar un código que quieras a try -with-resources Y último: pag 27 sin excepciones y que se pongan. Sol→ https://www.unirioja.es/cu/jearansa/0910/archivos/EIPR_Tema05.pdf

2. A clase File Utilizaremos a versión de Java JDK 17, unha versión LTS (long term support, con soporte a largo prazo).

Podes consultar a documentación dea biblioteca estándar de clases en https://docs.oracle.com/en/java/javase/17/ .

En concreto as clases que permiten traballar con ficheiros están no paquete java.io, onde na ***documentación premendo sole el atoparás a lista de interfaces e clases que contén o paquete.

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/File.html

O máis relevante pódes atopalo a continuación: Constructores File(String ruta) File(String ruta, String nombre) File(File directorio, String nombre) Métodos canRead() e canWrite() : comproba se o ficheiro se pode ler/escribir delete(): borra o ficheiro getPath(): devuelve a ruta do ficheiro mkdir(): crea un directorio coa ruta do obxecto que o recibe isDirectory() comproba se o ficheiro é un directorio

Podes atopar na coñecida web de referencia w3schools exemplos explicativos con código de manexo de ficheiros con estes métodos: https://www.w3schools.com/java/java_files.asp

Con todo, dende Java SE 7, existe un xeito mellor de xestionar os ficheiros: java.nio.

Entón, se existe unha forma máis axeitada de xestionar os ficheiros en Java… por qué aprendemos a utilizar esta? Porque debido o enorme número de aplicacións que usan a antigua API (o que se chame legacy), Oracle non ten plan de eliminar ou desaconsellar o seu uso nas vindeiras versións de Java. E por iso, porque podes atopar un montón de código en funcionamento o que lle tes que dar mantemento (ou facer migración), e que non vai desaparecer de xeito inminente, e bo coñecer esta API

Exercicio: adaptar copia de ficheiros para uso de excepcións

Exercicio de ampliación: serialización de obxectos java.nio para xestión de ficheiros e directorios Agora que xa coñecemos esta nova posibilidade de traballar con ficheiros, que permite contar con toda a funcionalidade que se acadaba antes, indagaremos sobre qué melloras trae e como traballar coa nova API.. Melloras de java.nio Cales son esas melloras na xestión? Por unha banda, permite facer unha mellor xestión de erros.

Poñamos este código de exemplo

File file = new File("probas/titorial.txt");
boolean result = file.delete();

Compila e pode funcionar mais no caso de fallar… cómo saberemos se é porque o ficheiro non existe, ou é porque non temos permisos para borrar?

Rescribindo o código coa nova NIO2 API:

Path path = Paths.get("probas/titorial.txt");
Files.delete(path);

Agora será preciso xestionar unha IOException, que dará os detalles de que foi o que aconteceu (por exemplo, non ter permisos de borrado).

A nova versión tamén mellora o soporte ós metadatos e trae melloras de rendemento evitando problemas de insuficiencia de memoria.

Diferencias con file e como facer uso de java.nio A primeira diferencia que salta rápido a vista dun programador é o cambio no nome do paquete e da clase (observables en letra grosa os cambios):

java.io.File file = new java.io.File("probas/titorial.txt");
java.nio.file.Path path = java.nio.file.Paths.get("probas/titorial.txt");

Antes construíamos un obxecto file via o construtor; agora obteremos un Path usando un método static (get).

Podemos obter o mesmo resultado encadeando o método resolve: Path outroXeitoPath = Paths.get("probas").resolve("titorial.txt");

Finalmente, é posibel convertir obxectos entre APIs:

Path pathFromFile = file.toPath();
File fileFromPath = path.toFile();

Both APIs deliver methods to manage files and directories. We'll demonstrate this using the previously created instance objects.

Para crear files usaremos os métodos createNewFile() e Files.createFile():

boolean result = file.createNewFile();
Path newPath = Files.createFile(path);

Para crear un directorio, mkdir() ou Files.createDirectory():

boolean result = file.mkdir();
File newPath = Files.createDirectory(path);

Hai variantes para incluir subdirectorios que non existan con mkdirs() e Files.createDirectories():

boolean result = file.mkdirs();
File newPath = Files.createDirectories(path);

Para renomear ou mover un ficheiro, precisamos crear outra instancia do obxecto e usar renameTo() ou Files.move():

boolean result = file.renameTo(new File("baeldung/tutorial2.txt"));
Path newPath = Files.move(path, Paths.get("baeldung/tutorial2.txt"));

Finalmente para borrar, usaremos delete() ou Files.delete():

boolean result = file.delete();
Files.delete(Paths.get(path));

Lembra que os métodos legacy devolven un flag cun result set a falso en caso de erro; e os métodos NIO2 devolve unha instancia de Path instance (agás a operación de borrado que lanza unha IOException en caso de erro).

Exercicio para indagar sobre metadatos Sabemos cómo consultar se un file existe con java.io e java.nio API // java.io API boolean fileExists = file.exists();

// java.nio API boolean pathExists = Files.exists(path);

Cal sería o código para saber se o indicado no path é u ficheiro? E se é posible escribir no mesmo? Amosa o código para as dúas apis:

Solución // java.io API boolean fileIsFile = file.isFile(); boolean fileWritable = file.canWrite();

// java.nio API boolean pathIsFile = Files.isRegularFile(path); boolean pathWritable = Files.isWritable(path); https://www.baeldung.com/java-path-vs-file

Exercicio para indagar sobre rutas (absoluta e canónica) Consulta a ruta canónica é absoluta dun ficheiro (o que ti escollas). Amosa o código para as dúas APIs.:

Solución // java.io API

String absolutePathStr = file.getAbsolutePath();
String canonicalPathStr = file.getCanonicalPath();

// java.nio API

Path absolutePath = path.toAbsolutePath();
Path canonicalPath = path.toRealPath().normalize();

Exercicio para indagar sobre rutas (absoluta e canónica) Consulta a ruta canónica é absoluta dun ficheiro (o que ti escollas). Amosa o código para as dúas APIs.:

Solución // java.io API

String absolutePathStr = file.getAbsolutePath();
String canonicalPathStr = file.getCanonicalPath();

// java.nio API

Path absolutePath = path.toAbsolutePath();
Path canonicalPath = path.toRealPath().normalize();

Explicación. While the Path object is immutable, it returns a new instance. Moreover, the NIO2 API has the toRealPath() and normalize() methods that we can use to remove redundancies.

Exercicio para amosar os contidos dun directorio Amosa o contido do directorio da túa elección

Solución

// java.io API
String[] list = file.list();
File[] files = file.listFiles();
// java.nio API
DirectoryStream<Path> paths = Files.newDirectoryStream(path);

A API NIO2 devolve o seu propio obxecto DirectoryStream object, que implementa a interface iterable.

Exercicio 10_1 e pedir recursividade


BBB

B

Java e o uso do sistema de ficheiros

Profundizaremos a continuación con algúns exemplos de operacións habituais para programar o acceso ó sistema de ficheiros do sistema operativo.

Creando un ficheiro

Podemos crear un ficheiro baleiro cos atributos que desexemos co método: createFile(Path, FileAttribute<?>)

Se non se especifican atríbutos, será creado con atríbutos por defecto. E de xa existir o ficheiro indicado na ruta, lanzarase unha excepción.

O seguinte código crea un ficheiro con atributos por defecto:

Path file = ...; try {

   // Create the empty file with default permissions, etc.
   Files.createFile(file);

} catch (FileAlreadyExistsException x) {

   System.err.format("O ficheiro chamado  %s" +  " xa existe%n", file);

} catch (IOException x) {

   // Outro tipo de fallo, coma os relacionados con permisos.
   System.err.format("createFile da erro: %s%n", x);

}

Outra posibilidade para crear un ficheiro e empregrando os métodos de newOutputStream methods; será suficiente con abrir un fluxo de saída e pechalo para que quede creado un ficheiro baleiro. Podes atopar información na documetación (https://docs.oracle.com/javase/tutorial/essential/io/file.html#createStream )

Creando un directorio

Pódese facer usando o método: createDirectory(Path, FileAttribute<?>)

Se non se especifica ningún atributo, terá os que correspondan por defecto.

Un exemplo: Path dir = ...; Files.createDirectory(path);

E outro exemplo con permisos específicos nun sistema de ficheiros POSIX: Set<PosixFilePermission> perms =

   PosixFilePermissions.fromString("rwxr-x---");

FileAttribute<Set<PosixFilePermission>> attr =

   PosixFilePermissions.asFileAttribute(perms);

Files.createDirectory(file, attr);

Mostrando o contido dun directorio Pódese facer usando o método:

newDirectoryStream(Path) 

Este método devolve un obxecto que implementa a interfaz DirectoryStream, que a súa vez implementa Iterable.

Recorda que o devolto DirectoryStream é un fluxo, e que se non o chamas desde un try-with-resources debes pechar o fluxo no bloque finally block-

A continuación un exemplo que amosa os contidos dun directorio:

Path dir = ...; try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {

   for (Path file: stream) {
       System.out.println(file.getFileName());
   }

} catch (IOException | DirectoryIteratorException x) {

   // IOException nunca será lazanda pola iteracion neste exemplo
   // so pode ser lanzado por newDirectoryStream.
   System.err.println(x);

}

Borrando un ficheiro, enlace simbólico ou directorio

Debes ter en conta que para borrar un directorio éste non debe ter contido, ou a operación de borrado fallará. Cando o que se borra é un enlace simbólico, o destino do enlace non será borrado.

A clase File facilita dous métoodos para o borrado.

O método delete(Path) → borra o ficheiro ou lanza unha expección se a operación falla (por exemplo, non existe a excepción ou non se gozan dos permisos requiridos.

Por exemplo try {

   Files.delete(path);

} catch (NoSuchFileException x) {

   System.err.format("%s: no such" + " file or directory%n", path);

} catch (DirectoryNotEmptyException x) {

   System.err.format("%s not empty%n", path);

} catch (IOException x) {

   // File permission problems are caught here.
   System.err.println(x);

}

O método deleteIfExists(Path) → a direferencia do anterior, non lanzará excepción se o ficheiro non existe (o cal pode ser útil cando se traballa con varios fíos).

Copiando un ficheiro ou directorio

Para copiar un ficheiro ou directorio existe o método:

copy(Path, Path, CopyOption...) 

A copia falla se o destino existe agás a opción REPLACE_EXISTING se atope específicada.

Os directorios poden ser copiados, pero debese ter en contacta que os ficheiros contidos na ruta orixe non o serán: o novo directorio sempre será creado baleiro.

O seguinte código exemplifica o funcionamento do método copy (recorda que será preciso importar o paquete con import static java.nio.file.StandardCopyOption.*;): Files.copy(orixe, destino, REPLACE_EXISTING);

Existen outros métodos da clase Files que permiten facer a copia entre un ficheiro e un fluxo: copy(InputStream, Path, CopyOptions… → todos os bytes dun dun fluxo de entrada a un ficheiro copy(Path, OutputStream) → todos os bytes dun ficheiro a un fluxo de saída Movendo un ficheiro ou directorio É posible mover un ficheiro e directorio usando move(Path, Path, CopyOption...) Files.move(source, target, REPLACE_EXISTING);

Neste exemplo empregouse a opción REPLACE_EXISTING que sobreescribe de xa existir na ruta de destino o ficheiro ou directorio

Outras operacións

Explicaronse algunhas das operacións máis comúns no uso de Java en relación co sistema de ficheiros. Con todo, a lista non é exhaustiva. Deixanse como enlaces para a comprensión estudo do programador interesado os seguintes: