Home CPSC 220

Files & IO

Introduction

Programs can read input and write output to the screen as we have seen. Many programs also do input and output with files as well.

Using files for input is nice because the user does not have to type each line by hand. By saving output to a file, we can keep it after the program is finished executing.

Today we will also learn ways for formatting output which work for files and also screen output as well.


Using Input Files

To read input in Java, we have to open the file we wish to read with a FileInputStream object, which takes the name of the file as an argument:


FileInputStream file = new FileInputStream("input.txt");

This requires importing from "java.io":


import java.io.*;

This line of code can produce a "FileNotFoundException" which Java actually enforces that you catch:


try {
    // open the file
    FileInputStream file = new FileInputStream("input.txt");

} catch (FileNotFoundException e) {
    // the file was not found!
    System.out.println("File input.txt could not be opened!");
}

Then, we can create a Scanner and pass the FileInputStream to it as a parameter:


Scanner in = new Scanner(file);

Then, we could use the "next..." functions of the Scanner just like when reading from the user. The following program reads a string and number from an input file called "input.txt":


import java.io.*;
import java.util.Scanner;

public class Input {

    public static void main(String args[]) {
        // try to read input    
        try {
            // open the file
            FileInputStream file = new FileInputStream("input.txt");
            
            // create a scanner for it
            Scanner in = new Scanner(file);
            
            // read something from it
            String name = in.next();
            int value = in.nextInt();
            
            // print the results
            System.out.println("Read " + name + " and " + value);
            
        } catch (FileNotFoundException e) {
            // the file was not found!
            System.out.println("File input.txt could not be opened!");
        }
    }
}

For this to work, the file must be in the same place as we run our program from.


Detecting EOF

Often times, we want to read the entire contents of a file until the end. This can be done by using the "hasNext" functions from the Scanner object. For example:


in.hasNext()

will return true if there is another string of input to read, and false if the end of the file has been reached. The following example reads numbers from an input file until the end:


import java.io.*;
import java.util.Scanner;

public class RealAll {

    public static void main(String args[]) {
        // try to read input    
        try {
            // open the file
            FileInputStream file = new FileInputStream("numbers.txt");
            
            // create a scanner for it
            Scanner in = new Scanner(file);
            
            // read all of the numbers from a file
            while (in.hasNextInt()) {
                int value = in.nextInt();
                System.out.println("Read a " + value);
            }
            
        } catch (FileNotFoundException e) {
            // the file was not found!
            System.out.println("File numbers.txt could not be opened!");
        }
    }
}

Closing Files

It is good practice to close a file when you are done with it. To do this, simply call the "close" function of the Scanner:


in.clode();

In most cases, there is no noticeable effect of closing a file, but it is good practice to always do so.


Using Output Files

We can also write output to a file instead of to the terminal window. To do this, we must create a PrintWriter object, and pass it the name of the file to use:


PrintWriter file = new PrintWriter("output.txt");

This also can throw a FileNotFoundException which you must put in a try/catch block.

Once the file is opened, you can write to it using the "print" and "println" functions just like with System.out.

The following example writes a list of perfect squares into an output file:


import java.io.*;

public class Output {

    public static void main(String args[]) {
        try {
            // open the output file
            PrintWriter file = new PrintWriter("output.txt");

            // write some numbers to the file
            for (int i = 1; i <= 10; i++) {
                file.println(i * i);
            }

            file.close();
        } catch (FileNotFoundException e) {
            System.out.println("Error, could not open output.txt");
        }
    }
}

You have to be careful doing file output. If you write to an existing file, it will over-write it!


Output Formatting

The "print" and "println" functions allow us to print strings or numeric data to the screen or a file, but don't provide much in the way of formatting. If we want to print data in a tabular format, or with a certain number of decimal points, we can use the "printf" function.

The printf function takes as the first parameter a "format string" which describes what to print. It then takes any number of parameters supplying the data to be printed.

This example prints a string and an integer:


public class Printf1 {
    public static void main(String args[]) {
        String name  = "Bob";
        int value = 42;
        System.out.printf("name is %s, and value is %d.\n", name, value);
    }
}

When run, this program outputs:


name is Bob, and value is 42.

The %s indicates a String will be passed, and the %d indicates an integer will be passed. Then we pass a String and an int whose values will be used in place of the markers. (If we pass the wrong types, we will get an "IllegalFormatConversionException").

Below is a list of format specifiers:

SpecifierMeaning
%dA (decimal) integer.
%fA floating-point number.
%eA floating-point number in scientific notation.
%sA string.
%cA character.
%%An actual percent sign.

The numerical ones (and percent signs) are demonstrated in this program:


public class Printf2 {
    public static void main(String args[]) {
        double value = 340.1234;
        System.out.printf("%%d = %d\n", (int)value);
        System.out.printf("%%f = %f\n", value);
        System.out.printf("%%e = %e\n", value);
    }
}

Which produces this output:


%d = 340
%f = 340.123400
%e = 3.401234e+02

We can also use printf to specify the number of digits to print for numbers. In order to do this, place ".X" between the '%' and the 'd' or 'f' where X is the number of digits you'd like. For example "%.2f" specifies that the value should be printed as a floating point number with 2 decimal digits:


public class Printf3 {
    public static void main(String args[]) {
        double value = 340.1234;
        System.out.printf("%.2f\n", value);
    }
}
outputs:

340.12

We can also use printf to specify the width of the fields we are printing. To do this, place the desired width between the '%' and the character specifying the type. The following program demonstrates this:


public class Printf4 {
    public static void main(String args[]) {
        String names [] = new String [] {"Alice", "Bob", "Claire", "Don"};
        int ages [] = new int [] {34, 52, 7, 101};
        
        for (int i = 0; i < names.length; i++) {
            System.out.printf("%10s is %5d years old.\n", names[i], ages[i]);
        }
    }
}
which outputs:

     Alice is    34 years old.
       Bob is    52 years old.
    Claire is     7 years old.
       Don is   101 years old.

We can also specify left-justification by placing a '-' before the number, such as "%-10s" to left-justify the names above.

printf is a very versatile function, which has counterparts in nearly every programming language.

Copyright © 2018 Ian Finlayson | Licensed under a Creative Commons Attribution 4.0 International License.