Fichier - PowerPoint by liwenting

VIEWS: 99 PAGES: 43

									             Fichier et
       Stream d’Entrée-Sortie
             IFT1025, Programmation 2
                   Jian-Yun Nie

                         Version court par Douglas Eck;
Version original @ www.iro.umontreal.ca/~nie/IFT1025/semaine_par_semaine.html
                Concepts
• Notion de fichier et de stream
• Opérations: ouverture, lecture/écriture,
  fermeture
• Format texte vs. binaire
• Accès séquentiel vs. aléatoire
• Organisation des indexes
                    Fichier
• Unité de stockage des données, sur
  disque dur,
• stockage permanent (vs. en mémoire vive)
• Un fichier contient un ensemble
  d’enregistrements
• Traitement               CPU



                          Mémoire
                          vive
          fichier

                          tampon
            Fichier en Java
• Stream: une suite de données (octets ou
  caractères)
          Opérations typiques
• Lecture:
                                      Établir un canal de
  – Ouvrir un stream                  communication

  – Lire tant qu’il y a des données
  – Fermer le stream             Relâcher les
                                      ressources allouées
• Écriture
  – Ouvrir un stream (ou créer un fichier)
  – Écrire des données tant qu’il y en a
  – Fermer le stream           Écrire ce qu’il est dans le
                               tampon, et Relâcher les
                                      ressources allouées
                             Exemple: TP1
public static void main(String[] args) {        public static void traiter_fichier() {

      ouvrir_fichier("liste_mots");                 String ligne;

      traiter_fichier();                            try { // catch EOFException

                                                       ligne = input.readLine();
      fermer_fichier();
                                                       while (ligne != null) {
  }
                                                           System.out.println(ligne);
public static void ouvrir_fichier(String nom)              ligne = input.readLine();
   {                                                   }
   try {                                        }
       input = new BufferedReader(
                new FileReader(nom));           public static void fermer_fichier() {
   }                                                try {
   catch (IOException e) {                             input.close();
       System.err.println("Impossible                  System.exit(0);
   d'ouvrir le fichier d'entree.\n" +               }
   e.toString());                                   catch (IOException e) {
       System.exit(1);                                 System.err.println("Impossible de
   }                                                fermer les fichiers.\n" + e.toString());
                                                    }
                                                  }
                     Un autre exemple
public void readFile() {
  FileReader fileReader = null;
  try {
      fileReader = new FileReader("input.txt");
     int c = fileReader.read();
      while (c != -1) {
          char d = ((char)c);
          c = fileReader.read();
      }
  } catch (FileNotFoundException e) {
      System.out.println("File was not found");
  } catch (IOException e) {
      System.out.println("Error reading from file");
  }
  if (fileReader != null) {
      try { fileReader.close(); }
      catch (IOException e) { /* ignore */ }
  }
}
         Deux unité de base
• Caractère (2 octets=16 bits) ou octet (8
  bits)
• Deux hiérarchies de classe similaires
  (mais en parallèle)
                Hiérarchies
• En haut des hiérarchies pour stream de
  caractères: 2 classes abstraites
• Reader
      java.lang.Object
            java.io.Reader
• Writer
      java.lang.Object
            java.io.Writer
• Implantent une partie des méthodes pour lire et
  écrire des caractère de 16 bits (2 octets)
Hiérarchie de Stream de caractère
• Les sous-classe de Reader




     simple          pré-traitement
• Chaque sous-classe ajoute des méthodes
Hiérarchie de Stream de caractère
• Les sous-classe de Writer
Hiérarchies Byte Stream




    System.in
Hiérarchie de Byte Stream




                        System.out


                        System.err
  Différence: caractère vs byte
• Reader:
  – int read()
  – int read(char cbuf[])
  – int read(char cbuf[], int offset, int length)

• InputStream:
  – int read()
  – int read(byte cbuf[])
  – int read(byte cbuf[], int offset, int length)
Utilisation de différentes classes
• En mémoire:
   – CharArrayReader, CharArrayWriter
   – ByteArrayInputStream, ByteArrayOutputStream
   – Lire/écrire dans un tableau de bytes

   – StringReader, StringWriter, StringBufferInputStream
   – Lire/écrire dans un String
• Pipe:
   – PipedReader, PipedWriter
   – PipedInputStream, PipedOutputStream
   – Relier la sortie d’un Stream comme une entrée d’un autre stream
     (pipeline)
                       Utilisation
• Fichier (*)
   – FileReader, FileWriter
   – FileInputStream, FileOutputStream
   – Lire/écrire dans un fichier
• Sérialisation
   – ObjectInputStream, ObjectOutputStream
• Conversion de données (*)
   – DataInputStream, DataOutputStream
   – Reconnaître les types de données primitifs
• Impression (*)
   – PrintWriter, PrintStream
   – Méthodes conviviales pour l’impression
                       Utilisation
• Buffer (Tampon)
   – BufferedReader, BufferedWriter
   – BufferedInputStream, BufferedOutputStream
   – Créer un tampon: accès plus efficace
• Filtering
   – FilterReader, FilterWriter
   – FilterInputStream, FilterOutputStream
   – Accepte un Stream, le filtre et ensuite passer à un autre Stream
• Convertir entre byte et caractère
   – InputStreamReader, OutputStreamWriter
   – Lire des bytes en caractère, ou écrire des caractère en byte
                                      Exemple
•   Utiliser FileReader et FileWriter
•   Méthodes simples disponible:
     – int read(), int read(CharBuffer []), write(int), ..
     – Exemple: copier un fichier caractère par caractère (comme un int)

     import java.io.*;
     public class Copy {
       public static void main(String[] args) throws IOException {
        File inputFile = new File("farrago.txt");
        File outputFile = new File("outagain.txt");

             FileReader in = new FileReader(inputFile);
             FileWriter out = new FileWriter(outputFile);
             int c;

             while ((c = in.read()) != -1) out.write(c);    Fin de fichier: -1
             in.close();
             out.close();
         }
     }
     – Méthodes limitées
Augmenter les possibilités: wrap
• Créer un stream en se basant sur un autre:
  FileReader in = new FileReader(new File("farrago.txt"));
• Avantage:
   – Obtenir plus de méthodes
      • Dans File: les méthodes pour gérer les fichier (delete(),
        getPath(), …) mais pas de méthode pour la lecture
      • Dans FileReader: les méthodes de base pour la lecture
   – Un autre exemple:
     DataOutputStream out = new DataOutputStream(
              new FileOutputStream("invoice1.txt"));
       • FileOutptuStream: écrire des bytes
       • DataOutputStream: les méthodes pour les types de données de
         base:
         write(int), writeBoolean(boolean), writeChar(int), writeDouble(double),
         writeFloat(float), …
    Pourquoi faire du wrapping?
• Les streams enrichis ont besoin d’un stream plus primitif
  dans son constructeur:
    – E.g.     DataInputStream(InputStream in)
               DataOutputStream(OutputStream out)
    – Impossible de créer un stream directement d’un nom de fichier
      comme
       new DataOutputStream(“sortie”);
    – Besoin de faire du wrapping:
       • New DataOutputStream(new FileOutputStream("sortie"))
•  Les streams de base s’occupe des E/S de base, et les
  streams enrichies ajoutent d’autres possibilités
• Wrapping: réutiliser les méthodes de base, et ajouter
  d’autre méthodes
Hiérarchies
                  Sink stream
Sink Type   Character Streams   Byte Streams

Memory      CharArrayReader,    ByteArrayInputStream,
            CharArrayWriter     ByteArrayOutputStream

            StringReader,       StringBufferInputStream
            StringWriter
Pipe        PipedReader,        PipedInputStream,
            PipedWriter         PipedOutputStream

File        FileReader,         FileInputStream,
            FileWriter          FileOutputStream
     Wrap dans quelle classe ?
• Dépend des méthodes dont on a besoin
• Quelques exemples
   – Lire ligne par ligne:
       • Besoin de String readLine() (null à la fin du fichier)
       • BufferedReader (sous classe de Reader)
       • Traitement:
             – Lire une ligne (String)
             – Traiter cette ligne avec un Parsing (comme TP1)
       • Constructeur: BufferedReader(Reader)
       • new BufferedReader(new FileReader("fichier")) (FileReader est
         sous-classe de Reader – classe abstraite)
   – Écrire ligne par ligne
       •   BufferedWriter (sous-classe de Writer)
       •   write(String): écrire un String
       •   newLine(): insérer un changement de ligne
       •   Traitement:
             – Organiser une ligne comme String
             – Écrire la ligne
       Wrap dans quelle classe ?
• Besoin: utiliser les méthodes comme dans
  System.out
   – write(int), …
   – print(…), println(…)
• Stream à utiliser: PrintWriter
• Constructeurs
   –   PrintWriter(OutputStream out)
   –   PrintWriter(Writer out)
   –   new PrintWriter(new FileOutputStream("fichier"))
   –   new PrintWriter(new FileWriter("fichier"))
    Wrap dans quelle classe ?
• Besoin: écrire les données des types
  primitifs
  – writeChar(Char), writeFloat(float), …
• Stream à utiliser: DataOutputStream
• Constructeur:
  – DataOutputStream(OutputStream out)
  – new DataOutputStream(new
    FileOutputStream("fichier"))
         Interface: JFileChooser
JFileChooser chooser = new JFileChooser();
FileReader in = null;
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
{
   File selectedFile = chooser.getSelectedFile();
   reader = new FileReader(selectedFile);
   ...
}
             Attention au format
• Stocker les données dans un fichier
• Mais aussi pouvoir les ressortir
• E.g. stocker le nom et la date de naissance:
   – GéraldTremblay19500101SusanneRoy19800406…
• Au moment de stocker, déterminer un format pour
  pouvoir les relire
   Façon simple:
   Gérald Tremblay 19500101 Susanne Roy 19800406…
   – Lire prénom (jusqu’à l’espace), nom, date de naissance (il faut
     ensuite la décomposer)
                          Format
• Stocker en binaire ou en caractère?
  – Caractère: lisible (e.g. avec more, vi, etc.)
  – Binaire: transformer en code binaire:
     •   int: 4 octets
     •   Long: 8 octets
     •   char: 2 octet
     •   …
  – Valeur 12345 (entier)
     •   En caractère: 12345 (10 octets)
     •   En binaire: 0 0 48 57 (4 octets) = 48*256+57
     •   Binaire plus économique en espace
     •   Binaire = espace fixe (facilite la lecture)
            Exemple en binaire
• Pour un compte bancaire:
   – No. de compte: entier
   – Montant: double
• Pour écrire un enregistrement (pour un compte)
   – writeInt(int)
   – writeDouble(double)
   – Classe: DataOutputStream
• Pour lire un enregistrement
   – readInt()
   – readDouble()
   – Classe: DataInputStream
• Penser aux écritures et aux lectures en même temps
                   DataInputStream et
                   DataOutputStream
• Lire et écrire des données des types de base
     – readBoolean(), readInt(), readFloat, readChar(), readLine();
       readUTF(), …
     – writeBoolean(boolean), writeInt(int), writeFloat(float),
       writeChar(char), writeChars(String), writeUTF(String), …
     – readUTF et writeUTF:
          • Entier en binaire correspondant à la longueur du String + String
          • Exemple: liste_mots
     ^@&sur          prep sing sur rel^@(laquelle       pron_rel
       fem sing ^@&systeme          n masc sing outil ^@ non
       adv non rel^@$echeance          n fem sing temps^@^^entite
       …
Références:
http://java.sun.com/j2se/1.5.0/docs/api/java/io/DataOutputStream.html
http://java.sun.com/j2se/1.5.0/docs/api/java/io/DataInputStream.html
                   Sérialiser
• Convertir un objet (avec une structure) en une
  suite de données dans un fichier
• Reconvertir du fichier en un objet
• Utilisation: avec ObjectOutputStream

  Employee[] staff = new Employee[3];
  ObjectOutputStream out = new
   ObjectOutputStream(new
         FileOutputStream("test2.dat"));
       out.writeObject(staff);
       out.close();
                   Sérialiser
• Utilité de sérialisation
  – Stocker un objet dans un fichier
  – Créer une copie d’objet en mémoire
  – Transmettre un objet à distance
     • Devient une transmission de String
                            Sérialiser
• Pour pouvoir sérialiser un objet:
   – sa classe doit implanter l’interface Serializable
   – Interface Serializable:
       • Pas de méthode exigée

       • Mais on peut réimplanter readObject() et writeObject() si on ne se
         contente pas de la version par défaut: defaultReadObject(),
         defaultWriteObject()

       • private void writeObject(java.io.ObjectOutputStream out)
                 throws IOException
       • private void readObject(java.io.ObjectInputStream in)
                 throws IOException, ClassNotFoundException

   – Beaucoup de classes existantes sont sérialisables (e.g.
     ArrayList)
      Sérialiser: une classe sérialisable
                                                 try{
                                                        OutputStream file = new FileOutputStream( "quarks.ser" );
import java.io.*;
                                                        OutputStream buffer = new BufferedOutputStream( file );
import java.util.*;
                                                        output = new ObjectOutputStream( buffer );
import java.util.logging.*;
                                                        output.writeObject(quarks);
public class ExerciseSerializable {
                                                    }
                                                    catch(IOException ex){
public static void main(String[] aArguments) {
                                                      fLogger.log(Level.SEVERE, "Cannot perform output.", ex);
  //create a Serializable List
                                                    }
  List quarks = new ArrayList();
                                                    finally{
  quarks.add("up");
                                                      try {
  quarks.add("down");
                                                        if (output != null) {
  quarks.add("strange");
                                                          //flush and close "output" and its underlying streams
  quarks.add("charm");
                                                          output.close();
  quarks.add("top");
                                                        }
  quarks.add("bottom");
                                                      }
                                                      catch (IOException ex ){
                                                        fLogger.log(Level.SEVERE, "Cannot close output
                                                       stream.", ex);
                                                      }
 ArrayList est sérialisable                         }
        Définir une clase sérialisable
class Employee implements Serializable          class Manager extends Employee
{ public Employee(String n, double s, Date d)   { public Manager(String n, double s, Date d,
  { name = n;                                        Employee e)
    salary = s;                                   { super(n, s, d);
    hireDate = d;                                   secretary = e;
  }                                               }

    public Employee() {}                            public Manager() {}

    public void raiseSalary(double byPercent)       public void raiseSalary(double byPercent)
    { salary *= 1 + byPercent / 100;                { // add 1/2% bonus for every year of service
    }                                                 Date today = new Date();
                                                      double bonus = 0.5 * (today.getYear() -
    public int hireYear()                              hireYear());
    { return hireDate.getYear();                      super.raiseSalary(byPercent + bonus);
    }                                               }

    public void print()                             public void print()
    { System.out.println(name + " " + salary        { super.print();
       + " " + hireYear());                           System.out.print("Secretary: ");
    }                                                 if (secretary != null) secretary.print();
                                                    }
    private double salary;
    private String name;                            private Employee secretary;
    private Date hireDate;                      }
}
          Utiliser une classe sérialisable
                                                        ObjectOutputStream out = new
                                                             ObjectOutputStream(new
import java.io.*;
                                                          FileOutputStream("test2.dat"));
import java.util.*;
                                                        out.writeObject(staff);
                                                        out.close();
class ObjectRefTest
{ public static void main(String[] args)
                                                        ObjectInputStream in = new
  { try
                                                         ObjectInputStream(
    {
                                                            new FileInputStream ("test2.dat"));
      Employee[] staff = new Employee[3];
                                                        Employee[] newStaff =
                                                            (Employee[])in.readObject();
      Employee harry = new Employee
           ("Harry Hacker", 35000,
                                                        for (int i = 0; i < newStaff.length; i++)
           new Date(1989,10,1));
                                                          newStaff[i].raiseSalary(100);
      staff[0] = harry;
                                                        for (int i = 0; i < newStaff.length; i++)
      staff[1] = new Manager("Carl Cracker",
                                                          newStaff[i].print();
           75000,
                                                       }
           new Date(1987,12,15), harry);
                                                       catch(Exception e)
      staff[2] = new Manager("Tony Tester",
           38000,
                                                       { e.printStackTrace();
           new Date(1990,3,15), harry);
                                                         System.exit(1);
                                                       }
                                                   }
                                               }
                Sortie de sérialisation
vi test2.dat:
   ¬í^@^Eur^@^K[LEmployee;ü¿6^QÅ<91>^QÇ^B^@^@xp^@^@^@^Csr^@
   ^HEmployee~BÅ<89>^V<99>q=^B^@^CD^@^FsalaryL^@^HhireDatet^@^
   PLjava/util/Date;L^@^Dnamet^@^RLjava/lang/String;xp@á^W^@^@^@^@
   ^@sr^@^Njava.util.Datehj<81>^AKYt^Y^C^@^@xpw^H^@^@7^Y×<8c>4<
   80>xt^@^LHarry Hackersr^@^GManager^U<9d><93>þ<8f>Íq^[^B^@^AL
   ^@secretaryt^@LEmployee;xq^@~^@^B@òO<80>^@^@^@^@sq^@~^@
   ^Fw^H^@^@7^L¥@t<80>xt^@^LCarl Crackerq^@~^@^Esq^@~^@@â
   <8e>^@^@^@^@^@sq^@~^@^Fw^H^@^@7^])^N<92>^@xt^@^KTony
   Testerq^@~^@ ^E
Lisible par désérialisation (readObject()):
   Harry Hacker 70000.0 1989
   Carl Cracker -555750.0 1988
   Secretary: Harry Hacker 70000.0 1989
   Tony Tester -281960.0 1990
   Secretary: Harry Hacker 70000.0 1989
  Accès séquentiel vs. aléatoire




• Séquentiel: Première donnée, suivante, …
   – Approprié pour traiter toutes les données
• Aléatoire (random): positionner à un endroit, lire les
  données à partir de cette position
   – Approprié pour sélectionner certaines données à traiter
   – Question importante:
       • Comment déterminer la position correcte ?
               RandomAccessFile
•   Un Stream en format binaire
•   Écrire et lire (selon l’ouverture)
•   Possibilité de positionner avec seek(long)
•   Exemple:
      file = new RandomAccessFile(filename, "rw");
      file.seek(100);
      int accountNumber = file.readInt();
      double balance = file.readDouble();

    Référence:
    http://java.sun.com/j2se/1.4.2/docs/api/java/io/RandomAccessFile.html
          RandomAccessFile
• Ouverture:
  file = new RandomAccessFile(filename, "rw");

                                         Mode
Modes:
 r: lecture seulement
 rw: lecture et ecriture
          RandomAccessFile
• Lecture ou écriture
  – Sans seek:
     • Position au début du fichier
     • Écrire et lire à partir de cette position comme un
       accès séquentiel
  – seek(long)
     • Positionner à la position (no. de bytes à partir du
       début)
     • Lire ou écrire à partir de cette position
                   Position
• Comment déterminer la bonne position?
  – Solution simple:
    • Chaque enregistrement = taille fixe
    • Pour un enregistrement: déterminer son numéro
    • seek(taille*no)
  – Solution plus complexe:
    • Organiser un indexe
    • Utiliser une table de hashage
Accès dans RandomAccessFile
• Exploiter seek pour déterminer la position pour
  lire ou écrire un enregistrement
  – Séquentiel: lire chaque enregistrement à partir du
    début
  – Direct: déterminer une position selon une clé, et
    lire/écrire l’enregistrement
     • Position = no. de compte * taille:
         – clé = no. de compte
     • Position est déterminer selon une conversion avec une clé
       (e.g. code permanent GILB76022304)
         – Non numérique
         – Non compact: valeur non continu

								
To top