Module 12, Part 1: File I/O#
In this first part of Module 12 we study how to perform input/output (I/O) operations on files.
Reading and Writing Data from/to Files#
The Java API provides several ways to read data from files, and to write data into files. In this section we explore one method to perform file I/O that is very similar to how we perform console I/O, by discussing:
how to create objects of the class ‘File’
how to read data from a File object
Creating File Objects#
The Java API includes a class named
java.io.File,
and an object of that class represents either a file or a directory. For
instance, if we plan to work on a file named test.txt in the current directory,
we can create a corresponding object of the class java.io.File by writing:
(try this on the Java shell!)
var file = new java.io.File("test.txt");
We can now call various methods of our new file — for instance,
file.exists() returns a boolean telling whether a file named test.txt
exists or not in the current directory.
Note
We will not focus on the methods of the class java.io.File, but they can be
very useful for many purposes: you can have a look at
the documentation
if you want to know more.
Reading Data from a File#
To read data from a file, we can create a java.util.Scanner object, similar to
the ones we typically use to
read inputs from the console.
The difference is that, instead of creating a Scanner object attached to
System.in, we create a Scanner object attached to the File object from
which we want to read data.
However, we need to be careful: creating a Scanner attached to a File
object may throw a java.io.FileNotFoundException if the file does not exist.
Such an exception is checked and
therefore we will need to decide how our code can handle it (as we did in
Example 69). This is illustrated in
Example 71 below.
Example 71 (Reading all lines in a file)
Suppose we have a file called blinkenlights.txt containing this
blinkenlights warning message:
ACHTUNG! ALLES LOOKENSPEEPERS!
Alles touristen und non-technischen looken peepers!
Das computermachine ist nicht fuer gefingerpoken und mittengrabben.
Ist easy schnappen der springenwerk, blowenfusen und poppencorken
mit spitzensparken. Ist nicht fuer gewerken bei das dumpkopfen.
Das rubbernecken sichtseeren keepen das cotten-pickenen hans in das
pockets muss; relaxen und watchen das blinkenlichten.
The program below reads all lines of this file and prints them on screen, by:
creating a
Fileobject for the fileblinkenlights.txt;creating a
Scannerobject attached to that file, inside atry-catchstatement that handles a possiblejava.io.FileNotFoundException;reading from the scanner as long as new lines are available, similarly to how we would read inputs from the console.
1class DisplayFile {
2 public static void main(String[] args) {
3 var filename = "blinkenlights.txt";
4 var file = new java.io.File(filename);
5
6 try {
7 var scanner = new java.util.Scanner(file); // May throw IOException
8 scanner.useLocale(java.util.Locale.ENGLISH);
9
10 System.out.println("** Contents of the file '" + filename + "' **");
11 while (scanner.hasNextLine()) {
12 var line = scanner.nextLine();
13 System.out.println(line);
14 }
15 scanner.close();
16 } catch (java.io.FileNotFoundException e) {
17 System.out.println("Error: " + e.getMessage());
18 }
19 }
20}
You can experiment with the program above, e.g. by running it, and then changing
the filename on line 3 with a non-existent file: you will see that an
exception will be thrown on line 7 and then caught on line 15.
Writing Data into a File#
To write data into a file, we can create an object of the class
java.io.PrintWriter
attached to the file we wish to write data into. So, if file is a
File object,
then we can create a PrintWriter object attached to it, by writing e.g.:
var printer = new java.io.PrintWriter(file);
Note that:
if the given
filedoes not already exist on disk, thePrintWriterconstructor above will create it. The file will be initially empty, and it will grow when data is written into it;if the given
filealready exists on disk, thePrintWriterconstructor will truncate it, i.e., turn it into an empty file; the file will grow when data is written into it. As a consequence, the original file contents will be lost;the
PrintWriterconstructor might throw ajava.io.FileNotFoundExceptionif the file cannot be created: such an exception is checked and therefore we will need to decide how our code handles it (as we did in Example 69).
Importantly, objects of the class java.io.PrintWriter provide methods that are
very similar to those offered by the object System.out that we use for console
output. (In fact, the class of the object
System.out
is
java.io.PrintStream,
and its constructor and methods are almost identical to the class
java.io.PrintWriter
we are using here for file I/O). Therefore, we can use the printer object
created above to call methods that have a name and a behaviour similar to what
we already know via System.out, such as:
printer.print("Hello")to write the stringHellointo the file to which theprinterobject is attached;printer.println("Hej")to write the stringHejfollowed by a line separator into the file to which theprinterobject is attached.
Important
When the printer object is not needed any more, we should close it by calling
printer.close().
The use of a PrintWriter object is illustrated in
Example 71 below.
Example 72 (Writing data into a file)
The program below writes all numbers from 0 to 99 (in a 10 \(\times\) 10
arrangement) in the file test.txt, by:
creating a
Fileobject for the filetest.txt;creating a
PrintWriterobject attached to that file, inside atry-catchstatement that handles a possiblejava.io.IOException;writing strings into the stream, using the methods
.print(...)and.println(...).
1class WriteFile {
2 public static void main(String[] args) {
3 var filename = "test.txt";
4 var file = new java.io.File(filename);
5
6 try {
7 var printer = new java.io.PrintWriter(file);
8
9 for (var i = 0; i < 10; i++) {
10 for (var j = 0; j < 10; j++) {
11 var padding = (i > 0) ? "" : "0"; // Initial '0' if needed
12 printer.print(padding + ((i * 10) + j) + " ");
13 }
14 printer.println(""); // Append line separator
15 }
16 printer.close();
17 } catch (java.io.IOException e) {
18 System.out.println("Error: " + e.getMessage());
19 }
20 }
21}
If you run this program, it will create a file called test.txt (if it does not
already exist) and its contents will look like:
00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99
Concluding Remarks#
You should now have an understanding of how to read and write data from/to files.
The Java API includes a large number of classes and methods for dealing with files, and you may use them when writing larger programs: see the References and Further Readings for some pointers.
References and Further Readings#
You are invited to read the following sections of the reference book:
Section 11.6 - “I/O Exceptions”
Section 5.5 - “Iterators” - subsection “Reading Text Files”
As mentioned above,
the class of the object
System.out
is
java.io.PrintStream,
which has constructors and methods that are almost identical to the class
java.io.PrintWriter
that we have used for writing data into a file.
Still, it is possible to use PrintStream instead of PrintWriter for writing
data into a file: the difference between the two classes is
quite subtle and is
important for some applications — but it is not crucial for the simple
programs addressed in this course, where the two classes can be used
interchangeably.
If you are curious, you can also explore the documentation of the following
classes, that contains useful information (e.g., how to create a PrintWriter
or PrintStream or a Scanner directly with a file name, without first
creating a File object):
Lab and Weekly Assessments#
During the lab you can try begin your work on the weekly assessments below.
Important
For each assessment, you can download the corresponding handout and submit your solution on DTU Autolab: https://autolab.compute.dtu.dk/courses/02312-E24.
For details on how to use Autolab and the assessment handouts, and how to submit your solutions, please read these instructions.
If you have troubles, you can get help from the teacher and TAs.
01 - Mini Grep#
The goal of this assessment is to write a program that works like a simplified version of the “grep” command-line utility.
Edit the file MiniGrep.java provided in the handout, and write a program that
takes two command-line arguments:
the first argument is a string to be searched;
the second argument is the name of a file.
First, the program must check whether it is being executed with exactly two arguments. If not, it must print and error and end immediately. For instance, if the program is executed as:
java MiniGrep.java aaa bbb ccc ddd
then the program must print the following message, and then end:
Error: expected 2 arguments, got 4 instead
If the program is being executed with exactly two arguments, then it must read each line of the file with the name specified as the second argument, and print each line that contains the string given as first argument. If the file does not exist, the program must print an error and end.
For instance, suppose that the program is executed as:
java MiniGrep.java Hello hello-world.txt
If the file hello-world.txt does not exist, the program must print the
following message, and then end:
Error: file hello-world.txt not found
Now, suppose instead that the file hello-world.txt exists, and contains the
following lines:
Hello, human!
This is a test
I just wanted to say: Hello, World!
The file ends here
Then, the program must print each line that contains the string Hello (which
was given as first command-line argument). Therefore, the program must print:
Hello, human!
I just wanted to say: Hello, World!
When you are done, submit the modified file MiniGrep.java on DTU Autolab.
Warning
The automatic grading on DTU Autolab includes some additional secret checks that test your submission with more command-line arguments and input files. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.
Hint
You might want to revise how to access the array of command-line arguments.
To read all lines in a file, you can follow Example 71.
To check whether a
Stringobjectstr1contains another stringstr2, you could write the string search code yourself, e.g. by adapting the solution to the assessment 09 - Sub-Array…Otherwise, you can call the method
str1.contains(str2)(provided by all objects of typeString): you can find more details here. Note that, according to the documentation, the method.contains(...)takes as argument any object of typeCharSequence— which is an interface defined in the Java API that is implemented by the classString(and this is why the method.contains(...)can take an object of typeStringas argument).
02 - Cow Say, Part 2#
This is a follow-up to the assessment 02 - Cow Say,
and the starting point is its solution, i.e. the file CowSay.java (you can
use either your own file, or the one provided by the teacher as a solution to
02 - Cow Say).
The purpose of this assessment is to extend the program CowSay.java so it can
optionally read its input from a file, and write its output into a file.
More in detail, the updated program must behave as follows:
If
CowSay.javais executed with no command-line arguments, then it must keep working as in 02 - Cow Say — i.e., it must read it input from the terminal, and then print its output on the terminal.If
CowSay.javais executed with one command-line argument, then it must read its input from the file specified by the command-line argument, and then print its output on the terminal. You can assume that the input file contains exactly one line of text. For instance, if the program is executed as:java CowSay.java joke1.txt
then the program must read the joke contained in the file
joke1.txt, and display on the terminal a cow telling that joke.If
CowSay.javais executed with two command-line arguments, then it must read its input from a file specified by the first command-line argument, and then write its output into a file specified by the second command-line argument. You can assume that the input file contains exactly one line of text; the program must not display any output on the terminal. For instance, if the program is executed as:java CowSay.java joke2.txt output.txt
then the program must read the joke contained in the file
joke2.txt, and write into the fileoutput.txtthe text representing a cow telling that joke.
When you are done, submit the modified file CowSay.java on DTU Autolab.
Hint
You may want to revise how to read the command-line arguments of a Java program.
Depending on how you structure your code, you may need to declare that the
mainmethod of the classCowSaymight throw ajava.io.IOException. Therefore, the method may look like:public static void main(String[] args) throws java.io.IOException { ... }
You can reuse and adapt most of the code of the solution to 02 - Cow Say (either yours or the teacher’s) by observing that its input/output operations use an object of type
Scannerto read text, and the objectSystem.out(which has typejava.io.PrintStream) to produce output. Therefore, you could adapt the code (and avoid duplication) by moving most of it into a utility method like:private static void readTextPrintCow(java.util.Scanner scanner, java.io.PrintStream out)
which reads a line from the given
scannerobject and produces output using the givenoutobject. When this method is called, the argumentscannermight be attached either toSystem.inor to an input file; instead, the argumentoutmight be either the objectSystem.outor a PrintStream object attached to an output file…
03 - Maze, Part 2#
This is a follow-up to the assessment 02 - Maze,
and the starting point is its solution, i.e. the file Maze.java (you can
use either your own file, or the one provided by the teacher as a solution to
02 - Maze).
Your task is to add the capability of loading a maze from a file, and saving a maze in a file. The contents of a file containing a maze look as follows:
6,5
### X
# #
# #
## #
## ##
# #
The first line of the file contains the number of rows and columns of the maze
separated by a comma , (in this example, 6 rows and 5 columns), and the
remaining lines contain each row of the maze, with one character per cell (the
meaning of each character is described in 02 - Maze).
Note
When a maze with n columns is saved in a file, each row of the maze must
contain exacty n characters. For instance, in the file contents shown above,
each row of the maze contains 5 characters (some of them are spaces: you can see
them more clearly by selecting the text).
Edit the file Maze.java and implement the following static methods:
public static char[][] load(String fname) throws java.io.IOException
This static method loads a maze from a text file called
fname. You can assume that the contents of the input file respect the description above.public static void save(char[][] maze, String fname) throws java.io.IOException
This static method saves the given
mazein a file calledfname. If the file already exists, this static method must overwrite it.
When you are done, submit the modified file Maze.java on DTU Autolab.
Hint
To extract the number of rows and columns of the maze from the input file, you might split the first input line (which is a
String) around the separator",". For more details, see Example 35.To retrieve the character at position
nof a string objectstr, you can usestr.charAt(n). For more details, see Some Useful String Methods.To convert the content of a
Stringobject into aninteger value, you can use the static methodInteger.parseInt(...)(provided by the Java API). For instance,Integer.parseInt("42")returns theintvalue 42.
Warning
The automatic grading on DTU Autolab includes some additional secret checks that test your submission by loading and saving other mazes. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.