Using Files as Input/Output in Java 5.0 Applications The goal of this module is to present enough information about files to allow you to write applications in Java that fetch their input from a file instead of from the keyboard. Files are useful in testing programs because they save the programmer from re-entering (typing) data into the program every time the program is run. Writing a program to read from a file instead of from the keyboard simply involves adding a few additional pieces to your program. In order to use file as input or output, you need to import the pre-defined Java io (input output) package and the Scanner Class. Example: import java.io.* ; import java.util.scanner; You will need also an object from the class Scanner and another object from the class FileInputStream when reading from a file. Example: Scanner infile = new Scanner(new FileInputStream("data.txt")); Before you can read from a file, you must first open the file. Opening a file sets up a communication conduit between your program and the file. Opening a file does not actually read the data in the file, it just sets up a conduit so that the program may read data from the file by using that conduit. For example, Scanner infile = new Scanner(new FileInputStream("data.txt")); where data.txt is the name of the file that we are using as input. Picture the class FileInputStream making a pipe (one-way going from the file towards the program) between the program and the file. In the example above, the end of the pipe anchored in the program would have a label infile, and at the other end the label data.txt. Checking the results of the open request There are some conditions under which the statement above can fail (such as the file does not exist, or the file is present, but your program does not have read-access to it, or the file contains the wrong type of data). In Java, it is very important to make sure that the input file has been successfully opened. If the file cannot be opened then Java will automatically generate an error (this is called throwing an exception ). The class Scanner generates this exception. Fortunately for us, this is done very nicely by using a try-catch block. Example: File Input and Output in Java 1
try FileInputStream textfile = new FileInputStream ( data.txt ); Scanner infile = new Scanner (textfile); System.out.println( File data.txt has been opened. ); catch (FileNotFoundException fnfe) System.out.println( File data.txt was not found! ); Reading from a file (File Input) When your program reads from a file, it always keeps track of what it has read previously so when you ask it to read again, it always reads the next un-read item. This is a very important concept to remember. There are many ways to read data from a file. Reading from a file mostly depends on how the data has been stored in the file (one item per line, more than one item per line) and the type of that data. For simplicity, we are going to restrict our discussion to files where each line contains only one piece of data. Examples: File A 20 33 124-5 -10 File B Hello Goodbye How What Where Reading from files shaped like the ones above is very easy in Java. All we have to do is to read one line at the time from the file and then do the necessary type conversions using the Wrapper Classes (if they are needed). Reading only one line at the time is done by the nextline() method of the class Scanner. To do the type conversions we use the Wrapper classes (Integer, Double, etc.) as we have done in the past. Example: String oneline = infile.nextline(); This statement will read the first line of the file associated with the object infile and store its contents into the String object oneline. File Input and Output in Java 2
Then we need to extract the value from the String object and store it into a local variable of the proper type. So, let s assume that the file data.txt looks like File A (a file of integers) above. int number = Integer.parseInt(oneLine); This statement will extract the integer value stored in the String oneline and it will assign it to the integer variable number. As you can see, it is very important to know in advance the type of data that the file has. Otherwise, we could end up using the wrong wrapper class. Because of this, it is important that we catch this type of error (throw an exception). The class FileReader takes car of this as follows: try FileInputStream textfile = new FileInputStream ( data.txt ); Scanner infile = new Scanner (textfile); System.out.println( File data.txt has been opened. ); String oneline = infile.nextline(); int number = Integer.parseInt(oneLine); catch (FileNotFoundException fnfe) System.out.println( File data.txt was not found! ); catch (IOException ioe) System.out.println( Error while reading the file. Possible wrong type! ); Obviously, the file has more than one line. So, how do we read more than one line? How do we know how many lines to read? These are very important questions! One solution is to physically count the number of lines in the file. This is not a good approach because we couldn t write an application generic enough to handle file of integers (independent of the number of lines). The correct approach is to use a loop with the correct stopping conditions. So, let s use a while loop to read one line at the time until there are no more lines left in the file. Example: #1 int number; #2 while(infile.hasnextline()) File Input and Output in Java 3
#3 String oneline = infile.nextline(); #4 number = Integer.parseInt(oneLine); #5 System.out.println( Number read is + number); Line #1 declares a local variable called number. This variable is used to staore one integer value at the time. Line #2 is the while loop we use to read all the lines in the file. The loop guard (infile.hasnextline()) checks to see if there is line in the file to be read. The method hasnextline() returns either true (there is a line) or false (there are no lines). If there is no data to be read from the file then the condition is false and the loop will not iterate. Otherwise, there is some data stored in the file and the condition is true. Once inside the loop (meaning that there is data to be read from the file) we do the following. Line #3 reads the first line from the input file. Line #4 extracts the integer value read from the file and stores it in the variable number. Line #5 prints the value read and stored. Line #6 attempts to read the next line in the file. After this, the loop guard in Line #2 is checked again. So, when does the loop stop? Assume that the last line has been read by Line #3. Line #4 and Line #5 get executed as expected. After this, the loop guard in Line #2 is checked again, but this time there is no line to be read (we are at the end of the file). Thus, the guard condition becomes false (there are no more lines to be read). Hence, the loop stops! Sample application The application below reads integer numbers from a file, sums them, and displays the total at the end. import java.util.scanner; import java.io.*; File Input and Output in Java 4
public class FileInput public static void main (String[] args) try Scanner infile = new Scanner(new FileInputStream("numbers.txt")); int sum = 0; int number = 0; while(infile.hasnextline()) String oneline = infile.nextline(); number = Integer.parseInt(oneline); System.out.println(number); sum = sum + number; infile.close(); System.out.println("The sum of the numbers is " + sum); catch(filenotfoundexception fnfe) System.out.println("File not found!!"); //end of main //end of class Using a string for the filename In the previous example, in order to change what file the program reads from, you must edit the program and then re-compile. That's pretty clumsy. Is there an easier way? The answer is yes. We will use an object of the class String to hold the file name that the user wants the program to process. The code below will ask the user what file to read, and then proceed to process that file. Scanner keyboard = new Scanner(System.in); System.out.print("Enter the name of the file: "); String filename = keyboard.nextline(); Scanner infile = new Scanner(new FileInputStream(fileName)); File Input and Output in Java 5
First, we instantiate keyboard, an object of the class Scanner, which is going to accept input from the console (i.e. typed by the user). Then, we prompt the user for the name of the file to be read. Finally, we use the method nextline() to read the sequence of characters and instantiate infile, an object of the class Scanner, which is going to fetch the input data from the file chosen be the user. Other type of input files So far, we have discussed files, which contain only one item per line. How about files which contain several pieces of data in a line? In this case, we would still read one line at the time from the file. Then we would use the StringTokenizer class to fetch each item (or token) from the line read. This technique and similar ones are discussed in a more advanced course. Caution If you are using Eclipse to compile and execute your application you must physically place the input file in the same folder where your project is located. Otherwise, Eclipse will not be able to locate your input file. Similarly, output files can be found in the same folder where your project is located. File Input and Output in Java 6