1.52k likes | 1.65k Views
Hoofdstuk 17: . FILES EN STREAMS. H 17. FILES EN STREAMS 1. INLEIDING. Files kan je enkel gebruiken in applicaties, NIET in applets Korte termijn geheugen versus lange termijn geheugen: Korte termijn geheugen: Variabelen, arrays Ogeslagen in het interne geheugen
E N D
Hoofdstuk 17: FILES EN STREAMS
H 17. FILES EN STREAMS1. INLEIDING • Files kan je enkel gebruiken in applicaties, NIET in applets • Korte termijn geheugen versus lange termijn geheugen: • Korte termijn geheugen: • Variabelen, arrays • Ogeslagen in het interne geheugen • De inhoud verdwijnt bij einde van de scope • Lange termijn geheugen: • Files en databanken • Opgeslagen op secundaire geheugenmedia • Persistente (“blijvende”) gegevens
H 17. FILES EN STREAMS1. INLEIDING • Files • Lange termijn opslag van grote hoeveelheden gegevens • Persistente gegevens (blijven bestaan na beëindiging van een programma) • Opgeslagen op secundaire geheugenmedia • Magnetische schijven • Optische schijven • Magnetische tapes • Sequentiële files en random access files
H 17. FILES EN STREAMS1. INLEIDING • Sequentiële file • Je doorloopt de gegevens in een sequentiële file van voor naar achter • Random access file • Je krijgt op willekeurige manier toegang tot de gegevens in een random access file
H 17. FILES EN STREAMS1. INLEIDING • Java file verwerking is een onderdeel van Java stream verwerking. • Een stream is een stroom van gegevens. Streams hebben niet alleen met files te maken, maar een stream is een manier om allerlei vormen van gegevenstransport in een computer te beschrijven: gegevens die via het toetsenbord binnenkomen, of via een netwerkkabel of gegevens die via twee programma’s worden uitgewisseld, kan je ook beschouwen als een stream. • Mogelijkheden van streams: • Bytes lezen uit en schrijven naar het geheugen • Bytes lezen uit en schrijven naar files • Bytes lezen en schrijven over netwerkverbindingen
H 17. FILES EN STREAMS2. Data hierarchie • Bit is de kleinste data item in een computer • Bit is 0 of 1 • Bit is een samentrekking van “binary digit” • Programmeurs werken met hogere niveau data items • Decimal digits: (0-9) • Letters: (A-Z en a-z) • Speciale symbolen zoals $, @, %, &, *, (, ), -, +, “, :, ?, /, … • Java gebruikt Unicode karakters, opgebouwd uit 2 bytes • Een byte bevat 8 bits • Velden (Java attribuutvariabelen) • Opgebouwd uit karakters of bytes
H 17. FILES EN STREAMS2. Data hierarchie • Data hierarchie • Data items in a computer vormen een hierarchie • Bits -> karakters -> velden -> ... • Records • Samengesteld uit meerdere velden • Geïmplementeerd als een class in Java • File is a groep van gerelateerde records • Elk record heeft een recordsleutel (record key) • Een recordsleutel is een unieke identificatie voor elk record in de file • Een recordsleutel vergemakkelijkt het opzoeken van een bepaald record uit een bestand • Sequentiële file • Records worden opgeslagen in volgorde van de recordsleutel
Sally Black Tom Blue Judy Green File Iris Orange Randy Red Name Color Record J u d y Field 01001010 Byte (ASCII character J) 1 Bit Fig. 17.1 Data hierarchy
n-1 7 6 2 4 5 1 8 9 ... 0 3 ... end-of-file marker H 17. FILES EN STREAMS3. Files en streams • Java ziet elke file als een sequentiële stream van bytes
H 17. FILES EN STREAMS3. Files en streams • Elk besturingssysteem kan het einde van een file bepalen door: • end-of-file merkteken • het totaal aantal bytes van de file bij te houden • Een java programma krijgt een signaal van het besturingssysteem wanneer het programma het einde van een stream bereikt door middel van een exceptie of door middel van een specifieke returnwaarde van een methode
H 17. FILES EN STREAMS3. Files en streams • Een file wordt geassocieerd met een object • Java associeert streams met devices: • System.in:standaard input stream object, laat toe om bytes via het toetsenbord in te lezen • System.out:standaard output stream object, laat toe om bytes weer te geven op het scherm • System.err:standaard error stream object, laat toe om foutboodschappen weer te geven op het scherm • Streams kan je omleiden door gebruik te maken van de methoden setIn, setOut en setErr van de klasse SystemDoor System.in om te leiden, kan een programma bytes lezen van een andere bron zoals een file en door System.out en/of System.err om te leiden kan je bytes wegschrijven naar een file op schijf
H 17. FILES EN STREAMS3. Files en streams • File verwerking gebeurt in Java met klassen van de package java.io • FileInputStream om bytes te lezen uit een file • FileOutputStream om bytes te schrijven naar een file • FileReader om karakters uit een file te lezen • FileWriter om karakters naar een file te schrijven
H 17. FILES EN STREAMS3. Files en streams • Buffering • Een buffer is een ruimte in het geheugen • Verbetert de performantie van I/O • Kopieert elke uitvoer naar een buffer. • De volledige inhoud van de buffer wordt naar de schijf geschreven • Eén grote toegang tot de schijf neemt minder tijd in beslag dan vele kleine • BufferedOutputStream: file output met buffers • BufferedInputStream: file input met buffers
H 17. FILES EN STREAMS4. De klasse File • De klasse File • Levert bruikbare informatie over een file of een directory • Opent of verwerkt de files NIET
Voorbeeld 1: FileTest • Grafische applicatie waarbij de gebruiker de naam van een file of directory in een instantie van JTextField typt en op enter drukt. • Informatie over deze file of deze directory wordt opgehaald en getoond in een JTextArea.
Voorbeeld 1 Gegevens uit een file lezen: • Met FileReader kan je een file openen om de karakters te lezen, maar hiermee kan je geen regels tekst lezen • Met BufferedReader kan je regels tekst lezen maar kan je geen file openen om te lezen • Oplossing: • Combineer FileReader en BufferedReader • Dit heet wrapping van stream objecten: de services van de ene stream worden toegevoegd aan de andere stream.
Hier typ je de naam van de file of de directory Bestaat die file of directory niet, dan krijg je daarvan melding
Informatie over een directory Informatie over een file
1 // Fig. 17.4: FileTest.java 2 // Demonstrating the File class. 3 import java.awt.*; 4 import java.awt.event.*; 5 import java.io.*; 6 import javax.swing.*; 7 8 publicclass FileTest extends JFrame 9 implements ActionListener { 10 11 private JTextField enterField; 12 private JTextArea outputArea; 13 14 // set up GUI 15 public FileTest() 16 { 17 super( "Testing class File" ); 18 19 enterField = new JTextField( "Enter file or directory name here"); 20 enterField.addActionListener( this ); 21 outputArea = new JTextArea(); 22 23 ScrollPane scrollPane = new ScrollPane(); 24 scrollPane.add( outputArea ); 25 Importeer de package java.io
Creëert een nieuwe referentie naar File Body van de if geeft informatie over de file als deze file bestaat 26 Container container = getContentPane(); 27 container.add( enterField, BorderLayout.NORTH ); 28 container.add( scrollPane, BorderLayout.CENTER ); 29 30 setSize( 400, 400 ); 31 setVisible( true ); 32 33 } // end constructor 34 35 // display information about file user specifies 36 publicvoid actionPerformed( ActionEvent actionEvent ) 37 { 38 File name = new File( actionEvent.getActionCommand() ); 39 40 // if name exists, output information about it 41 if ( name.exists() ) { 42 outputArea.setText( name.getName() + " exists\n" + 43 ( name.isFile() ? "is a file\n" : "is not a file\n" ) + 44 ( name.isDirectory() ? "is a directory\n" : 45 "is not a directory\n" ) + 46 ( name.isAbsolute() ? "is absolute path\n" : 47 "is not absolute path\n" ) + "Last modified: " + 48 name.lastModified() + "\nLength: " + name.length() + 49 "\nPath: " + name.getPath() + "\nAbsolute path: " + 50 name.getAbsolutePath() + "\nParent: " + name.getParent() ); 51
Test of de ingegeven naam een file is Creëer een BufferedReader om de gegevens van de file te lezen Lees de tekst regel per regel tot het einde van de file en plaats de inhoud ervan in buffer 52 // output information if name is a file 53 if ( name.isFile() ) { 54 55 // append contents of file to outputArea 56 try { 57 BufferedReader input = new BufferedReader( 58 new FileReader( name ) ); 59 StringBuffer buffer = new StringBuffer(); 60 String text; 61 outputArea.append( "\n\n" ); 62 63 while ( ( text = input.readLine() ) != null ) 64 buffer.append( text + "\n" ); 65 66 outputArea.append( buffer.toString() ); 67 } 68 69 // process file processing problems 70 catch ( IOException ioException ) { 71 JOptionPane.showMessageDialog( this, "FILE ERROR", 72 "FILE ERROR", JOptionPane.ERROR_MESSAGE ); 73 } 74 75 } // end if 76
Maak een lijst van alle files in de directory Als de file niet bestaat, druk dan een foutboodschap 77 // output directory listing 78 elseif ( name.isDirectory() ) { 79 String directory[] = name.list(); 80 81 outputArea.append( "\n\nDirectory contents:\n"); 82 83 for ( int i = 0; i < directory.length; i++ ) 84 outputArea.append( directory[ i ] + "\n" ); 85 } 86 87 } // end outer if 88 89 // not file or directory, output error message 90 else { 91 JOptionPane.showMessageDialog( this, 92 actionEvent.getActionCommand() + " Does Not Exist", 93 "ERROR", JOptionPane.ERROR_MESSAGE ); 94 } 95 96 } // end method actionPerformed 97 98 publicstaticvoid main( String args[] ) 99 { 100 FileTest application = new FileTest(); 101 application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 102 } 103 104 } // end class FileTest
H 17. FILES EN STREAMS5. Creatie van een sequentiële file • De records worden de één na de andere opgeslagen (consecutief) • Java legt geen structuur op aan een file • De programmeur structureert zelf de file om tegemoet te komen aan de vereisten van de applicatie • De gebruiker geeft de records in de juiste volgorde in (dwz in stijgende volgorde van de recordsleutel)
H 17. FILES EN STREAMS5. Creatie van een sequentiële file Basisbewerkingen: • Openen van een file om in te schrijven • Schrijven van een record naar een file • Sluiten van een file • Opvangen van de mogelijke excepties: IOException
H 17. FILES EN STREAMS5. Creatie van een sequentiële file • Openen van een file om in te schrijven • Voorbeeld: File filenaam(“c:\\oef\\test.dat”); Hiermee wordt de file NIET geopend ObjectOutputStream output; output = new ObjectOutputStream (new FileOutputStream(filenaam)); Met een FileOutputStream object kan je byte arrays en individuele bytes schrijven naar een file. Om objecten te schrijven hebben we wrapping met ObjectOutputStream nodig.Exception handling moet toegepast worden om problemen op te vangen bij het openen van de file.
H 17. FILES EN STREAMS5. Creatie van een sequentiële file • Schrijven van een record naar een file • Voorbeeld: output.writeObject(record); output.flush(); De methode flush() schrijft de gegevens van de buffer in het geheugen naar de file • Sluiten van een file • Voorbeeld: output.close();Exception handling moet toegepast worden voor het geval een file niet kan gesloten worden.
Voorbeeld 2: Creatie van sequentiële file • Dit programma maakt een sequentiële file aan met instanties van AccountRecord.Voor elke klant wordt een instantie van AccountRecord naar de file geschreven. • De attributen van AccountRecord zijn:account, firstName, lastName en balance • De methoden van AccountRecord zijn:constructor, accessors en mutators
Voorbeeld 2 • We maken twee domeinklassen, namelijk BankUI en AccountRecord die we in de volgende voorbeelden zullen hergebruiken. Daarom nemen we ze op in een package.
Domeinklasse BankUI • Een instantie van BankUI zullen we gebruiken als userinterface • Bevat 2 instanties van JButton, een array van instanties van JLabel en een array van instanties van JTextField • Het aantal elementen van de arrays wordt in de constructor ingesteld. • Er is geen default constructor. • De methoden getFieldValues, setFieldValues en clearFields beheren de tekst van de instanties van JTextField. • De methoden getFields, getDoTask1Button en getDoTask2Button geven de individuele GUI component weer zodat een programma er bijvoorbeeld ActionListener kan aan toevoegen.
Compileer deze klasse in een package voor hergebruik Userinterface voor alle voorbeelden uit dit hoofdstuk Deze knoppen zullen de acties uitvoeren in de voorbeelden 1 // Fig. 17.5: BankUI.java 2 // A reusable GUI for the examples in this chapter. 3 package com.deitel.jhtp5.ch17; 4 5 import java.awt.*; 6 import javax.swing.*; 7 8 publicclass BankUI extends JPanel { 9 10 // label text for GUI 11 protectedfinalstatic String names[] = { "Account number", 12 "First name", "Last name", "Balance", "Transaction Amount" }; 13 14 // GUI components; protected for future subclass access 15 protected JLabel labels[]; 16 protected JTextField fields[]; 17 protected JButton doTask1, doTask2; 18 protected JPanel innerPanelCenter, innerPanelSouth; 19 20 protectedint size; // number of text fields in GUI 21 22 // constants representing text fields in GUI 23 publicstaticfinalintACCOUNT = 0, FIRSTNAME = 1, LASTNAME = 2, 24 BALANCE = 3, TRANSACTION = 4; 25
26 // Set up GUI. Constructor argument size determines the number of 27 // rows of GUI components. 28 public BankUI( int mySize ) 29 { 30 size = mySize; 31 labels = new JLabel[ size ]; 32 fields = new JTextField[ size ]; 33 34 // create labels 35 for ( int count = 0; count < labels.length; count++ ) 36 labels[ count ] = new JLabel( names[ count ] ); 37 38 // create text fields 39 for ( int count = 0; count < fields.length; count++ ) 40 fields[ count ] = new JTextField(); 41 42 // create panel to lay out labels and fields 43 innerPanelCenter = new JPanel(); 44 innerPanelCenter.setLayout( new GridLayout( size, 2 ) ); 45 46 // attach labels and fields to innerPanelCenter 47 for ( int count = 0; count < size; count++ ) { 48 innerPanelCenter.add( labels[ count ] ); 49 innerPanelCenter.add( fields[ count ] ); 50 } 51
Levert de actieknoppen af 52 // create generic buttons; no labels or event handlers 53 doTask1 = new JButton(); 54 doTask2 = new JButton(); 55 56 // create panel to lay out buttons and attach buttons 57 innerPanelSouth = new JPanel(); 58 innerPanelSouth.add( doTask1 ); 59 innerPanelSouth.add( doTask2 ); 60 61 // set layout of this container and attach panels to it 62 setLayout( new BorderLayout() ); 63 add( innerPanelCenter, BorderLayout.CENTER ); 64 add( innerPanelSouth, BorderLayout.SOUTH ); 65 66 validate(); // validate layout 67 68 } // end constructor 69 70 // return reference to generic task button doTask1 71 public JButton getDoTask1Button() 72 { 73 return doTask1; 74 } 75 76 // return reference to generic task button doTask2 77 public JButton getDoTask2Button() 78 { 79 return doTask2; 80 }
81 82 // return reference to fields array of JTextFields 83 public JTextField[] getFields() 84 { 85 return fields; 86 } 87 88 // clear content of text fields 89 publicvoid clearFields() 90 { 91 for ( int count = 0; count < size; count++ ) 92 fields[ count ].setText( "" ); 93 } 94 95 // set text field values; throw IllegalArgumentException if 96 // incorrect number of Strings in argument 97 publicvoid setFieldValues( String strings[] ) 98 throws IllegalArgumentException 99 { 100 if ( strings.length != size ) 101 thrownew IllegalArgumentException( "There must be " + 102 size + " Strings in the array" ); 103 104 for ( int count = 0; count < size; count++ ) 105 fields[ count ].setText( strings[ count ] ); 106 }
107 108 // get array of Strings with current text field contents 109 public String[] getFieldValues() 110 { 111 String values[] = new String[ size ]; 112 113 for ( int count = 0; count < size; count++ ) 114 values[ count ] = fields[ count ].getText(); 115 116 return values; 117 } 118 119 } // end class BankUI
Domeinklasse AccountRecord • Implementeert de interface Serializable zodat de instanties van AccountRecord kunnen gebruikt worden met ObjectInputStream en ObjectOutputStream. • ObjectOutputStream maakt het mogelijk om objecten te serializeren dwz converteren naar een stroom van bytes. • ObjectInputStream maakt het mogelijk om gegevens te deserializeren dwz converteren van een serie bytes naar het oorspronkelijke object. • Serializable is een tagging interface, ze bevat geen methoden. Een klasse die deze interface implementeert, is getagged om een Serializable object te zijn.
Domeinklasse AccountRecord • ObjectOutputStream schrijft enkel serialized objecten weg. • In een klasse die Serializable implementeert, moeten alle instantievariabelen Serializable zijn of moeten de instantievariabelen transient zijn om aan te duiden dat die variabelen niet Serializable zijn en dat ze dus genegeerd moeten worden bij het serializable proces • Alle variabelen van primitieve datatypen zijn bij default serializable. • Voor referentievariabelen moet de klasse en mogelijk ook de superklasse nagekeken worden om zeker te zijn dat het type Serializable is.
Domeinklasse AccountRecord • Attributen: • account • firstName • lastName • balance • Methoden: • constructoren • accessors en mutators
Compileer deze klasse in een package voor hergebruik Implementeert Serializable zodat instanties van AccountRecord gebruikt kunnen worden met input en output streams 1 // Fig. 17.6: AccountRecord.java 2 // A class that represents one record of information. 3 package com.deitel.jhtp5.ch17; 4 5 import java.io.Serializable; 6 7 publicclass AccountRecord implements Serializable { 8 privateint account; 9 private String firstName; 10 private String lastName; 11 privatedouble balance; 12 13 // no-argument constructor calls other constructor with default values 14 public AccountRecord() 15 { 16 this( 0, "", "", 0.0 ); 17 } 18 19 // initialize a record 20 public AccountRecord( int acct, String first, String last, double bal) 21 { 22 setAccount( acct ); 23 setFirstName( first ); 24 setLastName( last ); 25 setBalance( bal ); 26 } 27
28 // set account number 29 publicvoid setAccount( int acct ) 30 { 31 account = acct; 32 } 33 34 // get account number 35 publicint getAccount() 36 { 37 return account; 38 } 39 40 // set first name 41 publicvoid setFirstName( String first ) 42 { 43 firstName = first; 44 } 45 46 // get first name 47 public String getFirstName() 48 { 49 return firstName; 50 } 51
52 // set last name 53 publicvoid setLastName( String last ) 54 { 55 lastName = last; 56 } 57 58 // get last name 59 public String getLastName() 60 { 61 return lastName; 62 } 63 64 // set balance 65 publicvoid setBalance( double bal ) 66 { 67 balance = bal; 68 } 69 70 // get balance 71 publicdouble getBalance() 72 { 73 return balance; 74 } 75 76 } // end class AccountRecord
Voorbeeld 2: Creatie van sequentiële file • We tonen een scherm met labels en tekstvelden om de records in te vullen en twee knoppen “Save into file” en “Enter”. Slechts één van beide knoppen is geactiveerd. • Eerst wordt een bestand gekozen waarin de records worden weggeschreven. Hiervoor maken we gebruik van een instantie van JFileChooser met als selectiemode FILES_ONLY. We gebruiken de methode showSaveDialog van JFileChooser om de dialoogbox Save te tonen. Dit is een modal dialoogbox, dwz de gebruiker moet eerst deze dialoogbox sluiten vooraleer hij een andere actie kan ondernemen. De methode getSelectedFile levert de waarde af van de geselecteerde file van het type File. Die bevat informatie over de file zoals de naam en de plaats, maar NIET de inhoud van de file. Om de file te openen gebruiken we FileOutputStream. De file wordt op deze manier geopend voor uitvoer en de eventueel aanwezige inhoud gaat op die manier verloren.
Voorbeeld 2 • Willen we een file openen voor uitbreiding (append), dan maken we gebruik van de constructor met twee argumenten. De bestaande inhoud van de file gaat dan NIET verloren, maar wordt uitgebreid • Voorbeeld: new FileOutputStream(filenaam, true); • Om objecten te schrijven naar de file, wrappen we de file in een ObjectOutputStream • De recordvelden worden ingevuld met de correcte gegevens en als men op de “Enter”-knop drukt, wordt het record weggeschreven naar de file. • De records worden weggeschreven in stijgende volgorde van de recordsleutel (Account Number).
Voorbeeld 2 • Het object wegschrijven naar de file gebeurt met de methode writeObject. Nadien wordt de methode flush gebruikt om de gegevens uit de buffer in het geheugen dadelijk weg te schrijven naar de file. • Wanneer je wrapped files gebruikt, moet de buitenste stream gebruikt worden om het bestand te sluiten.
Het programma toont deze grafische interface Je kan enkel op de knop Save into File... klikken En dan krijg je de volgende dialoogbox Hier geef je de naam van de file die je wenst aan te maken
Je kan nu de gegevens van de records invoeren. Wanneer je op de Enter-knop klikt, worden de gegevens weggeschreven naar de file
Importeer de grafische interface, een instantie van de klasse BankUI en het record, een instantie van de klasse AccountRecord Creëer de grafische interface en haal een referentie naar de eerste actie knop op 1 // Fig. 17.7: CreateSequentialFile.java 2 // Writing objects sequentially to a file with class ObjectOutputStream. 3 import java.io.*; 4 import java.awt.*; 5 import java.awt.event.*; 6 import javax.swing.*; 7 8 import com.deitel.jhtp5.ch17.BankUI; 9 import com.deitel.jhtp5.ch17.AccountRecord; 10 11 publicclass CreateSequentialFile extends JFrame { 12 private ObjectOutputStream output; 13 private BankUI userInterface; 14 private JButton enterButton, openButton; 15 16 // set up GUI 17 public CreateSequentialFile() 18 { 19 super( "Creating a Sequential File of Objects" ); 20 21 // create instance of reusable user interface 22 userInterface = new BankUI( 4 ); // four textfields 23 getContentPane().add( userInterface, BorderLayout.CENTER ); 24 25 // configure button doTask1 for use in this program 26 openButton = userInterface.getDoTask1Button(); 27 openButton.setText( "Save into File ..." );
Haal een referentie naar de tweede actie knop op 28 29 // register listener to call openFile when button pressed 30 openButton.addActionListener( 31 32 // anonymous inner class to handle openButton event 33 new ActionListener() { 34 35 // call openFile when button pressed 36 publicvoid actionPerformed( ActionEvent event ) 37 { 38 openFile(); 39 } 40 41 } // end anonymous inner class 42 43 ); // end call to addActionListener 44 45 // configure button doTask2 for use in this program 46 enterButton = userInterface.getDoTask2Button(); 47 enterButton.setText( "Enter" ); 48 enterButton.setEnabled( false ); // disable button 49 50 // register listener to call addRecord when button pressed 51 enterButton.addActionListener( 52
53 // anonymous inner class to handle enterButton event 54 new ActionListener() { 55 56 // call addRecord when button pressed 57 publicvoid actionPerformed( ActionEvent event ) 58 { 59 addRecord(); 60 } 61 62 } // end anonymous inner class 63 64 ); // end call to addActionListener 65 66 // register window listener to handle window closing event 67 addWindowListener( 68 69 // anonymous inner class to handle windowClosing event 70 new WindowAdapter() { 71 72 // add current record in GUI to file, then close file 73 publicvoid windowClosing( WindowEvent event ) 74 { 75 if ( output != null ) 76 addRecord(); 77 78 closeFile(); 79 }