Les fichiers de texte en Java
1. Importations
Il faut importer toutes les classes suivantes, selon le
"niveau" d'entrées/sorties souhaité :
- import java.io.IOException;
- Simple et inefficace :
import java.io.FileReader;
import java.io.FileWriter;
- [+] Efficace :
[+] import java.io.FileReader;
[+] import java.io.BufferedReader;
[+] import java.io.FileWriter;
[+] import java.io.BufferedWriter;
- [+] Simple et pratique :
[+] import java.io.File;
[+] import java.util.Scanner;
[+] import java.io.PrintWriter;
- [++] Efficace et pratique :
[++] import java.io.FileReader;
[++] import java.io.BufferedReader;
[++] import java.util.Scanner;
[++] import java.io.FileWriter;
[++] import java.io.BufferedWriter;
[++] import java.io.PrintWriter;
2. Déclaration de variable
Pour utiliser un fichier de texte (stocké en général
sur un disque dur) dans un programme, il faut déclarer une variable
d'un type particulier qui "repésentera" ce fichier au sein du programme :
FileReader fr; (fr est le nom de la variable)
pour lire dans le fichier, ou
[+] BufferedReader br; (br est le nom de la variable)
pour lire efficacement dans le fichier, ou
[+] Scanner sr; (sr est le nom de la variable)
pour lire facilement dans le fichier, ou
[++] idem ligne précédente
FileWriter fw; (fw est le nom de la variable)
pour écrire dans le fichier.
[+] BufferedWriter bw; (bw est le nom de la variable)
pour écrire efficacement dans le fichier.
[+] PrintWriter pw; (pw est le nom de la variable)
pour écrire facilement dans le fichier.
[++] idem ligne précédente
3. Ouverture
- 3.1 Instructions
La déclaration précédente ne fait que déclarer
une variable, sans l'initialiser.
L'ouverture d'un fichier consiste à associer un fichier sur disque
à une variable du programme :
fr = new FileReader( nom_fichier ); ou
[+] br = new BufferedReader( new FileReader( nom_fichier ) ); ou
[+] sr = new Scanner( new File( nom_fichier ) ); ou
[++] sr = new Scanner( new BufferedReader( new FileReader( nom_fichier ) ) ); ou
fw = new FileWriter( nom_fichier );
[+] bw = new BufferedWriter( new FileWriter( nom_fichier ) );
[+] pw = new PrintWriter( nom_fichier ); ou
[++] pw = new PrintWriter( new BufferedWriter( new FileWriter( nom_fichier ) ) );
où nom_fichier est une chaîne de caractères (constante ou variable)
contenant le chemin d'accès (absolu ou relatif) au fichier disque.
- 3.2 Chemins d'accès (sous linux)
- absolus : /chemin ou ~/chemin
- relatifs : chemin ou ./chemin ou ../chemin
- chemin : fichier ou répertoire/fichier
ou répertoire/sous-répertoire/fichier
ou ...
- 3.3 Conséquences de l'ouverture d'un fichier
- s'il est ouvert en lecture, les accès en écriture sont refusés
à tout autre programme
- s'il est ouvert en écriture :
- Les accès en lecture et en écriture
sont refusés à tout autre programme.
- L'écriture commence par le début du fichier
en écrasant toutes les données déjà
présentes si le fichier existait.
Pour empêcher cela, il faut compléter l'ouverture comme suit :
fw = new FileWriter( nom_fichier, true );
[+] bw = new BufferedWriter( new FileWriter( nom_fichier, true ) );
[-] non disponible
[++] pw = new PrintWriter( new BufferedWriter( new FileWriter( nom_fichier, true ) ) );
Dans ce cas, l'écriture commence à la fin du fichier.
4. Test d'ouverture
L'ouverture a pu mal se passer dans plusieurs cas, notamment :
- en lecture, si le fichier n'existe pas
- en lecture, si l'utilisateur n'a pas les droits de lecture sur ce fichier
(ou les droits de traverser un répertoire du chemin d'accès)
- en écriture, si le fichier existe déjà et que l'utilisateur
n'a pas les droits de modification sur ce fichier
- en écriture, si l'utilisateur n'a pas les droits de modification
sur le répertoire contenant le fichier
- en écriture, éventuellement selon les systèmes,
s'il n'y a plus assez de place sur le disque ou si le quota est
insuffisant
Il faut donc tester si l'ouverture s'est bien passée avant de commencer
à lire ou à écrire dans un fichier supposé ouvert;
en Java, cela consiste à traiter une exception, en l'occurence :
FileNotFoundException pour une ouverture en lecture, ou
IOException pour une ouverture en écriture.
5. Lectures / écritures
- 5.1 Lecture
car = fr.read(); (pour lire un seul caractère; suppose int car;)
[+] car = br.read(); (pour lire un seul caractère; suppose int car;)
[+] ligne = br.readline(); (pour lire une ligne; suppose String ligne;)
[+] Attention ! Le(s) caractère(s) de fin de ligne
ne fait(font) pas partie de la String ligne .
[++] mot = sr.next(); (pour lire un seul mot; suppose String mot;)
[++] ligne = sr.nextLine(); (pour lire une ligne; suppose String ligne;)
[++] Attention ! Le(s) caractère(s) de fin de ligne
ne fait(font) pas partie de la String ligne .
[++] e = sr.nextInt(); (pour lire un entier; suppose int e;)
[++] (de même pour les byte, short, long,
float, double, et boolean, mais pas pour les
char)
[++] Remarque : La lecture des mots peut être influencée par
sr.useDelimiter(String) qui précise les séparateurs
de mots à utiliser, et la lecture des nombres peut être influencée par
sr.useRadix(int) qui précise la base à utiliser et par
sr.useLocale(Locale) qui précise la "localisation" à
utiliser.
[++] idem lignes précédentes
- 5.2 Écriture
fw.write( car ); (pour écrire un seul caractère;
suppose int car; et car initialisé)
fw.write( ligne ); (pour écrire une ligne;
suppose String ligne; et ligne initialisée)
Attention ! Pour passer à la ligne suivante, il faut
remplacer l'instruction ci-dessus par fw.write( ligne+"\n" );
[+] bw.write( car ); (pour écrire un seul caractère;
suppose int car; et car initialisé)
[+] bw.write( ligne ); (pour écrire une ligne;
suppose String ligne; et ligne initialisée)
[+] Attention ! Pour passer à la ligne suivante, il faut
ajouter l'instruction bw.newLine(); .
[++] pw.print( ligne ); (pour écrire une ligne;
suppose String ligne; et ligne initialisée)
[++] Attention ! Pour passer à la ligne suivante, il faut
remplacer l'instruction ci-dessus par pw.println( ligne );
[++] Remarque : Des sorties formattées (presque comme en C)
peuvent être obtenues par pw.printf( format, arguments );
[++] idem lignes précédentes
6. Test de fin de fichier (en lecture)
Après de nombreuses opérations de lecture dans un fichier de texte,
il se peut qu'il n'y ait plus rien à lire !
On dit alors qu'on a atteint la fin de fichier.
Selon les systèmes, lire au-delà de cette limite peut soit provoquer
une erreur, soit simplement retourner toujours la même donnée.
Il faut donc tester la fin de fichier AVANT chaque lecture
(eh oui, le fichier peut être vide !) :
int car = -2;
while ( car != -1 ) {
car = fr.read();
if ( car != -1 )
traitement du caractère lu
}
[+] int car = -2;
[+] while ( car != -1 ) {
[+] car = br.read();
[+] if ( car != -1 )
[+] traitement du caractère lu
[+] }
[+] String ligne = "";
[+] while ( ligne != null ) {
[+] ligne = br.readLine();
[+] if ( ligne != null )
[+] traitement de la ligne lue
[+] }
[+] while ( sr.hasNext() ) {
[+] String mot = sr.next();
[+] traitement du mot lu
[+] }
[+] while ( sr.hasNextLine() ) {
[+] String ligne = sr.nextLine();
[+] traitement de la ligne lue
[+] }
[+] while ( sr.hasNextInt() ) {
[+] int e = sr.nextInt();
[+] traitement de l'entier lu
[+] }
[+] Remarque : de même pour les byte, short, long,
float, double, et boolean, mais pas pour les
char.
[++] idem lignes précédentes
7. Fermeture
Comme expliqué au paragraphe "ouverture", le fichier est plus ou moins inaccessible
aux autres programmes tant que le fichier reste ouvert.
Il faut donc le fermer le plus tôt possible, c'est-à-dire dès
qu'on n'en a plus besoin par l'instruction :
fr.close(); ou fw.close();
br.close(); ou bw.close();
sr.close(); ou pw.close();
idem ligne précédente
Il est souvent judicieux de placer ces instructions dans une clause
finally, en les faisant précéder d'un test
tel que : if ( sr != null )
8. Exemples de lectures
Soit un fichier contenant :
On peut envisager au moins 4 manières de le lire
(les virgules montrent à l'affichage la séparation entre les lectures) :
-
caractère par caractère (car=br.read();),
on obtient :
'1','2','3',' ',' ','4','5','6','\n','+','7','8','9','\n'
-
mot par mot (mot=sr.next();),
on obtient :
"123","456","+789"
-
ligne par ligne (ligne=sr.nextLine();),
on obtient :
"123 456","+789"
-
terme par terme (e=sr.nextInt();),
on obtient :
123,456,789
idem 3 lignes précédentes