Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
Advanced Java Programming Lecture 7 - streams dr hab. Szymon Grabowski dr inż. Wojciech Bieniecki [email protected] http://wbieniec.kis.p.lodz.pl 1 What is a stream Programming of I/O operationsin Java is to use streams that are class in the package java.io The data stream is a logical concept. Means a structure similar to a queue to which the data can be added and from which data can be downloaded. Basic operations on the stream: read and write data. Input Output Byte Streams InputStream OutputStream Character sterams Reader Writer Table: The ancestors of the class hierarchy stream - abstract classes Intro to I/O The (main) hierarchy of input / output classes starts with abstract classes InputStream and OutputStream. Basic classes for reading and writing sequences of bytes are FileInputStream and FileOutputStream. When we create a file object (eg. of any of the two just mentioned classes), the file is open. FileInputStream inp = new FileInputStream("1.dat"); FileOutputStream outp = new FileOutputStream("2.dat"); Read a byte (0..255), write a byte: int read(); write(int); Read value –1 signals the end of file. 3 Byte-by-byte copy example Don’t forget exception handling! 4 4 read(...) methods from InputStream We already know that read() is abstract. The methods int read(byte[ ] b), int read(byte[ ] b, int off, int len) are non-abstract, but they internally invoke read(). Moral: any subclass of InputStream must implement read(). read(b) == read(b, 0, b.length) If b.length == 0 (or param len == 0 in the latter method), then the methods return 0. Nothing to read (EOF)? The methods return –1. 5 Binding the stream with the source or receiver Character streams Byte streams CharArrayReader CharArrayWriter ByteArrayInputStream ByteArrayOutputStream StringReader StringWriter (not used because of improper byte-to-character conversion) Pipeline (allows exchange data between two threads) PipedReader PipedWriter PipedInputStream PipedOutputStream File FileReader FileWriter FileInputStream FileOutputStream Source/receiver Memory Table: Subject classes (associated with a particular source / receiver) Combining stream functionalities FileInputStream and FileOutputStream can read / write only bytes. Would be useful to have classes for reading / writing int, longs, doubles... Fortunately, there are classes DataInputStream, DataOutputStream, but... their constructors obtain an abstract stream (InputStream, OutputStream, respectively). How to work with files? The solution is typical for Java I/O: combining filters. FileInputStream fin = new FileInputStream("numbers.dat"); DataInputStream din = new DataInputStream(fin); double x = din.readDouble(); 7 Actually this is the Decorator design pattern The Decorator pattern lets us add functionality to an object at runtime. The idea. Object x from class X contains object y from Y. Object x will be called a decorator. x forwards method calls to y. x conforms to the interface of y, which allows the decorator to be used as if x were an instance of Y. (But typically x has its own capabilities, not existing in y.) DataInputStream din = new DataInputStream( new BufferedInputStream( new FileInputStream("numbers.dat"))); 8 FileOutputStream constructors FileOutputStream(String name) FileOutputStream(String name, boolean append) FileOutputStream(File file) FileOutputStream(File file, boolean append) append == true data are added at the end of the file (otherwise an existing file is deleted and start from scratch) 9 Transforming classes In I/O operations data transformation can be performed. Java has many classes specialized in automatic processing specific types of streams. These classes implement certain types of process streams, regardless of the source / receiver Character streams Byte streams Bufferinf BufferedReader BufferedWriter BufferedInputStream BufferedOutputStream Filtering – these classes define the methods for final filters Final filters are: FilterReader FilterWriter FilterInputStream FilterOutputStream Type of processing DataInputStream & DataOutputStream, BufferedInputStream BufferedOutputStream. LineNumberInputStream, PushbackInputStream, PrintStream. You can create custom filters Transforming classes Character streams Byte streams Type of processing Byte-to-character conversion InputStreamReader OutputStreamWriter Concatenate SequenceInputStream Object serialization ObjectInputStream ObjectOutputStream Data conversion DataInputStream DataOutputStream Line counting LineNumberReader LineNumberInputStream Previewing PushbackReader PushbackInputStream Printing PrintWriter PrintStream To apply processing classes you must: Create an object associated with a physical source.receiver Create an object that is overlayed on the physical stream Text I/O Use FileReader, FileWriter classes. FileReader: converts from the default codepage to Unicode. FileWriter: the other way around... import java.io.*; public class ReadByReader { public static void main(String[] args) { StringBuffer cont = new StringBuffer(); try { FileReader inp = new FileReader(args[0]); int c; while ((c=inp.read()) != -1) cont.append((char)c); inp.close(); } catch(Exception e) { System.exit(1); } String s = cont.toString(); System.out.println(s); } } 12 Non-standard character encoding If the characters in the file to read adhere to non-standard encoding, don’t use FileReader. Use its parent class, InputStreamReader, instead. OutputStreamWriter for writing, of course. FileInputStream fis = new FileInputStream("test.txt"); InputStreamReader isr = new InputStreamReader(fis, "UTF8"); 13 Reading a text (character) file On slide 8, the method read(), from InputStreamReader (not FileReader), was used. It reads a single char (a variant reading a number of chars into an array also exists). For a greater flexibility, use Scanner. 14 For writing to a text file, use PrintWriter 15 Buffering Buffering reduces the number of physical references to external devices. For example, reading large text files it should be avoided by reading the FileReader class. Using BufferedReader class will improve the effectiveness of the program. But BufferedReader class is a processing claass, therefore can not directly take the physical data source. This source is served to the FileReader object which is wrapped into a BufferedReader FileReader fr = new FileReader("plik.txt"); BufferedReader br = new BufferedReader(fr); String line; while ((line - br. readLine()) != null){ // processinf a line } Buffering find a word in a file class Search{ public boolean hasAnyWord(String fname, Hashtable wordtab) { boolean result = false; try{ String line; FileReader fr = new FileReader(fname); BufferedReader br = new BufferedReader(fr); search: while ((line = br.readLine()) != null ){ StringTokenizer st = new StringTokenizer(line, " ,.:;()\t\r\n"); while (st.hasMoreTokens()){ String word = st.nextToken(); if (wordtab.get(word) != null){ result = true; break search; } } } br.close(); } catch (IOException e){ System.err.println(e); } return result; } } Buffering find a word in a file public class Szukaj{ public static void main(String[] args){ /* argumenty: nazwa_pliku slowo1 slowo2 ... */ if (args.length < 2) System.exit(1); Object dummy = new Object(); Hashtable words = new Hashtable(); for (int i = 1; i<args.length; i++) words.put(args[i], dummy); Search srch = new Search(); boolean result = srch.hasAnyWord(args[0], words); String msg = " nie zawiera zadnego ze slow:"; if (result) msg = " zawiera ktores ze slow:"; System.out.println("Plik "+args[0]+msg); Enumeration en = words.keys();// uzyskujemy wszystkie klucze tablicy while (en.hasMoreElements()){ // ... i przebiegamy je po kolei String word = (String) en.nextElement(); System.out.println(word); } } } encoding ava uses Unicode characters. These are 16-bit size. If the constructors of these classes do not specify an encoding, the conversions will be accepted by default. public class DefaultEncoding { public static void main(String args[]) { String p = System.getProperty("file.encoding") ; System.out.println(p); } } Exemplary results: ISO8859_1, ibm-852, Cp852 (Latin 2) , Cpl252 (Windows Western Europe/Latin-1). Character streams can - invisible to us - convert the source byte character streams, and vice versa. "Under cover" of this process, there are two classes: InputStreamReader and OutputStreamWriter, which make the appropriate conversions when reading / writing. HTML conversion import java.io.*; public class Convert { public static void main(String[] args) { String infile = args[0],// plik we in_enc = args[1], // strona kodowa wejścia outfile = args[2], // plik wynikowy out_enc = args[3]; // strona kodowa wyjścia try{ FileInputStream fis = new FileInputStream(infile); InputStreamReader in = new InputStreamReader(fis, in_enc); FileOutputStream fos = new FileOutputStream(outfile); OutputStreamWriter out = new OutputStreamWriter(fos, out_enc); int c; while ((c = in.read()) != -1) out.write(c); in.close(); out.close(); } catch (IOException e){ System.err.println(e);} } } Object I/O In Java, there exists a powerful mechanism for storing arbitrary objects in files (e.g., on disk). Or, more generally, sending them to a stream. We save objects and then can recover them. This uses object serialization. Basically, we can use methods writeObject() and readObject() – for any objects. myOutFile.writeObject(thisObject); val = (MyClass)myInFile.readObject(); // casting necessary 21 Object I/O We shall use classes derived from OutputStream and InputStream: OutputStream FileOutputStream ObjectOutputStream InputStream FileInputStream ObjectInputStream Import them from java.io. 22 Object I/O For output: ObjectOutputStream out; out = new ObjectOutputStream ( new(FileOutputStream("myFile.dat") ); // use out.writeObject(...) out.close(); For input (say, we read from the same file): ObjectInputStream inp; inp = new ObjectOutputStream ( new(FileOutputStream("myFile.dat") ); // use inp.readObject() inp.close(); 23 Object serialization writeObject() is powerful. It converts an object of any class into a bit sequence and then sends it to a stream, in a way that it can be subsequently retrieved (using readObject()) as an object of the original class. Not surprisingly, static fields are NOT written to the stream. Serialized objects may contain e.g. other objects as their fields. No problem with e.g. arrays either. Even multi-dim arrays (btw, in Java a 2-d array is, formally speaking, an array of arrays). 24 How to serialize our own class Our class must simply implement the interface Serializable. public class Tank implements Serializable { ... } Serializable is empty (=has no methods)! Marker interfaces simply make possible to check a data type, e.g. with instanceof operator. That’s what the writeObject(...) method does: checks if x instanceof Serializable == true. If so, it sends the object to a stream, otherwise it doesn’t. (Other marker interfaces: Clonable, RandomAccess...) 25 Casting required When we read an object with readObject(), what we get is actually an object of the class Object(). Cast examples: Student s = (Student)inpFile.readObject(); int[] tab = (int[])inpFile.readObject(); 26 Remember about (checked) exceptions The signatures are: public final readObject() throws IOException, ClassNotFoundException; public final void writeObject(Object obj) throws IOException; 27 What if an object is shared by a couple others as part of their state? public class GradStudent { private Supervisor sv; ... } // many grad students may have the same supervisor No problem! Each GradStudent stores a ‘reference’ to the same Supervisor. BUT! Those refs are not just mem addresses! Instead, SERIALizable objects get unique SERIAL numbers. 28 Serialization algorithm • each encountered object reference gets a unique serial number, • if an object reference is encounted for the first time, the object data are saved to the output stream, • else (object ref already saved), it is written that here we have the same object as the one already saved with serial number x. Reading back: reversed procedure. 29 Can we serialize selected fields? Yes. Mark a field with keyword transient and it won’t be serialized. E.g. (example from Core Java, vol. 2): public class LabeledPoint implements Serializable { ... private String label; private transient Point2D.Double point; } // because Point2D.Double from java.awt.geom // is not serializable and we want to avoid NonSerializableException 30 Serialize on our own Sometimes we’re not happy with the default serialization mechanism. Cont’d previous slide example: we do want to serialize the data from the Point2D.Double field. Shall we write the serialization routine from scratch? First we label the Point2D.Double field as transient (of course). Then we implement private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException; private void writeObject(ObjectOutputStream out) throws IOException; A serializable class is allowed to define those methods. 31 Serialize on our own, cont’d private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // can be called only from writeObject(...) ! out.writeDouble(point.getX()); out.writeDouble(point.getY()); } private void readObject(ObjectInputStream in) throws IOException { in.defaultReadObject(); // can be called only from readObject(...) ! double x = in.readDouble(); double y = in.readDouble(); point = new Point2D.Double(x, y); } 32 Versioning writeObject(...) writes several things, incl. class name and 8-byte class fingerprint. The fingerprint is to ensure the class definition has not changed (between serialization and deserialization). What if it has changed? Indicate that the new class is compatible with the old one. Run serialver OurClass for the old class to obtain its fingerprint. Then add as (e.g.): public static final long serialVersionUID = -1814239825517340645L; to the new version of OurClass. No problem if only methods changed. If new fields added: risky. If field type changed: even worse. 33 java.util.zip (compressed I/O) The package java.util.zip contains several classes for data compression / decompression. A few class examples: Deflater – in-memory compression using zlib library Inflater – in-memory decompression using zlib library GzipOutputStream – compresses data using the GZIP format. To use it, construct a GZIPOutputStream that wraps a regular output stream and use write() to write compressed data GzipInputStream – like above, but for reading data ZipOutputStream, ZipInputStream – I won’t offend your intelligence ZipEntry – represents a single element (entry) in a zip file 34 Compression example (using gzip format) Writing to a stream of GZipOutStream type. Which means it’ll be compressed on-the-fly. 35 ZipFile class Used to read the entries in a zip file. In order to create or to read these entries, you have to pass a file as argument to the constructor: ZipFile zipfile = new ZipFile(new File("c:\\test.zip")); This instantiation can throw a ZipException, as well as IOException. One of the solutions is to surround it in a try/catch: try { zipfile = new ZipFile(new File("c:\\test.zip")); } catch (ZipException e) { } catch (IOException e) { } Now, files in a zip can be e.g. listed. 36 Listing a zip file 37 File class Representation of a file or directory path name. This class contains several methods for working with the path name, deleting and renaming files, creating new directories, listing the contents of a directory, etc. File f1 = new File("/usr/local/bin/dog"); File f2 = new File("d:/my_pets/cat1.txt"); File f3 = new File("cat.txt"); File g = new File("/windows/system"); File h = new File(g, "rabbit.gif"); File i = new File("/windows/system", "rabbit.gif"); // objects h and i refer to the same file! 38 File class Selected methods (1/2) boolean canRead() // Returns true if the file is readable boolean exists() // Returns true if the file exists boolean isDirectory() // Returns true if the file name is a directory boolean isFile() // Returns true if the file name // is a "normal" file (depends on OS) long length() // Returns the file length boolean setReadOnly() // (since 1.2) Marks the file read-only // (returns true if succeeded) boolean delete() // Deletes the file specified by this name. boolean mkdir() // Creates this directory. // All parent directories must already exist. 39 File class Selected methods (2/2) String[] list() // Returns an array of Strings with names of the // files from this directory. Returns null if not a dir. String[] list(FileNameFilter filter) File[] listFiles() File[] listFiles(FileFilter) File[] listFiles(FileNameFilter) // (all three since 1.2) FileFilter: public interface with only one method: boolean accept(File pathname); // tests if the specified pathname should be included in a pathname list FileNameFilter: similar interface but the arg list for accept() different Beware: JFileChooser (Swing) makes use of javax.swing.filechooser.FileFilter, 40 not java.io.FileFilter!