Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Chapter 16 – Files and Streams Announcements Only responsible for 16.1,16.3 Other sections “encouraged” Responsible for online supplements for Exceptions and File I/O (see syllabus) Chapter Goals To be able to read and write text files To become familiar with the concepts of text and binary formats To learn about encryption To understand when to use sequential and random file access keyboard standard input stream CPU standard output stream monitor terminal console What does information travel across? Streams MEM HDD keyboard standard input stream CPU monitor terminal console standard output stream What does information travel across? Streams file input stream LOAD READ MEM HDD files file output stream SAVE WRITE 16.1 Reading and Writing Text Files Text files – files containing plain text Created with editors such as notepad, etc. Simplest way to learn it so extend our use of Scanner Associate with files instead of System.in All input classes, except Scanner, are in java.io import java.io.*; Review: Scanner Two ways to use scanner two constructors First constructors takes an object of type java.io.InputStream – stores information about the connection between an input device and the computer or program Example: System.in Recall – only associate one instance of with System.in in your program Scanner Review: Numerical Input First way: Use nextInt() int number = scanner.nextInt(); Second way: Use nextLine(), Integer.parseInt() String input = scanner.nextLine(); int number = Integer.parseInt(input); What’s the difference? Exceptions nextInt() throws InputMismatchException parseInt() throws NumberFormatException Optimal use nextInt() when multiple information on one line nextLine() + parseInt() when one number per line Reading To read from a disk file, construct a FileReader Then, use the FileReader to construct a Scanner object FileReader reader = new FileReader("input.txt"); Scanner in = new Scanner(reader); Alternative Use File instead of FileReader Has an exists( ) method we can call to avoid FileNotFoundException File file = new File ("input.txt"); Scanner in; if(file.exists()){ in = new Scanner(file); } else { //ask for another file } What does this do? Allows us to use methods we already know next, nextLine, nextInt, etc. Reads the information from the file instead of console File Class java.io.File associated with actual file on hard drive used to check file's status Constructors File(<full path>), File(<path>, <filename>) Predicate Methods exists() canRead(), canWrite() isFile(), isDirectory() Writing To File We will use a PrintWriter object to write to a file What if file already exists? Empty file (delete whatever is there) Doesn’t exist? Create empty file with that name How do we use a PrintWriter object? Have we already seen one? Almost. PrintWriter The out field of System is a PrintStream object associated with the console. PrintWriter is a similar class optimized for writing characters. We will associate our PrintWriter with a file now Can use either a filename or File object PrintWriter fileOut = new PrintWriter("output.txt"); fileOut.println(29.95); fileOut.println(new Rectangle(5, 10, 15, 25)); fileOut.println("Hello, World!"); This will print the exact same information as with System.out (except to a file “output.txt”)! Closing File Only difference is that we have to close the file stream when we are done writing If we do not, some output may not get written At the end of output, call close() fileOut.close(); Why? Short answer When you call print( ) and/or println( ), the output is actually written to buffer. When you close or flush the output, the buffer is written to the file The slowest part of the computer is hard drive operations – much more efficient to write once instead of writing repeated times File name When determining a file name, default is to place in the same directory as your .class files If we want to define other place, use absolute path (e.g. C:\My Documents) in = new FileReader(“C:\\homework\\input.dat”); Getting it all to work Remember: Have to import from java.io I/O requires us to catch checked exceptions java.io.IOException How long do we read from the file? Until the end. (duh) Use the hasNext( ), hasNextLine( ) and hasNextInt( ) predicate methods from Scanner. Otherwise you risk creating a NoSuchElementException Java Input Review CONSOLE: Scanner stdin = new Scanner( System.in ); FILE: Scanner inFile = new Scanner( new File ( srcFileName ) ); import import import import java.io.FileReader; java.io.IOException; java.io.PrintWriter; java.util.Scanner; public class LineNumberer { public static void main(String[] args){ Scanner console = new Scanner(System.in); System.out.print(“Enter input file: "); String inFile = console.next(); System.out.print(“Enter output file: "); String outFile = console.next(); try{ File reader = new File(inFile); Scanner in = new Scanner(reader); PrintWriter out = new PrintWriter(outputFileName); int lineNumber = 1; while (in.hasNextLine()){ String line = in.nextLine(); out.println("/* " + lineNumber + " */ " + line); lineNumber++; } out.close(); } catch (IOException exception){ System.out.println("Error processing file: " + exception.getMessage()); } } } Common Error You can run into problems using nextLine( ) in conjunction with nextInt( ) from the Scanner class. 77 hello In order to read this file You typed this code, but got this output int I = input.nextInt(); String s = input.nextLine(); System.out.println(i+”,”+s); What went wrong? 77, Buffering gone bad To Java, the file is a long buffer of characters nextInt removes the characters corresponding to a number, and that’s all. nextLine looks for the next newline character (‘\n’), and returns everything before the first one it finds, even if that String is empty! 7 7 \n h e l \n h e o \n … l l l o \n l … i = 77 h e s = “” l … What to do? Avoid using nextInt( ) and nextLine( ) in combination Always use nextLine( ) and convert to integers using Integer.parseInt( ) Use nextInt( ) in conjunction with next( ), which will skip over newlines to find the next non-whitespace string Check to see if Strings from nextLine( ) have length 0, and if so, call it again. 16.3 An Encryption Program Demonstration: Use encryption to show file techniques File encryption To scramble a file so that it is readable only to those who know the encryption method and secret keyword (Big area of CS in terms of commercial applications – biometrics, e-commerce, etc.) Caesar Cipher Encryption key – the function to change the value Simple key – shift each letter over by 1 to 25 characters If key = 3, A D B E etc. Decrypt = reverse the encryption Here we just subtract the key value Caesar Cipher for alphabetic characters public void encrypt (Scanner in, PrintWriter out, int key) { while (in.hasNextLine()) { String line = in.nextLine(); String outLine = “”; for (int i=0; i<line.length; i++) { char c = line.charAt(i); if (c >= ‘a’ && c <= ‘z’) c = (char)((c – ‘a’ + key) % 26 + ‘a’); else if (c >= ‘A’ && c <= ‘Z’) c = (char)((c – ‘A’ + key) % 26 + ‘A’); outLine += c; } out.println(outLine); } } "Meet me at the secret place." key=5 => "Rjjy rj fy ymj xjhwjy uqfhj." Modifications of Output Two constraints so far: Files are overwritten Output is buffered and not written immediately We have options to get around this if we need to More on that after this… Tokenizing Often several text values are in a single line in a file to be compact “25 38 36 34 29 60 59” Line must be broken into parts (i.e. tokens) “25” “38” “36” Tokens then can be parsed as needed “25” can be turned into the integer 25 Why Inputting each value on a new line makes the file very long May want a file of customer info – name, age, phone number all on one line File usually separate each piece of info with a delimiter – any special character designating a new piece of data (space in previous example) Tokenizing in Java Use a method of the String class called split Parameters: delimiting rules Returns: An array of tokens We need to determine what delimiters are needed for each line. Put them in a string that looks like this: “[<delimeters>]+” “[,]+” “[ \n\t]+” String Tokenizing in Java Scanner stdin = new Scanner(System.in); System.out.print("Enter a line with commaseparated integers(no space): " ); String input = stdin.nextLine(); String[] st = input.split(“[,]+”); for ( int i=0; i<st.length; i++ ) { int n = Integer.parseInt(st[i]); System.out.println(n); } What if I want to read this file? Class 1:8:10:7:6:5 Class 2:4:4:5:10:8:8:8 Class 3:6:7:9:10:7:5 Class 4:9:9:8:7:8 Class 5:9:10:9:3 Write a program to print out the average of the scores for each class File gradeFile = new File(“scores.txt”); if(gradeFile.exists()) { Scanner inFile = new Scanner(gradeFile); while( inFile.hasNextLine() ) { String line = inFile.nextLine(); String[] st = line.split(“[:]+"); System.out.print(st[0] + “’s”); double sum = 0; for (int n=1; n<st.length; n++) sum += Integer.parseInt(st[n]); System.our.println(" average is "+ sum/(st.length-1)); } inFile.close(); } Modifications of Output Two constraints so far: Files are overwritten Output is buffered and not written immediately But what if we want more control? File Class java.io.FileWriter Associated with File object Connects an output stream to write bytes of info Constructors FileWriter( <Filename>, <boolean> ); true to append data, false to overwrite all of file This will overwrite an existing file To avoid, create File object and see if exists() is true Java File Output PrintWriter composed from several objects false: overwrite true: appends PrintWriter out = new PrintWriter( new FileWriter( dstFileName, false ), true ); throws FileNotFoundException Methods true: autoflush false: no autoflush print(), println(): buffers data to write flush(): sends buffered output to destination close(): flushes and closes stream Java File Output // Append to an existing file PrintWriter outFile1 = new PrintWriter( new FileWriter(dstFileName,true),false); // Autoflush on println PrintWriter outFile2 = new PrintWriter( new FileWriter(dstFileName,false),true); outFile1.println( “appended w/out flush” ); outFile2.println( “overwrite with flush” ); to flush or not to flush Advantage to flush: Disadvantage Safer – guaranteed that all of our data will write to the file Less efficient – writing to file takes up time, more efficient to flush once (on close) Can call flush( ) on a PrintWriter object created with just a filename at any time to force the buffer to disk Other Ways to Read/Write Files Binary files (InputStream/OutputStream) Storing readable text is rather inefficient (2 bytes/character, but we tend to use less than 100 letters) Images, music, class files use 0’s and 1’s directly Can read and write these files, but we typically must work byte by byte Random access (RandomAccessFile) What if we don’t want to read from the beginning to the end? We have a “cursor” in the file, and can “seek” around to different points in any order, overwriting what was there before