Input Files Using the File Class to Access and Manipulate Files Using the Scanner Class to Read Files • File Paths • Checked Exceptions • Token by Token Processing 1 2Data and Files • Electronic data are stored in files – Java programs – Word documents – Excel Spreadsheets – Images • Java programs can read files, process files, and write files – Called input/output (I/O) • Reading a file is very similar to reading input from the command line! 3File Objects • Use File objects to represent and interact with files on the computer – import java.io.*; • Create a on disk. – Creating a File object in a Java program does not create a new file on the system File object to get info about a file File f = new File("data.txt"); 4File Methods https://docs.oracle.com/javase/8/docs/api/java/io/File.html if (f.exists() } && f.length() > 1000) { f.delete(); Method Description canRead() Returns whether file is able to be read delete() Deletes the given file exists() Whether or not this file exists on the system getAbsolutePath() The full path to where this file is located getName() The name of this file as a String, without any path attached isDirectory() Whether this file represents a directory/folder on the system isFile() Whether this files represents a file (non-folder) on the system length() The number of characters in this file renameTo(file) Changes this files name to be the given file’s name File f = new File("data.txt"); 5File paths • absolute path: specifies the file’s location starting at the top level folder – / in Linux (/afs/unity.ncsu.edu/users/m/mlglatz/csc116) – C:\ in Windows (C:\users\mlglatz\csc116 or C:\csc116) – Specify file paths using / in Java. Even if you are on a Windows machine, Java will handle the paths properly • relative path: does not specify any top-level folder names.dat input/kinglear.txt – Assumed to be relative to the current directory: File f = new File("data/readme.txt"); If our program is in ~/csc116/exercises/Lab13 Filewill look for ~/csc116/exercises/Lab13/data/readme.txt 6Using File Methods • Download the following file from Lecture 13 on our course Moodle page (this was copied from the book website): • hamlet.txt • In the same directory, create a file named FileInfo.java with the following code: import java.io.*; public class FileInfo { public static void main(String[] args) { File f = new File("hamlet.txt"); System.out.println("exists returns " + f.exists()); System.out.println("canRead returns " + f.canRead()); System.out.println("length returns " + f.length()); System.out.println("getAbsolutePath returns " + f.getAbsolutePath()); } } 7Using File Methods • Compile and run FileInfo.java • Output: exists returns true canRead returns true length returns 196197 getAbsolutePath returns /afs/unity.ncsu.edu/users/m/mlglatz/csc116/Lab13/hamlet.txt C:\CSC116\exercises\Lab13\hamlet.txt 8Reading files • Previously, we used Scanner objects to read input from the console window – Uses System.in as source of input – Need to prompt the user for information • We can also use Scanner objects to read from a file. – Uses a File object as source of input – Do NOT need to prompt for information • We need separate Scanners for reading from the console and reading from a File 9Creating Scanners for Files • Using a specific File object that may be used later File f = new File("data.txt"); Scanner input = new Scanner(f); • Using a File object that is just read once Scanner input = new Scanner(new File("mydata.txt")); 1 0 Scanner Methods Now we have access to the Scanner methods, such as: – next() – hasNext() – nextInt() – hasNextInt() – nextDouble() – hasNextDouble() 1 1 Using File Input • In the directory where you put hamlet.txt, create a file named FileInputTest.java with the following code: import java.util.*; import java.io.*; public class FileInputTest { public static void main(String[] args) { File f = new File("hamlet.txt"); Scanner input = new Scanner(f); String data = input.next(); System.out.println("data from file: " + data); } } • Try to compile your completed FileInputTest.java program. 1 2 Compiler Error The program fails to compile: FileInputTest.java:7: error: unreported exception FileNotFoundException; must be caught or declared to be thrown Scanner input = new Scanner(f); ^ 1 error 1 3 Exceptions Recall: Exceptions are errors that prevent a program from continuing normal execution calling charAt on a String and passing too large an index trying to read the wrong type of value from a Scanner trying to read a file that does not exist – Exceptions are “thrown” by the program – We can “catch” exceptions to handle them FileNotFoundException – Java can’t work if the file doesn’t exist in the system – Checked Exception (compiler ‘checks’ that we are aware of the possibility of this exception) – We must specify how our program will handle file I/O failures. – Can’t ignore this exception like you could an InputMismatchException (unchecked exception) – If program does not acknowledge or handle I/O exception: • Get unreported exception compile error 8 The throws clause • throws clause: declaration on a method's header that states that the method will not attempt to handle a particular type of exception. – Explicit acknowledgement that a statement in the method may cause java to throw the exception – Passing the exception on to the client of our method – Add @throws to method javadoc • Syntax: public static type name(params) throws type { throws – Example: public class ReadFile { main(String[]public static void args) FileNotFoundException{ 10 1 6 Correcting the Compiler Error • Add the following throws clause to your FileInputTest program: import java.util.*; import java.io.*; public class FileInputTest { public static void main(String[] args) throws FileNotFoundException { File f = new File("hamlet.txt"); Scanner in = new Scanner(f); String data = in.next(); System.out.println("data from file: " + data); } } • Compile and run your corrected FileInputTest.java program. 1 7 Token-Based Processing • Token-Based Processing: Processing input token by token – Recall: A token is a single element of input (e.g. one word, one number). With Scanner objects, tokens are separated by whitespace (spaces, tabs, newline characters). • Use while loops to process unknown contents of a file • Use for loops if bounds of the loop are known Input tokens token: A unit of user input, separated by whitespace. A Scanner splits a file's contents into tokens. If an input file contains the following: 23 3.14 "John Smith" The Scanner can interpret the tokens as the following types: Token 23 3.14 "John Smith" Type(s) int, double, String double, String String String 18 Copyright 2008 by Pearson Education 1 9 The Scanner and Files Consider a file numbers.txt that contains this text: 308.2 14.9 7.4 2.8 3.9 4.7 2.8 -15.4 A Scanner views all input as a stream of characters: 308.2\n 14.9 7.4 2.8\n\n3.9 4.7 -15.4\n 2.8\n ^ input cursor: The current position of the Scanner. 2 0 Input Cursor A Scanner always keeps track of its current location in a file using an input cursor The input cursor starts at the beginning of the File. Scanner scan = new Scanner(new File("numbers.txt")); 308.2 14.9 7.4\n2.8\n\n3.9 4.7 -15.4\n2.8 Consuming tokens consuming input: Reading input and advancing the cursor. Calling nextInt etc. moves the cursor past the current token. 308.2\n 14.9 7.4 2.8\n\n3.9 4.7 -15.4\n 2.8\n ^ ^ 21 Copyright 2008 by Pearso Education ^ double x = input.nextDouble(); // 308.2 308.2\n 14.9 7.4 2.8\n\n3.9 4.7 -15.4\n 2.8\n String s = input.next(); // "14.9" 308.2\n 14.9 7.4 2.8\n\n3.9 4.7 -15.4\n 2.8\n Note that the cursor is placed immediately after the token that has been processed (at the whitespace character). ^ String l = input.nextLine(); // " 7.4 2.8" 308.2\n 14.9 7.4 2.8\n\n3.9 4.7 -15.4\n 2.8\n String l = input.nextLine(); // "" 308.2\n 14.9 7.4 2.8\n\n3.9 4.7 -15.4\n 2.8\n ^ 2 2 Scanner: Forward Processing • Scanners are designed for file processing in a forward manner. – There is no support for reading the input backwards • There is NO way to reset the Scanner back to the beginning of input – Instead you will have to construct a new Scanner object, which would position the input cursor at the beginning of the file 2 3 Scanners should be closed • Every Scanner to an input file must be closed after you are done using it Scanner input = new Scanner(new File("filename")); ... input.close(); • If you do not close the Scanner and then try to rename or move or delete the file, it will not work. • Closing a Scanner to the Console Window is NOT really necessary, but it's a good habit. File input question Recall the input file numbers.txt: 308.2 14.9 7.4 2.8 3.9 4.7 -15.4 2.8 Write a program that reads the first 5 values from the file and prints them along with their sum. Sum = 337.2 24 Copyright 2008 by Pearson Education number = 308.2 number = 14.9 number = 7.4 number = 2.8 number = 3.9 25 Copyright 2008 by Pearson Education File input answer // Displays the first 5 numbers in the given // and displays their sum at the end. file, public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("numbers.txt")); double sum for (int i double = 0.0; = 1; i <= 5; i++) { next = input.nextDouble(); System.out.println("number = " + next); sum = sum + next; } input.close(); System.out.printf("Sum = %.1f\n", sum); } } import java.io.*; // for File import public java.util.*; class Echo { // for Scanner 2 6 Scanner Exceptions InputMismatchException You read the wrong type of token (e.g. read "hi" as int). NoSuchElementException You read past the end of the input. Finding and fixing these exceptions: Read the exception text for line numbers in your code (the first line that mentions your file; often near the bottom): Exception in thread "main" java.util.NoSuchElementException at java.util.Scanner.throwFor(Unknown Source) at java.util.Scanner.next(Unknown Source) at FileInputTest.main(FileInputTest.java:11) 2 7 Avoiding NoSuchElementException To detect the end of a file: Scanner input = new Scanner(new File("example.txt")); while (input.hasNext()) { String token = input.next(); System.out.println("token: " + token); } Team Exercise Given the following 3 lines of text (source: http://www.lipsum.com , Mr. John Slankas) in SampleText.txt: Lorem ipsum 10 dolor 25.5 sit amet, consectetur adipiscing elit. Donec 23 ut 76.6 0.25 5 orci ipsum. What values are returned from the Scanner method calls listed to the right: • assume they are executed sequentially, • assume we have created a Scanner s: Scanner s = new Scanner(new File("SampleText.txt")); If the value returned is a String, put double quotes around your answer (ex. "Lorem"), if it is not a String (integer, double, boolean), do not put quotes around the answer. Put the answers in a text file named FileScanner.txt 1.s.next(); 2.s.hasNextInt(); 3.s.hasNextDouble(); 4.s.next(); 5.s.hasNextDouble(); 6.s.hasNextInt(); 7.s.nextDouble(); 8.s.next(); 9.s.hasNextDouble(); 10.s.hasNextInt(); 11.s.next(); 12.s.nextLine(); 13.s.nextLine(); 14.s.next(); 15.s.nextInt(); 16.s.next(); 17.s.hasNextInt(); 18.s.nextDouble(); 19.s.hasNextInt(); 20.s.hasNextDouble(); 21.s.next(); 22.s.hasNextInt(); 23.s.nextDouble(); 24.s.nextLine(); 25.s.hasNext(); 26.s.next(); Reading an entire file 29 Copyright 2008 by Pearson Education Suppose we want our program to process the entire file. (It should work no matter how many values are in the file.) Sum = 329.3 • Can use while loops to process unknown contents of a file number = 308.2 number = 14.9 number = 7.4 number = 2.8 number = 3.9 number = 4.7 number = -15.4 number = 2.8 File input question 2 30 Copyright 2008 by Pearson Education Modify the Echo program to process the entire file: (It should work no matter how many values are in the file.) Sum = 329.3 number = 308.2 number = 14.9 number = 7.4 number = 2.8 number = 3.9 number = 4.7 number = -15.4 number = 2.8 File input answer 2 31 Copyright 2008 by Pearson Education // Displays each number in the given file, public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("numbers.txt")); double sum = 0.0; while (input.hasNextDouble()) { double next = input.nextDouble(); System.out.println("number = " + next); sum = sum + next; } input.close(); System.out.printf("Sum = %.1f\n", sum); } } // and displays their sum at the end. import import java.io.*; java.util.*; // for // for File Scanner public class Echo { In-class Exercise • Go to the moodle page and work on the CountTokens.java assignment. • Your CountTokens program should count all the tokens in the file hamlet.txt. • You must use a File Scanner to read the tokens from the file and after processing the entire file print the following line: There are ______ tokens in the file hamlet.txt. • Where the underlined space is replaced by the number of tokens computed by your program. • Don’t forget to import both the util and io java packages. • Don’t forget the throws clause • Don’t forget to close your Scanner. File input question 3 33 Copyright 2008 by Pearson Education Modify the Echo program to handle files that contain non- numeric tokens (by skipping them). For example, it should produce the same output as before when given this input file, numbers2.txt: 308.2 hello 14.9 7.4 bad stuff 2.8 3.9 4.7 oops -15.4 :-) 2.8 @#*($& File input answer 3 34 Copyright 2008 by Pearson Education // Displays each number in the given file, // and displays their sum at the end. public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("numbers2.txt")); double sum = 0.0; while (input.hasNext()) { if (input.hasNextDouble()) { double next = input.nextDouble(); System.out.println("number = " + next); sum = sum + next; } else { input.next(); // throw away the bad token } } input.close(); System.out.printf("Sum = %.1f\n", sum); } } import java.io.*; // for File import public java.util.*; class Echo2 { // for Scanner Election question 35 Copyright 2008 by Pearson Education Write a program that reads a file polls.txt of poll data. Format: State Clinton% Trump% ElectoralVotes Pollster The program should print how many electoral votes each candidate leads in, and who is leading overall in the polls. Clinton: 214 votes Trump: 257 votes CT 56 31 7 Oct U. of Connecticut NE 37 56 5 Sep Rasmussen AZ 41 49 10 Oct Northern Arizona U. Election answer } 36 Copyright 2008 by Pearson Education // Computes leader in presidential polls, based on input file such as: // AK 42 53 3 Oct Ivan Moore Research import java.io.*; import java.util.*; // for File // for Scanner public class Election { public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("polls.txt")); int clintonVotes = 0, trumpVotes = 0; while (input.hasNext()) { if (input.hasNextInt()) { int clinton = input.nextInt(); int trump = input.nextInt(); int eVotes = input.nextInt(); if (clinton > trump) { clintonVotes = clintonVotes + eVotes; } else if (trump > clinton) { trumpVotes = trumpVotes + eVotes; } } else { input.next(); // skip non-integer token } } input.close(); System.out.println("Clinton: " + clintonVotes + System.out.println("Trump: " + trumpVotes " votes"); + " votes");} Scanners as Parameters • When a Scanner is passed as a parameter to a method, the input cursor does not reset to the beginning of the File • Need to create a new Scanner if you want to go back and read the file again 14 Scanner as Parameter Example 14 import import java.io.*; java.util.*; public class ShowSum3 { public static void main(String[] args) throws FileNotFoundException { Scanner input = new Scanner(new File("numbers.dat")); processTokens(input, 2); processTokens(input, 3); processTokens(input, 2); input.close(); } double sum = 0.0; for (int i = 1; i < n; i++) { double next = input.nextDouble(); System.out.println ("number " + i + " = " + next); sum += next; } System.out.println("Sum = " + sum); System.out.println(); } } public static void processTokens(Scanner input, int n) { 308.2 14.9 7.4 2.8 3.9 4.7 -15.4 2.8 numbers.dat number 1 = 308.2 number 2 = 14.9 Sum = 323.09999999999997 number 1 = 7.4 number 2 = 2.8 number 3 = 3.9 Sum = 14.1 Output: number 1 = 4.7 number 2 = -15.4 Sum = -10.7 Getting Filename from User • Create a Scanner for console input – Prompt user for filename – Read in filename • Check that file exists. If not, re-prompt • Create a Scanner for file input 14 Getting Filename from User return Scanner 14 public static Scanner getInputScanner(Scanner console) throws FileNotFoundException { System.out.print("Enter a file name to process: "); File file = new File(console.next()); while (!file.exists()) { System.out.print("File doesn't exist. " + "Enter a file name to process: "); file = new File(console.next()); } Scanner fileScanner = new Scanner(file); return fileScanner; } Lab Exercise • Go to the moodle page and work on the ProcessFile.java assignment. • Finish the given ProcessFile.java program that prompts the user for a filename and reprompts if the file doesn’t exist. – You will process the data in the file; skipping any text or real (double) numbers. – You will print the max, min, sum, count, and average of the integers in the file. – You will want to create test files that contain integers, doubles, and Strings. • HINT: Use the Election example to help set up your loop to read in the integer data and skip the non-integers. • The min/max loops material from the Lecture 8 slides may be helpful.