Chapter 2: Streams and File I/O - Advanced Programming
Document Details

Uploaded by FastForgetMeNot7181
Haramaya University
2025
Tags
Summary
This document is a presentation on Java programming's input and output (I/O) operations. It covers topics such as file and directory handling, stream I/O in the standard Java I/O package discussing byte-based and character-based I/O. Concepts such as object serialization are also introduced. The content aligns with a university-level course on advanced programming.
Full Transcript
Haramaya University College of Computing and Informatics Department of Software Engineering Advanced Programming January 2025 CHAPTER 2 Streams and File I/O 2 Java input and output JDK has two sets of I/O packages:...
Haramaya University College of Computing and Informatics Department of Software Engineering Advanced Programming January 2025 CHAPTER 2 Streams and File I/O 2 Java input and output JDK has two sets of I/O packages: The Standard I/O (in package java.io), introduced since JDK 1.0 for stream-based I/O, and The New I/O (in packages java.nio), introduced in JDK 1.4, for more efficient buffer-based I/O JDK 1.5 introduces the formatted text-I/O via new classes java.util.Scanner and Formatter, and C-like printf() and format() methods for formatted output using format specifiers. 3 File and Directory The class java.io.File can represent either a file or a directory. [JDK 1.7] introduces a more versatile java.nio.file.Path, which overcomes many limitations of java.io.File. A path string is used to locate a file or a directory. Unfortunately, path strings are system dependent e.g., "c:\myproject\java\Hello.java" in Windows or "/myproject/java/Hello.java" in Unix/Mac. The java.io.File class maintains these system-dependent properties, for you to write programs that are portable 4 You can construct a File instance with a path string or URI, as follows. public File(String pathString) public File(String parent, String child) public File(File parent, String child) // Constructs a File instance based on the given path string. File and Directory public File(URI uri) // Constructs a File instance by converting from the cont’d… given file-URI file://.... Examples: File file = new File("in.txt"); // A file relative to the current working directory File file = new File("d:\\myproject\\java\\Hello.java"); // A file with absolute path File dir = new File("c:\\temp"); // A directory 5 Verifying Properties of a File/Directory public boolean exists() // Tests if this file/directory exists. public long length() // Returns the length of this file. File and public boolean isDirectory() // Tests if this instance is a directory. Directory public boolean isFile() // Tests if this instance is a file. public boolean canRead() // Tests if this file is cont’d… readable. public boolean canWrite() // Tests if this file is writable. public boolean delete() // Deletes this file/directory. public void deleteOnExit() // Deletes this file/directory when the program terminates. public boolean renameTo(File dest) // Renames this file. public boolean mkdir() // Makes (Creates) this directory. 6 For a directory, you can use the following methods to list its contents: public String[] list() // List the contents of this directory in a String-array public File[] listFiles() // List the contents of List Directory this directory in a File- array Example: The next program recursively lists the contents of a given directory (similar to Unix's "ls -r" command). 7 import java.io.File; public class ListDirectoryRecusive { public static void main(String[] args) { File dir = new File("d:\\myproject\\test"); // Escape sequence needed for '\' List Directory } listRecursive(dir); cont’d… public static void listRecursive(File dir) { if (dir.isDirectory()) { File[] items = dir.listFiles(); for (File item : items) { System.out.println(item.getAbsoluteFile()); if (item.isDirectory()) listRecursive(item); // Recursive call } } }} 8 Programs read inputs from data sources (e.g., keyboard, file, network, memory buffer, or another program) and write outputs to data sinks Stream I/O (e.g., display console, file, network, memory buffer, or another program). in Standard In Java standard I/O, inputs and outputs are I/O (java.io handled by the so-called streams. A stream is a sequential and contiguous one- Package) way flow of data The Java program receives data from a source by opening an input stream, and sends data to a sink by opening an output stream. 9 Stream I/O operations involve three steps: Open an input/output stream associated with a physical device (e.g., file, network, console/keyboard), by constructing an appropriate I/O Java Stream I/O stream instance. Read from the opened input stream until "end-of-stream" encountered, or write to the opened output stream (and optionally flush the buffered output). Close the input/output stream. 10 Java internally stores characters (char type) in 16-bit UCS-2 character set. But the external data source/sink could store characters in other character set (e.g., Java Stream US-ASCII, ISO-8859-x, UTF-8, UTF-16, and many others), in fixed length of 8-bit or 16- I/O Cont’d… bit, or in variable length of 1 to 4 bytes. As a consequence, Java needs to differentiate between byte-based I/O for processing raw bytes or binary data, and character-based I/O for processing texts made up of characters. 11 Java Stream I/O Cont’d… 12. Byte-Based I/O & Byte Streams 13 Byte streams are used to read/write raw bytes serially from/to an external device. All the byte streams are derived from the abstract superclasses InputStream and OutputStream, as illustrated in Byte-Based the diagram. I/O & Byte Reading from an InputStream: Streams The abstract superclass InputStream declares an abstract method read() to read one data-byte from the input source: Cont’d… public abstract int read() throws IOException returns the input byte read as an int in the range of 0 to 255, or returns -1 if "end of stream" condition is detected, or throws an IOException if it encounters an I/O error. 14 Writing to an OutputStream Similar to the input counterpart, the abstract superclass OutputStream declares an abstract method write() to write a data-byte to the output Byte-Based sink. write() takes an int. I/O & Byte Similar to the read(), two variations of the write() Streams method to write a block of bytes from a byte-array are implemented: Cont’d… public void write(byte[] bytes, int offset, int length) throws IOException // Write "length" number of bytes, from the bytes array starting from offset of index. public void write(byte[] bytes) throws IOException // Same as write(bytes, 0, bytes.length) 15 Opening & Closing I/O Streams You open an I/O stream by constructing an instance of the stream. Byte-Based Both the InputStream and the OutputStream provides a close() method to close the stream, which performs the I/O & Byte necessary clean-up operations to free up the system resources. It is a good practice to explicitly close the I/O stream, by Streams running close() in the finally clause of try-catch-finally to free up the system resources immediately when the stream is no longer needed. Cont’d… JDK 1.7 introduces a new try-with-resources syntax, which automatically closes all the opened resources after try or catch, as follows. try (FileInputStream in = new FileInputStream(...)) {...... } catch (IOException ex) { ex.printStackTrace(); } 16 Layered (or Chained) I/O Streams The I/O streams are often layered or chained with other I/O streams, for purposes such as buffering, filtering, or data-format conversion (between raw bytes and primitive types). For example, we can layer a BufferedInputStream to a FileInputStream for buffered input, and stack a DataInputStream in front for formatted data input 17 File I/O Byte-Streams - FileInputStream & FileOutputStream FileInputStream and FileOutputStream are concrete implementations to the abstract classes InputStream and OutputStream, to support I/O from disk files. Layered (or Buffered I/O Byte-Streams - BufferedInputStream & Chained) I/O BufferedOutputStream The read()/write() method in InputStream/OutputStream are Streams designed to read/write a single byte of data on each call. Buffering, which reads/writes a block of bytes from the Cont’d… external device into/from a memory buffer in a single I/O operation, is commonly applied to speed up the I/O. FileInputStream/FileOutputStream is not buffered. It is often chained to a BufferedInputStream or BufferedOutputStream, which provides the buffering. 18 Example: FileInputStream fileIn = new FileInputStream("in.dat"); Layered (or BufferedInputStream Chained) I/O bufferIn = new BufferedInputStream(fileIn); Streams DataInputStream dataIn = new Cont’d… DataInputStream(bufferIn); // or DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("in.dat"))); 19 //Copying a file byte-by-byte without Buffering. import java.io.*; public class FileCopyNoBufferJDK7 { public static void main(String[] args) { String inFileStr = "test-in.jpg"; Layered (or String outFileStr = "test-out.jpg"; Chained) I/O long startTime, elapsedTime; // for speed benchmarking Streams // Check file length File fileIn = new File(inFileStr); Cont’d… System.out.println("File size is " + fileIn.length() + " bytes"); // "try-with-resources" automatically closes all opened resources. try (FileInputStream in = new FileInputStream(inFileStr); FileOutputStream out = new FileOutputStream(outFileStr)) { 20 startTime = System.nanoTime(); int byteRead; // Read a raw byte, returns an int of 0 to 255. while ((byteRead = in.read()) != -1) { // Write the least-significant byte of int, drop the upper 3 bytes out.write(byteRead); Cont'd } elapsedTime = System.nanoTime() - startTime; System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec"); } catch (IOException ex) { ex.printStackTrace(); } }} 21 //Copying a file byte-by-byte with Buffering. import java.io.*; public class FileCopyBufferedStream { // Pre-JDK 7 public static void main(String[] args) { Layered (or String inFileStr = "test-in.jpg"; String outFileStr = "test-out.jpg"; Chained) I/O BufferedInputStream in = null; BufferedOutputStream out = null; Streams long startTime, elapsedTime; // for speed benchmarking Cont’d… // Check file length File fileIn = new File(inFileStr); System.out.println("File size is " + fileIn.length() + " bytes"); try { in = new BufferedInputStream(new FileInputStream(inFileStr)); out = new BufferedOutputStream(new FileOutputStream(outFileStr)); 22 startTime = System.nanoTime(); int byteRead; while ((byteRead = in.read()) != -1) { // Read byte-by-byte from buffer out.write(byteRead); } elapsedTime = System.nanoTime() - startTime; System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec"); Cont'd } catch (IOException ex) { ex.printStackTrace(); } finally { // always close the streams try { if (in != null) in.close(); if (out != null) out.close(); } catch (IOException ex) { ex.printStackTrace(); } }}} 23 Output Layered (or First Program: Chained) I/O File size is 417455 bytes Elapsed Time is 3781.500581 msec Streams Cont’d… Second Program: File size is 417455 bytes Elapsed Time is 61.834954 msec 24 Formatted Data-Streams: DataInputStream & DataOutputStream The DataInputStream and DataOutputStream can be Layered (or stacked on top of any InputStream and OutputStream to parse the raw bytes so as to perform I/O operations Chained) I/O in the desired data format, such as int and double. Streams To use DataInputStream for formatted input, you Cont’d… can chain up the input streams as follows: DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("in.dat"))); 25 DataOutputStream implements DataOutput interface, which provides methods to write formatted primitive data and String. For examples, Layered (or // for primitive data types public final void writeInt(int i) throws IOExcpetion; Chained) I/O //WriteFloat, WriteDouble.etc...as well Streams // String public final void writeBytes(String str) throws IOExcpetion; //writeUTF, writeChars..etc.as well… Cont’d… public final void writeChars(String str) throws IOExcpetion; // Write String as UCS-2 16-bit char. public final void writeUTF(String str) throws IOException; // Write String as UTF, with first two bytes indicating UTF bytes length 26 Character-Based I/O & Character Streams The byte/character streams refer to the unit of operation within the Java programs, which does not necessary correspond to the amount of data transferred from/to the external I/O devices. 27 Instead of InputStream and OutputStream, we use Reader and Writer for character-based I/O. Character- Based I/O & The abstract superclass Reader operates on char. It declares an abstract method read() to read one Character character from the input source. Streams read() returns the character as an int between 0 to 65535 (a char Cont’d… in Java can be treated as an unsigned 16-bit integer); or -1 if end-of-stream is detected; or throws an IOException if I/O error occurs. 28 Character- There are also two variations of read() to read a block of characters into char-array. Based I/O & public abstract int read() throws IOException Character public int read(char[] chars, int offset, int length) throws IOException Streams public int read(char[] chars) throws IOException Cont’d… The abstract superclass Writer declares an abstract method write(), which writes a character to the output sink. public void abstract void write(int aChar) throws IOException public void write(char[] chars, int offset, int length) throws IOException public void write(char[] chars) throws IOException 29 File I/O Character-Streams - FileReader & FileWriter Character- FileReader and FileWriter are concrete implementations to Based I/O & the abstract superclasses Reader and Writer, to support I/O from disk files. Character FileReader/FileWriter assumes that the default character encoding (charset) is used for the disk file. Streams Buffered I/O Character-Streams - BufferedReader & Cont’d… BufferedWriter BufferedReader and BufferedWriter can be stacked on top of FileReader/FileWriter or other character streams to perform buffered I/O, instead of character-by-character. BufferedReader provides a new method readLine(), which reads a line and returns a String (without the line delimiter). 30 Buffered I/O Character-Streams - BufferedReader & BufferedWriter Character- BufferedReader and BufferedWriter can be stacked on top of FileReader/FileWriter or other character Based I/O & streams to perform buffered I/O, instead of character- by-character. Character Streams BufferedReader provides a new method readLine(), which reads a line and returns a String (without the Cont’d… line delimiter). 31 Example Character- try (BufferedWriter out = new BufferedWriter(new FileWriter(strFilename))) { Based I/O & out.write(message); Character out.flush(); } catch (IOException ex) { Streams ex.printStackTrace(); } Cont’d… try (BufferedReader in = new BufferedReader(new FileReader(strFilename))) { String inLine; while ((inLine = in.readLine()) != null) { // exclude newline System.out.println(inLine); } } catch (IOException ex) { ex.printStackTrace(); } 32 Text File I/O - InputStreamReader and OutputStreamWriter Character- Based I/O & As mentioned, Java internally stores characters (char type) in 16-bit UCS-2 character set. Character But the external data source/sink could store characters in other character set (e.g., US-ASCII, ISO-8859-x, UTF-8, UTF-16, Streams and many others), in fixed length of 8-bit or 16-bit, or in variable length of 1 to 4 bytes. Cont’d… The FileReader/FileWriter introduced earlier uses the default charset for decoding/encoding, resulted in non- portable programs. To choose the charset, you need to use InputStreamReader and OutputStreamWriter. InputStreamReader and OutputStreamWriter are considered to be byte-to-character "bridge" streams. 33 Text File I/O - InputStreamReader and OutputStreamWriter You can choose the character set in the InputStreamReader's constructor: Character-Based public InputStreamReader(InputStream in) // Use default charset I/O & Character public InputStreamReader(InputStream in, Streams Cont’d… String charsetName) throws UnsupportedEncodingException public InputStreamReader(InputStream in, Charset cs) As the InputStreamReader/OutputStreamWriter often needs to read/write in multiple bytes, it is best to wrap it with a BufferedReader/BufferedWriter. 34 Character- Text file: OutputStreamWriter Example // Try these charsets for encoding text file Based I/O & String[] csStrs = {"UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16", "GB2312", "GBK", "BIG5"}; Character String outFileExt = "-out.txt"; // Output filenames are "charset-out.txt" // Write text file in the specified file encoding charset Streams for (int i = 0; i < csStrs.length; ++i) { try (OutputStreamWriter out = new OutputStreamWriter(new Cont’d… FileOutputStream(csStrs[i] + outFileExt), csStrs[i]); BufferedWriter bufOut = new BufferedWriter(out)) { // Buffered for efficiency System.out.println(out.getEncoding()); // Print file encoding charset bufOut.write(message); bufOut.flush(); } catch (IOException ex) { ex.printStackTrace()} } 35 Text file: InputStreamReader Example // Read text file with character-stream specifying its encoding. // The char will be translated from its file encoding charset to. Java internal UCS-2. // for (int i = 0; i < csStrs.length; ++i) { try (InputStreamReader in = new InputStreamReader( new FileInputStream(csStrs[i] + Character-Based outFileExt), csStrs[i]); BufferedReader bufIn = new BufferedReader(in)) I/O & Character { // Buffered for efficiency System.out.println(in.getEncoding()); // print file Streams Cont’d… encoding charset int inChar; int count = 0; while ((inChar = in.read()) != -1) { ++count; System.out.printf("[%d]'%c'(%04X) ", count, (char)inChar, inChar); } System.out.println(); } catch (IOException ex) { ex.printStackTrace();}}}} 36 Data streams (DataInputStream and DataOutputStream) allow you to read and write primitive data (such as int, Object double) and String, rather than individual bytes. Serialization Object streams (ObjectInputStream and and Object ObjectOutputStream) go one step further to allow you to read and write entire objects (such as Date, ArrayList or any Streams custom objects). Object serialization is the process of representing a "particular state of an object" in a serialized bit-stream, so that the bit stream can be written out to an external device (such as a disk file or network). 37 The bit-stream can later be re-constructed to recover the state of that object. Object Object serialization is necessary to save a state of an object into a disk file for persistence or sent the object across the Serialization network for applications such as Web Services, Distributed- object applications, and Remote Method Invocation (RMI). and Object In Java, object that requires to be serialized must implement Streams java.io.Serializable or java.io.Externalizable interface. cont’d… Serializable interface is an empty interface (or tagged interface) with nothing declared. Its purpose is simply to declare that particular object is serializable. 38 Object ObjectInputStream & ObjectOutputStream Serialization The ObjectInputStream and ObjectOutputStream can be used to serialize an object into a bit-stream and transfer it and Object to/from an I/O streams, via these methods: public final Object readObject() throws IOException, Streams ClassNotFoundException; public final void writeObject(Object obj) throws cont’d… IOException; ObjectInputStream and ObjectOutputStream must be stacked on top of a concrete implementation of InputStream or OutputStream, such as FileInputStream or FileOutputStream. 39 Object ObjectInputStream & ObjectOutputStream For example, the following code segment writes objects to a Serialization disk file. The ".ser" is the convention for serialized object file type. and Object Streams ObjectOutputStream out = new ObjectOutputStream( new BufferedOutputStream( new cont’d… FileOutputStream("object.ser"))); out.writeObject("The current Date and Time is "); // write a String object out.writeObject(new Date()); // write a Date object out.flush(); out.close(); 40 ObjectInputStream & ObjectOutputStream To read and re-construct the object back in a program, use the method readObject(), Object Serialization which returns an java.lang.Object. Downcast the Object back to its original type. and Object Streams cont’d… ObjectInputStream in = new ObjectInputStream( new BufferedInputStream( new FileInputStream("object.ser"))); //String str = (String)in.readObject(); Date d = (Date)in.readObject(); // downcast in.close(); 41 The End 42