1 / 50

Fichier et Stream d’Entrée-Sortie

Fichier et Stream d’Entrée-Sortie. IFT1025, Programmation 2 Jian-Yun Nie. 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.

brooke
Download Presentation

Fichier et Stream d’Entrée-Sortie

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. 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

  3. 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 fichier Mémoire vive tampon

  4. Fichier en Java • Stream: une suite de données (octets ou caractères)

  5. Opérations typiques • Lecture: • Ouvrir un stream • Lire tant qu’il y a des données • Fermer le stream • Écriture • Ouvrir un stream (ou créer un fichier) • Écrire des données tant qu’il y en a • Fermer le stream Établir un canal de communication Relâcher les ressources allouées Écrire ce qu’il est dans le tampon, et Relâcher les ressources allouées

  6. public static void main(String[] args) { ouvrir_fichier("liste_mots"); traiter_fichier(); fermer_fichier(); } public static void ouvrir_fichier(String nom) { try { input = new BufferedReader( new FileReader(nom)); } catch (IOException e) { System.err.println("Impossible d'ouvrir le fichier d'entree.\n" + e.toString()); System.exit(1); } public static void traiter_fichier() { String ligne; try { // catch EOFException ligne = input.readLine(); while (ligne != null) { System.out.println(ligne); ligne = input.readLine(); } } public static void fermer_fichier() { try { input.close(); System.exit(0); } catch (IOException e) { System.err.println("Impossible de fermer les fichiers.\n" + e.toString()); } } Exemple: TP1

  7. 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 */ } } }

  8. Deux unité de base • Caractère (2 octets=16 bits) ou octet (8 bits) • Deux hiérarchies de classe similaires (mais en parallèle)

  9. 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)

  10. Hiérarchie de Stream de caractère • Les sous-classe de Reader simple pré-traitement • Chaque sous-classe ajoute des méthodes

  11. Hiérarchie de Stream de caractère • Les sous-classe de Writer

  12. Hiérarchies Byte Stream System.in

  13. Hiérarchie de Byte Stream System.out System.err

  14. 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)

  15. 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)

  16. 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

  17. 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

  18. 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); in.close(); out.close(); } } • Méthodes limitées Fin de fichier: -1

  19. 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), …

  20. 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

  21. Hiérarchies

  22. Sink stream

  23. 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

  24. 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"))

  25. 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"))

  26. Interface: JFileChooser JFileChooser chooser = new JFileChooser(); FileReader in = null; if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { File selectedFile = chooser.getSelectedFile(); reader = new FileReader(selectedFile); . . . }

  27. 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)

  28. 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)

  29. 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

  30. 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

  31. 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();

  32. 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

  33. 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)

  34. import java.io.*; import java.util.*; import java.util.logging.*; public class ExerciseSerializable { public static void main(String[] aArguments) { //create a Serializable List List quarks = new ArrayList(); quarks.add("up"); quarks.add("down"); quarks.add("strange"); quarks.add("charm"); quarks.add("top"); quarks.add("bottom"); try{ OutputStream file = new FileOutputStream( "quarks.ser" ); OutputStream buffer = new BufferedOutputStream( file ); output = new ObjectOutputStream( buffer ); output.writeObject(quarks); } catch(IOException ex){ fLogger.log(Level.SEVERE, "Cannot perform output.", ex); } finally{ try { if (output != null) { //flush and close "output" and its underlying streams output.close(); } } catch (IOException ex ){ fLogger.log(Level.SEVERE, "Cannot close output stream.", ex); } } Sérialiser: une classe sérialisable ArrayList est sérialisable

  35. class Employee implements Serializable { public Employee(String n, double s, Date d) { name = n; salary = s; hireDate = d; } public Employee() {} public void raiseSalary(double byPercent) { salary *= 1 + byPercent / 100; } public int hireYear() { return hireDate.getYear(); } public void print() { System.out.println(name + " " + salary + " " + hireYear()); } private double salary; private String name; private Date hireDate; } class Manager extends Employee { public Manager(String n, double s, Date d, Employee e) { super(n, s, d); secretary = e; } public Manager() {} public void raiseSalary(double byPercent) { // add 1/2% bonus for every year of service Date today = new Date(); double bonus = 0.5 * (today.getYear() - hireYear()); super.raiseSalary(byPercent + bonus); } public void print() { super.print(); System.out.print("Secretary: "); if (secretary != null) secretary.print(); } private Employee secretary; } Définir une clase sérialisable

  36. import java.io.*; import java.util.*; class ObjectRefTest { public static void main(String[] args) { try { Employee[] staff = new Employee[3]; Employee harry = new Employee ("Harry Hacker", 35000, new Date(1989,10,1)); staff[0] = harry; staff[1] = new Manager("Carl Cracker", 75000, new Date(1987,12,15), harry); staff[2] = new Manager("Tony Tester", 38000, new Date(1990,3,15), harry); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test2.dat")); out.writeObject(staff); out.close(); ObjectInputStream in = new ObjectInputStream( new FileInputStream ("test2.dat")); Employee[] newStaff = (Employee[])in.readObject(); for (int i = 0; i < newStaff.length; i++) newStaff[i].raiseSalary(100); for (int i = 0; i < newStaff.length; i++) newStaff[i].print(); } catch(Exception e) { e.printStackTrace(); System.exit(1); } } } Utiliser une classe sérialisable

  37. 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

  38. 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 ?

  39. 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

  40. RandomAccessFile • Ouverture: file = new RandomAccessFile(filename, "rw"); Modes: r: lecture seulement rw: lecture et ecriture Mode

  41. 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

  42. 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

  43. 008: public class BankData 009: { 024: public void open(String filename) 025: throws IOException 026: { 027: if (file != null) file.close(); 028: file = new RandomAccessFile(filename, "rw"); 029: } 035: public int size() 036: throws IOException 037: { 038: return (int) (file.length() / RECORD_SIZE); 039: } 071: public int find(int accountNumber) 072: throws IOException 073: { 074: for (int i = 0; i < size(); i++) 075: { 076: file.seek(i * RECORD_SIZE); 077: int a = file.readInt(); 078: if (a == accountNumber) // Found a match 079: return i; 080: } 081: return -1; // No match in the entire file 082: } 056: public BankAccount read(int n) 057: throws IOException 058: { 059: file.seek(n * RECORD_SIZE); 060: int accountNumber = file.readInt(); 061: double balance = file.readDouble(); 062: return new BankAccount(accountNumber, balance); 063: } 089: public void write(int n, BankAccount account) 090: throws IOException 091: { 092: file.seek(n * RECORD_SIZE); 093: file.writeInt(account.getAccountNumber()); 094: file.writeDouble(account.getBalance()); 095: } 096: 097: private RandomAccessFile file; 098: 099: public static final int INT_SIZE = 4; 100: public static final int DOUBLE_SIZE = 8; 101: public static final int RECORD_SIZE 102: = INT_SIZE + DOUBLE_SIZE; 103: } Taille fixe pour un enregistrement

  44. 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 continue

  45. Cas 1: Recherche binaire • Les enregistrements sont stockés dans l’ordre croissant des clés • Accès binaire (O(log(n)): • Chercher(clé, début, fin): • Lire le milieu • Si clé_milieu == clé, trouvé • Si clé_milieu < clé, • Si (milieu – début) < 2, introuvable; • Sinon cherche(clé, début, milieur-1) • Si clé_milieu > clé, • Si (fin – milieu) < 2, introuvable; • Sinon cherche(clé, milieu+1, fin)

  46. Recherche binaire Fichier

  47. Cas 2: table de hashage • Table de hashage: déterminer une position pour chaque clé • Fonction de hashage: clé entier • Contraintes: • Le plus compacte possible (pas beaucoup de positions vides) • Le moins de conflit possible (2 clés – même position)

  48. Exemple simple • Transformer un code permanent en un entier: • VALB23027502 code • 4 premiers caractères: A-Z: (1-26)4 • 2 chiffres: 01-31: 1-31 • 2 chiffres: 01-12, 5-62: 1-24 • 2 chiffres: 50-40 (1950-2040): 0-90 • 2 chiffres: 01-99: 1-99 • Fonction1(clé) = concatener les codes • Compacte ? 00*** et 27***, **00*** et **32*** non utilisés • Fonction2(clé) = code(L1)*…*code(L4)*code(jour)* code(mois)*code(année)*code(derniers_ch) • Conflit? VALB23027502 = VALB23017504

  49. Approche générale • Valeur non conflictuelle (mais relativement compact) • Déterminer la taille approximative du fichier souhaitée (taille) • Valeur % primaire • Primaire est un nombre proche de la taille • E.g. <10 000 enregistrements: 10007, 10009, 10037, …12007, … • Prévoir plus large • Prévoir un mécanisme pour résoudre le conflit • Aller à la suivante • Rehashage: appliquer une autre fonction de hashage • …

  50. Cas 3: indexe • Maintenir une structure d’indexe (clé= lettre + nombre) Clé=a21

More Related