Java intensif Entrées/Sorties

publicité
Java intensif
Entrées/Sorties
Serge Rosmorduc
2013-2014
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
1 / 50
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
2 / 50
But du cours
Donner les éléments de base pour comprendre les entrées/sorties en
java.
Cours 1 : Notions générales, lecture et écriture simple dans un
fichier en java ;
Cours 2 : Notions avancées, fichiers, lecture de textes structurés ;
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
3 / 50
Première partie I
Notions fondamentales
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
4 / 50
Flux
o n j o u r
m o n d e \n ...
⇑
Définition : Un flux est une suite de données, finie ou infinie, dotée
d’un curseur. Il existe deux grands types de flux : les flux en lecture
(auquel cas le curseur correspond à une tête de lecture) et les flux en
écriture. Les lectures (resp. écritures) dans le flux se font à la position
du curseur.
L’opération de lecture (resp. d’écriture) avance le curseur.
Exemple de flux : canal de communication en réseau, fichier sur
disque, entrée standard au clavier...
...
b
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
5 / 50
Notion de fichier
Définition : un fichier sera ici défini comme un flux enregistré sur un
disque.
Un fichier peut être envisagé de deux manières : comme objet du
système de gestion de fichiers, il a une taille, des droits, une date de
création, il appartient à un répertoire... par ailleurs, il contient un flux.
En java, un fichier (existant ou non !) est représenté par un objet File
(java 1.6) ou Path (à partir de 1.7).
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
6 / 50
Flux séquentiels et accès direct
flux séquentiel : un flux séquentiel est un flux dans lequel les
caractères sont lus (ou écrits) les uns après les autres, du
premier au dernier.
flux en accès direct : un flux en accès direct (Random Access) permet
la lecture de n’importe quel caractère du flux. Le
programmeur peut déplacer la tête de lecture (ou
d’écriture) à n’importe quel endroit du flux.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
7 / 50
Flux binaires et flux textes
Définitions
Flux binaire un flux binaire est une suite d’octets. L’interprétation de
ces octets est à la charge du programme qui les lit ou les
écrit.
Flux texte un flux texte est une suite de caractères. Il est stocké
selon un système de codage déterminé.
Note : En dernier ressort, un flux texte est toujours contruit au dessus
d’un flux binaire.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
8 / 50
Flux binaire, exemples
les fichiers créés par la plupart des bases de données ;
la plupart des formats d’images ;
les fichiers “.doc” de word
les fichiers compressés (zip et autres)
...
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
9 / 50
Petit rappel d’architecture
Plusieurs manière de coder un entier sur 4 octets en mémoire :
numéro de l’octet
valeur
0
3
1
100
2
30
3
20
big endian octet le plus significatif d’abord
56.892.948 = 3 ∗ 2563 + 100 ∗ 2562 + 30 ∗ 256 + 20
little endian octet le moins significatif d’abord
337.536.003 = 3 + 100 ∗ 256 + 30 ∗ 2562 + 20 ∗ 2563
java, 68000 : big endian
x86 : little endian
Voir classe ByteBuffer.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
10 / 50
Exemple simpliste de fichier binaire : un format
d’image couleur.
Format du fichier :
le fichier commence par deux entiers courts, codés en binaire sur
deux octets, en mode “Big Endian” (Grandboutien) ; ces entiers
sont respectivement la largeur et la hauteur de l’image.
il comporte ensuite la liste des points de l’image, ligne après ligne,
avec trois octets par points, représentant les valeurs R, V, et B de
la couleur.
Pseudo code pour écrire un petit fichier dans ce format :
écrire(0); écrire(3); // largeur 3 pixels
écrire(0); écrire(1); // hauteur 1 pixel
écrire(255); écrire(0); écrire(0); // 1 pixel rouge
écrire(100); écrire(100); écrire(0); // 1 pixel jaune
écrire(255); écrire(255); écrire(255); // 1 pixel blanc
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
11 / 50
Fichiers textes et codage des caractères
Approche simple (anciennement) :
un fichier est une suite de nombre entre 0 et 255 ;
le codage d’un fichier texte associe à chaque nombre un
caractère ;
le codage dépend généralement du système et de sa
configuration.
Exemple
caractère
espace
0 (zéro)
1 (un)
a
A
é
œ
code ASCII
32
48
49
97
65
(non)
(non)
Serge Rosmorduc ()
code latin-1
32
48
49
97
65
233
(non)
code MacRoman
32
48
49
97
65
142
207
Java intensifEntrées/Sorties
2013-2014
12 / 50
Unicode
Un codage pour tous les caractères → dépasse l’octet !
Les codes vont de 0 à 0x10FFFF (1.114.111)
comment les représenter concrètement : UTF-8, UTF-16 (BE/LE),
UTF-32(BE/LE)
I
I
texte
Latin-1
UTF-8
pour les fichiers : UTF-8 (plus compact, pas d’ambiguı̈té BE/LE) ;
en interne, un char java est en UTF-16.
u
0x75
0x75
Serge Rosmorduc ()
n
0x6E
0x6E
0x20
0x20
é
0xE9
0xC3 0xA9
Java intensifEntrées/Sorties
t
0x74
0x74
é
0xE9
0xC3 0xA9
2013-2014
13 / 50
Fins de lignes
trois codages différents
unix : saut de ligne, ’\n’, code 10
mac : retour chariot, ’\r’, code 13
dos/windows : ’\r\n’
Attention : un fichier créé par windows et lu sous unix comportera
probablement le saut de lignes windows !
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
14 / 50
Deuxième partie II
Et en Java ?
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
15 / 50
Exemple d’écriture
public static void testEcritureTexte (String fname)
throws IOException {
// On crée un objet Writer, ce qui ouvre le fichier
FileWriter f= new FileWriter(fname);
try {
// On utilise cet objet pour écrire
f.write("hello ");
f.write("world.");
f.write("It works !");
} finally {
// On ferme le flux
f.close();
}
}
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
16 / 50
Écriture, points importants
La création du FileWriter permet crée le fichier et permet
d’écrire dedans ;
on écrit avec write() ;
ne jamais oublier de fermer les fichiers ouverts.
pratiquement toutes les méthodes d’I/O renvoient des
IOExceptions.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
17 / 50
Exemple de lecture
public static void testLectureTexte (String fname)
throws IOException {
FileReader f= new FileReader(fname);
try {
// Le résultat de read doit être un entier
// (à cause du -1 qui est renvoyé en fin de fichier)
int cc;
cc= f.read();
while (cc != -1)
{
// Pour ranger cc dans un char, il faut un cast :
char c= (char)cc;
... on fait quelque chose de la valeur de c...
cc= f.read(); // Lecture du suivant.
}
} finally {
f.close();
}
}
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
18 / 50
Lecture, points importants
FileReader f= new FileReader(fname) : on crée le
lecteur.
la méthode read() renvoie le code du caractère lu ou -1 si on
arrive à la fin du fichier.
-1 n’est pas un code de caractère. On ne peut pas le ranger dans
un char. C’est pour ça que cc est un int.
chaque appel de read() avance dans le fichier. D’où l’intérêt de
stocker la valeur reçue dans une variable.
que se passe-t-il si on oublie le read() dans la boucle ?
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
19 / 50
Récapitulation
On manipule le flux à travers un objet (Reader, Writer,
OutputStream ou InputStream) ;
la création de l’objet ouvre le flux (et crée le fichier si on est en
écriture) ;
ouvrir un fichier existant en écriture efface l’ancien fichier ;
on doit toujours fermer les flux ouverts ;
écriture : on ouvre, on écrit, on ferme ;
lecture : on ouvre, on boucle en lisant, on atteint la fin du fichier,
on ferme ;
read() et write() avancent dans le fichier ;
la lecture est plus complexe que l’écriture
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
20 / 50
Architecture des entrées/sorties en Java
Flux séquentiels
Important ! ! !
Lecture
Binaire InputStream
Texte
Reader
Écriture
OutputStream
Writer
Flux à accès direct
RandomAccessFile.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
21 / 50
Organisation des classes
remarquez les méthodes abstraites !
Writer
close()
flush()
write(cbuf : char[])
write(cbuf : char[],off : int,len : int)
write(c : int)
write(s : String)
write(s : String,off : int,len : int)
BufferedWriter
newLine()
Un BufferedWriter écrit dans un
autre Writer
Serge Rosmorduc ()
OutputStreamWriter
out : OutputStream
StringWriter
toString() : String
PrintWriter
print(s : String)
FileWriter
Java intensifEntrées/Sorties
2013-2014
22 / 50
Nommage des classes
Typiquement :

∅




Buffered



StringBuffer/ByteArray
File





Filter


Piped

InputStream



OutputStream
Reader



Writer
D’autres classes sont plus spécifiques : e.g. PrintWriter.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
23 / 50
Filtrage
Certaines classes fonctionnent comme des filtres ;
LineNumberReader f=
new LineNumberReader(
new InputStreamReader(System.in));
...
c= f.read();
On lit l’entrée standard (System.in) (binaire) ;
Le InputStreamReader : flux texte au dessus de cette entrée
LineNumberReader : reader qui compte les lignes.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
24 / 50
La classe Writer, étude détaillée
void write (int c)
throws IOException
Écrit le caractère dont le code est c. Notez que cette méthode
fonctionne correctement que c soit un char ou un int.
void write (char [] s)
throws IOException
écrit le contenu de s.
void write (char[] s, int off, int longueur)
throws IOException
écrit les caractères de s compris entre l’indice off et off +
longueur.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
25 / 50
void write (String s)
throws IOException
écrit la chaı̂ne s.
void flush ()
throws IOException
Réalise effectivement la copie sur le support. C’est utile dans le
cas de classes comme BufferedWriter, où les données sont
stockées temporairement en mémoire.
void close ()
throws IOException
ferme le Writer
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
26 / 50
La classe Reader : étude détaillée
int read ()
throws IOException
lit un caractère sur l’entrée, et renvoie son code, ou -1 si la fin du
fichier est atteinte.
int read (char [] buf)
throws IOException
Remplit le tableau buf avec les caractères lus. Attention, cette
méthode n’alloue pas le tableau.
La méthode renvoie le nombre de caractères lus, ou -1 si la fin du
fichier est atteinte.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
27 / 50
int read (char [] buf, int dep, int longueur)
throws IOException
Copie les caractères lus dans buf, en commençant à l’indice
dep, en en lisant au maximum longueur caractères.
La méthode renvoie le nombre de caractères lus, ou -1 si la fin du
fichier est atteinte.
void close ()
throws IOException
ferme le lecteur.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
28 / 50
Reader et Writer, précisions sur les sauts de ligne
Codage des sauts de ligne dépend du système d’exploitation ;
Reader et Writer ne distinguent pas les sauts de lignes des
autres caractères ;
ils manipulent donc \n et \r comme des caractères comme les
autres.
pour lire ligne par ligne, le plus simple est d’utiliser un
BufferedReader.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
29 / 50
Classes InputStream et OutputStream
En gros, même méthodes que respectivement Reader et
Writer ;
Mais écrivent et lisent des octets ;
les méthodes recoivent ou renvoient des int entre 0 et 255 ;
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
30 / 50
Lecture d’un fichier
Classes FileOutputStream, FileWriter
Ouverture/création du flux :
FileOutputStream (File file)
throws IOException
FileOutputStream (String name)
throws IOException
FileOutputStream (String name, boolean append)
throws FileNotFoundException
ouvre le flux en écriture ; ajoute les données écrites à la fin du
fichier actuel si append vaut vrai.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
31 / 50
Lecture d’un fichier
Classes FileReader, FileInputStream
FileInputStream (File file)
throws FileNotFoundException
FileInputStream (String name)
throws FileNotFoundException
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
32 / 50
Déclaration de Reader
Qu’est-ce qui est mieux :
1
void maFonction(FileReader f)
2
void maFonction(Reader r)
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
33 / 50
Déclaration de Reader
Qu’est-ce qui est mieux :
1
void maFonction(FileReader f)
2
void maFonction(Reader r)
Réponse : avec Reader, la fonction sera utilisable pour tous les
Reader. On préfère donc cette version dans la plupart des cas.
De même pour les variables d’instance de type Reader.
On peut écrire :
Reader r= new FileReader("toto.txt");
(l’intérêt est plus mitigé).
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
33 / 50
Classes orientées chaı̂nes : écriture
CharArrayWriter, ByteArrayOutputStream, StringWriter
int c;
FileReader f= new FileReader(args[0]);
StringWriter sw= new StringWriter();
c= f.read();
while (c != -1) {
// écrit c dans sw
sw.write(c);
c= f.read();
}
// Copie le contenu de sw dans s.
String s= sw.toString();
System.out.println(s);
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
34 / 50
Classes orientées chaı̂nes : lecture
CharArrayReader, ByteArrayInputStream, StringReader,
StringBufferInputStream
Reader f=
new StringReader("hello tout le monde");
int c;
while ((c= f.read()) != -1) {
System.out.println((char) c);
}
f.close();
utiliser des méthodes prévues pour des flux sur des chaı̂nes de
caractères.
tests JUnit : pas besoin de créer de fichier, on teste sur des
StringReader ou des StringWriter...
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
35 / 50
Ponts entre flux binaires et textes
Les classes InputStreamReader et OutputStreamWriter
permettent de construire un flux orienté caractères au dessus
d’un flux d’octets.
Elles permettent de spécifier le codage d’un fichier (impossible
avec FileWriter/FileReader
Ouverture explicite en UTF-8
Reader r= new InputStreamReader(
new FileInputStream("toto.txt"),
"UTF-8");
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
36 / 50
BufferedReader
Lecture Bufferisée
Souvent utilisée à cause de sa méthode readLine() Utilise un
tampon (buffer).
Lecture plus rapide ;
retour en arrière :
void reset ()
throws IOException
revient à la dernière marque
void mark (int limite)
throws IOException
pose une marque (oubliée après limite caractères)
lecture ligne à ligne :
String readLine ()
throws IOException
renvoie le texte de la ligne (sans la fin de ligne), ou null si le
fichier est terminé.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
37 / 50
1
2
3
4
5
6
7
1
2
3
4
5
6
Lecture d’un fichier ligne à ligne avec BufferedReader
code propre :
BufferedReader r = . . . . ;
S t r i n g s= r . r e a d L i n e ( ) ;
while ( s ! = n u l l ) {
...
s= r . r e a d L i n e ( ) ;
}
r . close ( ) ;
En réalité :
BufferedReader r = . . . . ;
String s ;
while ( ( s= r . r e a d L i n e ( ) ) ! = n u l l ) {
...
}
r . close ( ) ;
(p.s. normalement, le close est dans un finally) .
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
38 / 50
Troisième partie III
Algorithmes
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
39 / 50
Approche des algorithmes de lecture
Exemple : lecture et interprétation à la main d’une suite de
nombres réels représentés en texte.
On commence par spécifier précisément la forme du texte (qu’est
ce qui est optionnel, qu’est-ce qui ne l’est pas...)
ici :
I
I
I
exemple lecture d’une heure, de la forme :
un ou deux chiffres, zéro ou plusieurs espaces, la lettre h , zéro
ou plusieurs espaces, un ou deux chiffres.
expression régulière : [0-9][0-9]? * h * [0-9][0-9]?
on lit un caractère, puis on l’interprète → on termine une
interprétation en lisant le caractère suivant ;
On raisonne en terme d’états. À moment donné, où en
sommes-nous, quels caractères pouvons nous attendre ?
lié à la notion d’automates
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
40 / 50
Début : lecture d’un ou deux chiffres pour l’heure
// On lit le premier caractère avant de commencer
c= lire()
si c n’est pas un chiffre
erreur()
fin si
h= valeur de c
c= lire() //on avance...
//lecture du chiffre "optionnel"
si c est un chiffre
h= h * 10 + valeur de c
c= lire() // on avance
fin si
à la fin, c correspond au caractère qui suit le texte reconnu
intérêt de -1 comme fin de flux ;
on est prêt à lire la suite...
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
41 / 50
Lecture de zéro ou n espaces,
// c a déjà été lu...
tant que c = espace
c= lire()
fin tant que
on est sûr que c n’est pas un espace après la boucle ;
on s’arrête forcément : fin du fichier, lire() renvoie -1.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
42 / 50
Lecture de h Normalement, après les espaces, le caractère qui suit est un h .
si ça n’est pas le cas, on a une erreur ;
si c’est le cas, on passe le h et on avance au caractère
suivant.
si c != ’h’ alors
erreur()
fin si
c= lire()
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
43 / 50
On réunit le tout...
c=
si
h=
c=
si
lire()
c n’est pas un chiffre alors erreur() fin
valeur de c
lire()
c est un chiffre
h= h * 10 + valeur de c
c= lire()
fin si
tant que c = espace faire c= lire() fin tant
si c != ’h’ alors erreur() fin si
c= lire()
tant que c = espace faire c= lire() fin tant
si c n’est pas un chiffre alors erreur() fin
m= valeur de c
c= lire()
si c est un chiffre
m= m * 10 + valeur de c
c= lire()
fin si
Serge Rosmorduc ()
Java intensifEntrées/Sorties
si
que
que
si
2013-2014
44 / 50
Exemple plus complexe : lecture de nombres réels
(en réalité, c’est bien entendu déjà fait ailleurs dans les classes java)
un nombre est de la forme ’-’ ? [0-9]+ (’.’ [0-9]+) ?
ils sont séparés par une suite de 0 espaces ou plus.
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
45 / 50
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
Algorithme pour ’-’ ? [0-9]+ (’.’ [0-9]+) ? :
l u = read ( )
nb= 0
NEGATIF= FAUX
SI l u == ’− ’ ALORS
n e g a t i f = VRAI
l u = read ( ) / / s a u t e r l e ’ − ’ !
FIN SI
SI l u non c h i f f r e e x c e p t i o n FIN SI
REPETER
nb= nb ∗ 10 + ( l u − ’ 0 ’ )
l u = read ( )
TANT QUE l u e s t un c h i f f r e
SI l u == ’ . ’ ALORS
l u = read ( )
dec= 0 . 1
SI l u non c h i f f r e e x c e p t i o n FIN SI
REPETER
nb= nb + dec ∗ ( l u − ’ 0 ’ )
dec= dec ∗0.1
l u = read ( )
TANT QUE l u e s t un c h i f f r e
FIN SI
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
46 / 50
Quatrième partie IV
Java 7 (et un peu 6 aussi)
pleins de nouveautés...
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
47 / 50
Bloc try with resource
garantit la fermeture d’un ou plusieurs flux, même en cas d’exception.
Forme
try (CREATION DE RESOURCES R1, R2...) {
CODE UTILISANT R1, R2...
}
Les resources sont des objets de classes qui ont une méthode
close. (Closeable)
public static void testEcritureTexte (String fname)
throws IOException {
try (FileWriter f= new FileWriter(fname)) {
f.write("hello ");
f.write("world.");
f.write("It works !");
} // pas besoin de close :
//f est fermé automatiquement.
}
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
48 / 50
Classe Files
Classe utilitaire (helper class) = classe pour méthodes statique.
Utilise Path et non plus File (cours prochain)
méthodes de manipulation de fichier (copie, déplacement...) ;
comprend des notions complexes (liens symboliques, etc.) ;
contient des méthodes-fabriques (factory methods) pour créer des
flux. Exemple :
static BufferedReader newBufferedReader (Path
path, Charset cs)
throws throws IOException
Crée un flux texte en lecture, en spécifiant le chemin du
fichier et son codage. (Rmq : pour le codage, utiliser la
méthode statique Charset.forName())
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
49 / 50
Entrées/sorties textes simplifiées
classe java.io.Console, récupérable par l’appel
System.console(). Entrées/sorties clavier/écran formatées,
lecture de mot de passe, etc ;
Scanner (on en cause au cours suivant) ;
PrintWriter : affichages de type printf du C (déjà en 1.5).
Serge Rosmorduc ()
Java intensifEntrées/Sorties
2013-2014
50 / 50
Téléchargement