510 likes | 620 Vues
Java I/O. L. Grewe. Overview of java.io. methods for accessing file, text data, object serialization and internationalization Sequential and Random access Reading and writing of primitive values Applications and applets are provided with three streams automatically
E N D
Java I/O L. Grewe
Overview of java.io • methods for accessing file, text data, object serialization and internationalization • Sequential and Random access • Reading and writing of primitive values • Applications and applets are provided with three streams automatically • System.out (output stream) • System.in (input stream) • System.err (error stream
Uses Streams • Two kinds of basic streams: 1. byte based streams • 8 bits, data-based • input streams and output streams 2. character based streams • 16 bits, text-based • readers and writers
Byte Streams • Two parent abstract classes: InputStream and OutputStream • Reading bytes: • InputStream class defines an abstract method public abstract int read() throws IOException • Designer of a concrete input stream class overrides this method to provide useful functionality. • E.g. in the FileInputStream class, the method reads one byte from a file • InputStream class also contains nonabstract methods to read an array of bytes or skip a number of bytes • Writing bytes: • OutputStream class defines an abstract method public abstract void write(int b) throws IOException • OutputStream class also contains nonabstract methods for tasks such as writing bytes from a specified byte array • Close the stream after reading of writing to it to free up limited operating system resources by using close()
Byte Streams (Binary Streams)…some of the hierarchy FileInputStream BufferedInputStream InputStream FilterInputStream DataInputStream Object FileOutputStream BufferedOutputStream OutputStream FilterOutputStream DataOutputStream PrintStream
ByteStream some of the hierarchy AutioInputStream FileInputStream ObjectInputStream InputStream SequenceInputStream ByteArrayInputStream PipedInputStream FilterInputStream
Byte Streams FileOutputStream ObjectOutputStream ByteArrayOutputStream OutputStream PipeOutputStream FilterOutputStream Java Programming
Byte Streams The abstract class InputStream declares methods to read bytes from a particular source. import java.io.*; public class CountBytes { public static void main(String[] args) throws IOException { InputStream in; if (args.length == 0) in = System.in; else in = new FileInputStream(args[0]); int total = 0; while (in.read() != -1) total++; System.out.println(total + " bytes"); } } Type is InputStream Reads a single byte of data and returns the byte that was read, as an integer in the range 0 to 255, not -128 to 127(unsigned).
Example code1: import java.io.*; class CountBytes { public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream(args[0]); int total = 0; while (in.read() != -1) total++; in.close(); System.out.println(total + ” bytes”); } } Example code2: import java.io.*; class TranslateByte { public static void main(String[] args) throws IOException { byte from = (byte)args[0].charAt(0); byte to = (byte)args[1].charAt(0); int x; while((x = System.in.read()) != -1) System.out.write(x == from ? to : x); } } If you run “java TranslateByte b B” and enter text bigboy via the keyboard the output will be: BigBoy!
Byte Stream - Output The abstract class OutputStream provides an abstraction for writing bytes to a destination. import java.io.*; public class TranslateByte { public static void main(String[] args) throws IOException { byte from = (byte) args[0].charAt(0); byte to = (byte) args[1].charAt(0); int b; while ((b = System.in.read()) != -1) System.out.write(b == from ? to : b); } } Type is PrintStream Run: Java TranslateByte b B Result: (input abracadabra!) aBracadaBra!
Character streams • Two parent abstract classes for characters: Reader and Writer. Each support similar methods to those of its byte stream counterpart–InputStream and OutputStream, respectively • The standard streams—System.in, System.out and System.err—existed before the invention of character streams. So they are byte streams though logically they should be character streams.
Character Streams - Reading BufferedReader InputStreamReader StringReader Reader CharArrayReader PipedReader FilterReader
Character Streams - Writing BufferedWriter OutputStreamWriter StringWriter Writer CharArrayWriter PipedWriter FilterWriter PrintWriter
Conversion between byte and character streams • The conversion streams InputStreamReader and OutputStreamReadertranslate between Character and byte streams • public InputStreamReader(InputStream in) • public InputStreamReader(InputStream in, String encoding) • public OutputStreamWriter(OutputStream out) • public OutputStreamWriter(OutputStream out, String encoding) • read method of InputStreamReader read bytes from their associated InputStream and convert them to characters using the appropriate encoding for that stream • write method of OutputStreamWriter take the supplied characters, convert them to bytes using the appropriate encoding and write them to its associated OutputStream • Closing the conversion stream also closes the associated byte stream – may not always desirable
Character Stream Example The abstract classes for reading and writing streams of characters are Reader and Writer. import java.io.*; public class CountSpace { public static void main(String[] args) throws IOException { Reader in; if (args.length == 0) in = new InputStreamReader(System.in); else in = new FileReader(args[0]); intch; int total; int spaces = 0; for (total = 0; (ch = in.read()) != -1; total++) { if (Character.isWhitespace((char) ch)) spaces++; } System.out.println(total + " chars " + spaces + " spaces"); } } The abstract class Reader provides a character stream analogous to the byte stream InputStream and the methods of Reader essentially mirror those of InputStream. Run: Java CountSpace CountSpace.java Result: 520 characters 172 spaces The conversion streams InputStreamReader and OutputStreamWriter translate between character and byte streams using either a specified character set encoding or the default encoding for the local system.
Reading from a Stream • The basic read() method reads a byte at a time. • Some other methods that read more than 1 byte • public int read(byte[] data) throws IOException • Tries to read enough bytes to fill the array data • public int read(byte[] data, int offset, int length) throws IOException • Tries to read length bytes from stream and store in data[] at starting index offset • These methods then return the number of bytes actually read. You should not assume that the array will be filled or that length bytes will actually have been read. If the end of stream is encountered, -1 is returned
Reading Use other classes with richer methods (i.e. DataInputStream) to read in different data types
Writing abstract void write(char[] cbuf, int off, intlen) Write a portion of an array of characters. void write(int c) Write a single character. voidwrite(Stringstr) Write a string. voidwrite(Stringstr, int off, intlen) Write a portion of a string. Other methods – for byte based – print*(*) abstract void close() Close the stream, flushing it abstract void flush() Flush the stream. voidwrite(char[] cbuf) Write an array of characters.
File I/O : The File class • The File class is particularly useful for retrieving information about a file or a directory from a disk. • A File object actually represents a path, not necessarily an underlying file • A File object doesn’t open files or provide any file-processing capabilities • Three constructors • public File( String name) • public File( String pathToName, String name) • public File( File directory, String name) • Main methods • boolean canRead() / boolean canWrite() • boolean exists() • boolean isFile() / boolean isDirectory() / boolean isAbsolute() • String getAbsolutePath() / String getPath() • String getParent() • String getName() • long length() • long lastModified()
File I/O • Sequential-Access file: the File streams—FileInputStream, FileOutputStream, FileReader and FileWriter—allow you to treat a file as a stream to input or output sequentially • Each file stream type has three types of constructors • A constructor that takes a String which is the name of the file • A constructor that take a File object which refers to the file • A constructor that takes a FileDescriptor object • Random-Access file: RandomAccessFile allow you to read/write data beginning at the a specified location • a file pointer is used to guide the starting position • It’s not a subclass of InputStream, OutputStream, Reader or Writer because it supports both input and output with both bytes and characters
Example of RandomAccessFile import java.io.*; class Filecopy { public static void main(String args[]) { RandomAccessFile fh1 = null; RandomAccessFile fh2 = null; long filesize = -1; byte[] buffer1; try { fh1 = new RandomAccessFile(args[0], “r”); fh2 = new RandomAccessFile(args[1], “rw”); } catch (FileNotFoundException e) { System.out.println(“File not found”); System.exit(100); } try { filesize = fh1.length(); intbufsize = (int)filesize/2; buffer1 = new byte[bufsize]; fh1.readFully(buffer1, 0, bufsize); fh2.write(buffer1, 0, bufsize); } catch (IOException e) { System.out.println("IO error occurred!"); System.exit(200); } } }
Add more efficiency • BufferedReader reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines. BufferedReader (Reader in) • For example: to wrap an InputStreamReader inside a BufferedReader BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); to wrap a FileReader inside a BufferedReader BufferedReader in = new BufferedReader(new FileReader(“fileName”)); then you can invoke in.readLine() to read from the file line by line
import java.io.*; public class EfficientReader { public static void main (String[] args) { try { BufferedReaderbr = new BufferedReader(new FileReader(args[0])); // get line String line = br.readLine(); // while not end of file… keep reading and displaying lines while (line != null) { System.out.println("Read a line:"); System.out.println(line); line = br.readLine(); } // close stream br.close(); } catch(FileNotFoundExceptionfe) { System.out.println("File not found: “+ args[0]"); } catch(IOExceptionioe) { System.out.println("Can’t read from file: “+args[0]); } } }
Buffering • Improves performance of I/O • Copies each output to a region of memory called a buffer • Entire buffer output to disk at once • One long disk access takes less time than many smaller ones • BufferedInputStream buffers file output • BufferedOutputStream buffers file input
Appending to a File To append to file instead of overwriting it pass the boolean value true as the second argument to the FileOutputStream() constructor. For example, FileOutputStreamfos = new FileOutputStream(“File.txt", true);
More on Writing to a File The java.io.FileWriter class writes text files using the platform's default character encoding and the buffer size. If you need to change these values, construct an OutputStreamReader on a FileOutputStream instead.
LineNumberReader class • The java.io.LineNumberReader class is a subclass of java.io.BufferedReader that keeps track of which line you're currently reading. It has all the methods of BufferedReader including readLine(). It also has two constructors, getLineNumber(), and setLineNumber() methods: • public LineNumberReader(Reader in) • public LineNumberReader(Reader in, int size) • public intgetLineNumber() • public void setLineNumber(intlineNumber) The setLineNumber() method does not change the file pointer. It just changes the value getLineNumber() returns. For example, it would allow you to start counting from -5 if you knew there were six lines of header data you didn't want to count.
Lets look at some examples First ---- using DataInputStream and DataOutputStream
Data Input/Output Stream example OUTPUT 256 3.141592653589793 Java DataInputStreamdis= null; try { FileInputStreamfis = new FileInputStream ("data.dat"); dis = new DataInputStream (fis); System.out.println (dis.readInt ()); System.out.println (dis.readDouble ()); System.out.println (dis.readUTF ()); } catch (IOException e) { System.out.println (e.getMessage ()); return; } finally { if (dis != null) try { dis.close (); } catch (IOException e) { } } } } import java.io.*; class DOSDISDemo { public static void main (String [] args) { DataOutputStream dos = null; try { FileOutputStreamfos = new FileOutputStream ("data.dat"); dos = new DataOutputStream (fos); dos.writeInt (256); dos.writeDouble (Math.PI); dos.writeUTF ("Java"); } catch (IOException e) { System.out.println (e.getMessage ()); return; } finally { if (dos != null) try { dos.close (); } catch (IOException e) { } }
Another Example--- Piped Streams • A Motivation? • Threads are often required to communicate. • Can use piped streams. • IDEA= connect a piped output stream to a piped input stream. • Then, one thread writes data to the piped output stream and another thread reads that data by way of the piped input stream. • CAUTION= streams have limited sizes. As a result, a writing thread could write more output to a piped output stream than that stream can accommodate, and the excess output would be lost. To prevent that from happening, the reading thread must be responsive.
import java.io.*; public class BufferedReaderTest { public static void main(String[] args) throws IOException { BufferedReadercharStream = new BufferedReader (new InputStreamReader(System.in)); String data = charStream.readLine(); // Read a line from standard input System.out.println("Input = " + data); } } The Buffered stream classes buffer their data to avoid every read or write going directly to the next stream. These classes are often used in conjunction with File streams. import java.io.*; class TextGenerator extends Thread { private Writer out; public TextGenerator(Writer out) { this.out = out; } public void run() { try { try { for (char c = 'a'; c <= 'z'; c++) out.write(c); } finally { out.close(); } } catch(IOException e) { getUncaughtExceptionHandler().uncaughtException(this, e); } } } public class Pipe { public static void main(String[] args) throws IOException { PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(out); TextGenerator data = new TextGenerator(out); data.start(); intch; while ((ch=in.read()) != -1) System.out.print((char) ch); System.out.println(); } } Buffered Streams, Piped Streams (here with same thread) InputStream Character Stream Result: abcdefghijklmnopqrstuvwxyz
Another Example--- LineNumberReader Tracks Line numbers as you are reading
The Print streams provide methods that make it easy to write the values of primitive types and object to a stream, in a human-readable text format print and println method The call out.print(f) is equivalent to out.write(String.valueOf(f).getBytes()); LineNumberReader The LineNumberReader stream keeps track of line numbers while reading text. import java.io.*; public class FindChar { public static void main(String[] args) throws IOException { if (args.length != 2) throw new IllegalArgumentException( "need char and file"); int match = args[0].charAt(0); FileReaderfileIn = new FileReader(args[1]); LineNumberReader in = new LineNumberReader(fileIn); intch; while ((ch = in.read()) != -1) { if (ch == match) { System.out.println("'" + (char) ch + "' at line " + in.getLineNumber()); return ; } } System.out.println((char) match + " not found"); } } Print Streams, LineNumberReader Run: %java FindChar I FindChar.java Result: ‘I’ at line 4
Another Example …Pushback Streams Pushback is used on an input stream to allow a byte to be read and then returned(that is "pushed back") to the stream. PushbackInputStream provides a mechanism to "peek " at what is coming from an input stream without disrupting it.
A Pushback stream lets you push back, or “unread” characters or bytes when you have read too far. Pushback is typically useful for breaking input into tokens. For example, lexical scanners often know that a token (such as an identifier) has ended only when they have read the first character that follows it. The following example uses PushbackInputStream to report the longest consecutive sequence of any single byte in its input: import java.io.*; public class SequenceCount { public static void main(String[] args) throws IOException { PushbackInputStream in = new PushbackInputStream(System.in); int max = 0; // longest sequence found intmaxB = -1; // the byte in that sequence int b; // current byte in input do { intcnt; int b1 = in.read(); for (cnt = 1; (b = in.read()) == b1; cnt++) continue; if (cnt > max) { max = cnt; // remember length maxB = b1; // remember which byte value } in.unread(b); // pushback start of ntextseq } while (b != -1); // until we hit end of input System.out.println(max + " byte of " + maxB); } } Pushback Streams Run and Result: % java SequenceCount 12345111 ^D in Unix(or ^Z in Windows) 3 bytes of 49
Another Example • Java I/O even has ability to read zip files • java.util.zip • ZipInputStream ,ZipOutputStream
Zip File Example –will list contents // ZipReader.java catch (IOException e) { System.out.println (e.getMessage ()); } finally { try { zis.close (); } catch (IOException e) { } } } } • To run ZipReader, you need access to either a Zip file or a Jar file (which is basically a Zip file with a .jar extension // ZipReader.java import java.io.*; import java.util.zip.*; class ZipReader { public static void main (String [] args) { if (args.length != 1) { System.out.println ("usage: java ZipReader pathname"); return; } ZipInputStreamzis = null; try { FileInputStreamfis = new FileInputStream (args [0]); zis = new ZipInputStream (fis); ZipEntryze; while ((ze = zis.getNextEntry()) != null) System.out.println (ze.getName ()); }
A useful class in I/O –the StreamTokenizer The StreamTokenizer gives simple tokenization. More general facility for scanning and converting input text is provided by the java.util.Scanner class.
Four token type TT_WORD TT_NUMBER TT_EOL TT_EOF import java.io.*; class StreamTokenizerDemo { public static void main(String args[]) { try { FileReaderfr = new FileReader(args[0]); BufferedReaderbr = new BufferedReader(fr); StreamTokenizerst = new StreamTokenizer(br); st.ordinaryChar('.'); st.wordChars('\'', '\''); while(st.nextToken() != StreamTokenizer.TT_EOF) { switch(st.ttype) { case StreamTokenizer.TT_WORD: System.out.println(st.lineno() + ") " + st.sval); break; case StreamTokenizer.TT_NUMBER: System.out.println(st.lineno() + ") " + st.nval); break; default: System.out.println(st.lineno() + ") " + (char)st.ttype); } } fr.close(); } catch (Exception e) { System.out.println("Exception: " + e); } } } StreamTokenzier Result 1) The 1) price 1) is 1) $ 1) 23.45 1) . 2) Is 2) that 2) too 2) expensive 2) ? 3) ( 3) I 3) don’t 3) think 3) so 3) . 3) ) Input (tokens.txt) The price is $23.45. Is that too expensive? (I don’t think so.) Run: java StreamTokenizerDemo tokens.txt
DataInput and DataOutput These interfaces define methods that transmit primitive types across a stream. Read / Write methods Read Write Type readBooleanwriteBooleanboolean readCharwriteChar char readBytewriteByte byte readShortwriteShort short readIntwriteIntint readLongwriteLong long readFloatwriteFloat float readDoublewriteDouble double readUTFwriteUTF String(in UTF format) public static void writeData(double[] data, String file) throws IOException { OutputStreamfout = new FileOutputStream(file); DataOutputStream out = new DataOutputStream(fout); out.writeInt(data.length) for(double d : data) out.writeDouble(d); out.close(); } public static double[] readData(String file) throws IOException { InputStream fin = new FileInputStream(file); DataInputStream in = new DataInputStream(fin); double[] data = new double[in.readInt()]; for (inti = 0; i < data.length; i++) data[i] = in.readDouble(); in.close(); return data; } Another on ….Data Byte Streams
Reading and Writing Objects? What if we could save and store and object and read it (bring it back to life)….wow!
Object Serialization • What is Object Serialization? • Serialization: process of converting an object’s representation into a stream of bytes • Deserialization: reconstituting an object from a byte stream • Process of reading and writing objects • Writing an object is to represent its state in a serialized form sufficient to reconstruct the object as it is read. • serialization is the process of converting a object state into a format that can be stored (for example, in a file or memory buffer, or transmitted across a network connection link) and "resurrected" later in the same or another computer environment.
How to Write to an ObjectOutputStream Writing objects to a stream is a straight-forward process. Example of constructing a Date object and then serializing that object: FileOutputStream out = new FileOutputStream("theTime"); ObjectOutputStream s = new ObjectOutputStream(out); s.writeObject("Today"); s.writeObject(new Date()); s.flush(); How to Read from an ObjectOutputStream Example that reads in the String and the Date object that was written to the file named theTime in the read example: FileInputStream in = new FileInputStream("theTime"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject(); Serializing Objects
Serializing Objects • Providing Object Serialization for Your Classes • Implementing the Serializable Interface • Customizing Serialization • Implementing the Externalizable Interface • Protecting Sensitive Information
Accessing Files • Channels • Channels were introduced in the 1.4 release of Java to provide a faster capability for a faster capability for input and output operations with files, network sockets, and piped I/O operations between programs than the methods provided by the stream classes. • The channel mechanism can take advantage of buffering and other capabilities of the underlying operating system and therefore is considerably more efficient than using the operations provided directly within the file stream classes. • A summary of the essential role of each of them in file operations • A File object encapsulates a path to a file or a directory, and such an object encapsulating a file path can be used to construct a file stream object. • A FileInputStream object encapsulates a file that can be read by a channel. A FileoutputStream object encapsulates a file that can be written by a channel. • A buffer just holds data in memory. The loaded data to be written to a file will be saved at buffer using the buffer’s put() method, and retrieved using buffer’s get() methods. • A FileChannel object can be obtained from a file stream object or a RandomAccessFile object.
Accessing Files The hierarchy of the channel interfaces
Accessing Files The Capacities of Different Buffers
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class ReadPrimes { public static void main(String[] args) { File aFile = new File("primes.bin"); FileInputStreaminFile = null; try { inFile = new FileInputStream(aFile); } catch(FileNotFoundException e) { e.printStackTrace(System.err); System.exit(1); } FileChannelinChannel = inFile.getChannel(); final int PRIMECOUNT = 6; ByteBufferbuf = ByteBuffer.allocate(8*PRIMECOUNT); long[] primes = new long[PRIMECOUNT]; try { while(inChannel.read(buf) != -1) { ((ByteBuffer)(buf.flip())).asLongBuffer().get(primes); // List the primes read on the same line System.out.println(); for(long prime : primes) System.out.printf("%10d", prime); buf.clear(); // Clear the buffer for the next read } System.out.println("\nEOF reached."); inFile.close(); // Close the file and the channel } catch(IOException e) { e.printStackTrace(System.err); System.exit(1); } System.exit(0); } } Channel Example (ReadPrimes) From the channel, read data and save to the buffer Channel Setup You also need to read the “PrimesToFile.java” which prints prime numbers to the file. Buffer Setup