Resum

Molts llenguatges de programació proporcionen biblioteques que permeten accedir directament als mecanismes de gestió del sistema de fitxers que ofereix el sistema operatiu. D’aquesta manera, és possible crear codi font des del qual, amb les instruccions adients, es poden realitzar operacions típiques d’un explorador de fitxers. L’API de Java no és una excepció, aprofitant el conjunt de classes incloses dins del package java.io.

El punt de partida per a la gestió del sistema de fitxers mitjançant Java és la classe File. El seu nom és una mica enganyós (“fitxer”, en anglès), ja que no es refereix a un fitxer realment, sinó a una ruta dins el sistema. O sigui, un text que identifica de forma general el nom tant d’un fitxer com d’una carpeta, de manera que indica de forma única la seva localització en el sistema de fitxers.

El format d’una ruta varia segons el sistema operatiu. En els sistemes Unix els elements d’una ruta se separen mitjançant barres i sempre parteixen de la carpeta arrel del sistema. Per exemple, /Documents/Fotos/Foto.png. En canvi, en un sistema Windows, una ruta comença pel nom de la unitat de disc i els elements se separen amb contrabarres. Per exemple, C:\Documents\Fotos\Foto.png. Aquest fet cal tenir-lo en compte quan es duu a terme un programa.

Donada una ruta, aquesta pot seguir dos formats: relatiu o absolut. Una ruta absoluta és la que es refereix a un element destinació a partir de la carpeta arrel d’un sistema de fitxers. En aquesta s’enumeren, una per una, totes les carpetes que hi ha entre la carpeta arrel fins a la carpeta que conté l’element destinació. En canvi, una ruta relativa és la que es considera que parteix des de la carpeta, o directori, de treball de l’aplicació. Aquesta carpeta pot ser diferent cada cop que s’executa el programa i depèn de la manera com s’ha dut a terme aquesta execució.

La inicialització de la classe File requereix d’un paràmetre, una cadena de text que indica precisament la ruta que es vol manipular. Aquest text pot correspondre a un fitxer o una carpeta, que existeixi o no dins el sistema de fitxers. Una de les particularitats de la classe File en ser inicialitzada és que actua com un tipus compost. Això vol dir, cada cop que s’inicialitza, a l’identificador emprat s’hi assigna un valor associat a aquella ruta. A partir de llavors, l’identificador actua com una variable dins el programa. Per operar amb diferents rutes alhora caldrà inicialitzar i manipular diferents variables, igual que es faria amb diferents cadenes de text.

Com altres classes, File ofereix un ventall de mètodes que permeten manipular la ruta de diferents maneres. Segons el mètode, caldrà que la ruta compleixi certes condicions: que estigui associada a un fitxer, a una carpeta, o que l’element en qüestió realment existeixi o no. Entre els més rellevants es troba:

  • String getAbsolutePath() s’avalua en el text relatiu a la ruta absoluta. Si la variable File es va inicialitzar usant una ruta relativa, el resultat inclou també la part associada a la carpeta de treball.
  • boolean exists() comprova si la ruta existeix dins del sistema de fitxers o no. S’avaluarà en true o false, per a cada cas, respectivament. Es considera que la ruta no existeix si qualsevol dels elements individuals que la conformen no existeix.
  • long length() s’avalua en la mida d’un fitxer, en bytes. Aquest mètode només pot ser invocat sobre una ruta que representi un fitxer, i en cas contrari no es pot garantir la correctesa del resultat (no es pot considerar com a vàlid en cap cas).
  • boolean mkdir() permet crear una carpeta d’acord a la ubicació que indica la ruta, la qual ha d’indicar el nom d’una carpeta que no existeix en el moment d’invocar el mètode.
  • boolean delete() esborra el fitxer o carpeta que indica la ruta, la qual ha d’indicar el nom d’una carpeta que sí que existeix en el moment d’invocar el mètode.
  • File[ ] listFiles() s’avalua en un array de File on s’enumeren tots els elements continguts a la carpeta indicada pel text usat en inicialitzar la variable de tipus File. Perquè s’executi correctament, el text de la ruta ha d’indicar forçosament una carpeta.

Ara bé, normalment, les aplicacions que fan ús de fitxers no estan centrades en la gestió del sistema de fitxers de l’ordinador. L’objectiu principal d’usar fitxers és poder emmagatzemar-hi dades, de manera que entre diferents execucions del programa, fins i tot en diferents equips, és possible recuperar-les. File no té cap mètode directe per fer-ho. Això és perquè requereix de classes auxiliars que permetin desar les dades des de les variables del programa en un fitxer, o bé a la inversa, llegir valors emmagatzemats dins un fitxer per poder tractar-los mitjançant les instruccions del programa, de la mateixa manera que es pot fer amb dades obtingudes des del teclat.

Java considera que els fitxers que pot gestionar pertanyen a dos tipus possibles. D’una banda, els fitxers orientats a caràcter, en els quals les dades es representen com una seqüència de cadenes de text, i cada valor es diferencia de l’altre usant un delimitador, com un espai. D’altra banda, en els fitxers orientats a byte les dades es representen directament d’acord al seu format en binari, sense cap separació.

Per gestionar les dades d’un fitxer, tant per llegir-les com per escriure-les, hi ha diversos mecanismes. El més habitual és l’accés seqüencial, que es correspon al tractament d’un conjunt d’elements de manera que només és possible accedir-hi d’acord a l’ordre d’aparició. Per poder tractar un element, cal haver tractat tots els elements anteriors. Per tant, dins un fitxer, es considera que tots els valors prenen la forma d’una seqüència, l’un rere l’altre. Abans de poder tractar-ne un, cal tractar tots els anteriors.

Per tractar seqüencialment i de manera senzilla fitxers orientats a caràcter, Java ofereix les classes Scanner, pertanyent al package java.util, i PrintStream, pertanyent al package java.io. La primera s’usa exclusivament per llegir valors mentre que la segona serveix només per escriure’n. Ambdues requereixen un valor de tipus File per inicialitzar-se correctament. La ruta indicada per aquest serà el fitxer que es generarà o des d’on es llegirà. En el cas de PrintStream, el tractament seqüencial de dades només permet treballar amb fitxers nous, per la qual cosa si la ruta especificada es refereix a un fitxer que ja existeix, el seu contingut serà eliminat totalment.

Conceptualment, la lectura de dades seqüencial és molt semblant a llegir dades des del teclat (que també es fa amb la calsse Scanner). En iniciar-se el procés de tractament de dades seqüencial, es considera que hi ha un apuntador en el primer valor del fitxer. Cada cop que es llegeix un valor, l’apuntador avança al següent, descartant espais en blanc o salts de línia, que són els elements usats per separar el text d’un valor del d’un altre. Scanner ofereix un conjunt de mètodes per llegir diferents tipus primitius. Cal usar el mètode adient d’acord al tipus de cada valor escrit en el fitxer, o hi haurà un error.

Al contrari del que passa quan es llegeixen dades del teclat, si s’arriba al final del fitxer i s’intenten llegir més valors, hi haurà un error al programa. Per tant, per evitar aquest error cal usar algun mecanisme per saber que ja no es poden llegir nous valors. El sistema més simples és saber a priori el nombre de dades que hi ha al fitxer o bé fer que el darrer valor sigui un d’especial, que serveix com a marca de fi.

L’escriptura és una mica més senzilla, ja que no cal tractar elements dins un fitxer, sinó generar-los. El funcionament és idèntic a escriure dades en una pantalla. Mitjançant els mètodes de la classe PrintStream és possible escriure qualsevol valor de tipus primitiu o cadena de text en el fitxer en forma de text. Un cop escrit, no és possible recular.

En ambdós casos, en acabar, cal invocar un mètode especial anomenat close(). Aquest indica al sistema operatiu que fitxer ja no està essent utilitzat pel programa.

Per al cas del tractament seqüencial de fitxers orientats a byte cal usar la classe RandomAccessFile, pertanyent al package java.io. Aquesta classe permet tant llegir com escriure valors. El seu funcionament és molt semblant a les anteriors, basant-se en un apuntador que va avançant i oferint tot de mètodes que permeten tant la lectura com l’escriptura de diferents tipus de valors. La principal diferència és que, en el cas de l’escriptura, no es genera un nou fitxer, sinó que es van sobreescrivint els valors un per un. Això vol dir que si s’escriuen menys valors que els que hi havia inicialment en el fitxer, aquests romandran en acabar l’escriptura, no s’eliminaran.

Evidentment, l’altra diferència important és el format de les dades dins un fitxer orientat a byte. Aquests no s’escriuen en forma de text, com es faria a la pantalla, sinó en el format binari original d’acord a la representació de les dades dins la memòria de l’ordinador de cada tipus primitiu. No hi ha cap separador entre els bytes de cada valor, per la qual cosa cal ser molt acurat.

Ara bé, en realitat, un fitxer no és ben bé com el teclat o la pantalla, ja que les dades ni deixen d’estar disponibles un cop llegides, ni té sentit que resulti impossible sobreescriure-les parcialment a partir de qualsevol posició de les dades. L’accés seqüencial és una manera còmoda de tractar fitxers, però a canvi de perdre un cert grau de versatilitat. La classe RandomAccessFile també permet un sistema més avançat de tractar les dades d’un fitxer, l’accés relatiu, el qual es basa en la capacitat d’accedir a qualsevol element dins una seqüència de dades sense haver de tractar prèviament els elements anteriors. Per tant, en el cas d’un fitxer, a Java, aquest mecanisme només es pot usar en fitxers orientats a byte.

En aquest aspecte, es pot considerar que un fitxer orientat a byte es comporta exactament igual que un array, on cada posició representa un byte, però cada valor n’ocupa més d’una. Mitjançant la invocació de certs mètodes, és possible desplaçar de manera arbitrària, endavant o enrere, l’apuntador del fitxer, de manera que es poden iniciar operacions de lectura o escriptura a partir de qualsevol posició.

Finalment, cal dir que totes les operacions vinculades als tractaments de dades amb fitxers, des de la inicialització de les classes implicades fins a la lectura o escriptura de dades, pot generar diversos errors d’execució. Des que, simplement, el fitxer a llegir no existeixi en la ruta especificada, fins que hi hagi algun error en el sistema d’entrada / sortida de l’ordinador o el fitxer estigui corromput i l’operació es vegi interrompuda inesperadament. El tractament de dades amb fitxers és un procés molt més delicat del que aparenta inicialment. Java controla aquests errors d’una manera una mica especial, mitjançant la captura d’excepcions. Una excepció és un error especial produït en invocar certs mètodes de classes de l’API de Java.

Tot un bloc d’instruccions vinculat a operacions d’entrada sortida ha d’estar dins una sentència try/catch. Estructuralment, aquesta és molt semblant a la sentència if/else, ja que es compon de dos blocs de codi consecutius però diferenciats. El primer, tot just després de la instrucció try, per indicar les instruccions que cal anar executant mentre no hi ha excepcions, i un altre, després de la instrucció catch, que només cal executar en el moment que es produeixi algun error d’entrada / sortida (una excepció). El programa va executant totes les instruccions dins el bloc try, i si mai hi ha una excepció, el flux de control salta immediatament a la primera instrucció del bloc catch. Si no hi ha cap excepció i s’arriba al final del bloc try, les instruccions dins el bloc catch no s’executen.

Java és tan estricte amb el control d’errors que si s’escriuen operacions sobre fitxers sense envoltar-les d’una sentència try/catch, hi haurà un error de compilació. Per tant, cal ser molt acurat amb la captura d’excepcions.

Anar a la pàgina següent:
Resultats d'aprenentatge