Files and reading data
A considerable amount of software is in one way or another based on handling data. Software created for playing music handles music files and those created for the purpose of image manipulation handle image files. Applications that run on the internet and mobile devices, such as Facebook, WhatsApp, and Telegram, handle user information that is stored in file-based databases. What these all have in common is that they read and manipulate data in one way or another. Also, the data being handled is ultimately stored in some format in one or more files.
Reading From the Keyboard
We've been using the Scanner
-class since the beginning of this course to read user input. The block in which data is read has been a while-true loop where the reading ends at a specific input.
Scanner scanner = new Scanner(System.in);
while (true) {
String line = scanner.nextLine();
if (line.equals("end")) {
break;
}
// add the read line to a list for later
// handling or handle the line immediately
}
In the example above, we pass system input (System.in
) as a parameter to the constructor of the Scanner-class. In text-based user interfaces, the input of the user is directed into the input stream one line at a time, which means that the information is sent to be handled every time the user enters a new line.
The user input is read in string form. If we wanted to handle the input as integers, for instance, we'd have to convert it to another form. An example program has been provided below - it reads input from the user until the user inputs "end". As long as the user input is not "end" the inputs are handled as integers — in this case, the number is simply printed.
Scanner scanner = new Scanner(System.in);
while (true) {
String row = scanner.nextLine();
if (row.equals("end")) {
break;
}
int number = Integer.valueOf(row);
System.out.println(row);
}
Files and the Filesystem
Files are collections of data that live in computers. These files can contain, among other things, text, images, music, or any combination of these. The file format determines the content of the file as well as the program required to read the file. For example, PDF files are read with a program suited for reading PDF files, and music files are read with a program suited for reading music files. Each of these programs is made by humans, and the creators of these programs — i.e., programmers — also specify the file format as part of the work.
Computers have several different programs for browsing files. These programs are specific to the operating system. All programs used for browsing files make use of the filesystem of the computer in one way or another.
Our development environment provides us with the ability to browse the files of a project. In NetBeans you can take a look at all the files attached to a project by selecting the Files
tab, which is found in the same place as the Projects
tab. If the tab cannot be be found, it can be opened from the Window
menu. Clicking the project to open it will reveal all its files.
Reading From a File
Reading a file is done using the Scanner-class. When we want to read a file using the Scanner-class, we give the path for the file we want to read as a parameter to the constructor of the class. The path to the file can be acquired using Java's Paths.get
command, which is given the file's name in string format as a parameter: Paths.get("filename.extension")
.
When the Scanner
-object that reads the file has been created, the file can be read using a while-loop. The reading proceeds until all the lines of the file have been read, i.e., until the scanner finds no more lines to read. Reading a file may result in an error, and it's for this reason that the process requires separate blocks - one for the try
, and another to catch
potential errors. We'll be returning to the topic of error handling later.
// first
import java.util.Scanner;
import java.nio.file.Paths;
// in the program:
// we create a scanner for reading the file
try (Scanner scanner = new Scanner(Paths.get("file.txt"))) {
// we read the file until all lines have been read
while (scanner.hasNextLine()) {
// we read one line
String row = scanner.nextLine();
// we print the line that we read
System.out.println(row);
}
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
A file is read from the project root by default ( when new Scanner(Paths.get("file.txt"))
is called), i.e., the folder that contains the folder src
and the file pom.xml
(and possibly other files as well). The contents of this folder can the inspected using the Files
-tab in NetBeans.
In the example below, we read all the lines of the file "file.txt", which are then added to an ArrayList.
ArrayList<String> lines = new ArrayList<>();
// we create a scanner for reading the file
try (Scanner scanner = new Scanner(Paths.get("file.txt"))) {
// we read all the lines of the file
while (scanner.hasNextLine()) {
lines.add(scanner.nextLine());
}
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
// we print the total number of lines
System.out.println("Total lines: " + lines.size());
Reading Data of a Specific Format From a File
The world is full of data that are related to other data — these form collections. For example, personal information can include a name, date of birth and a phone number. Address information, on the other hand, can include a country, city, street address, postal number and so on.
Data is often stored in files using a specific format. One such format that's already familiar to us is comma-separated values (CSV) format, i.e., data separated by commas.
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Enter name and age separated by a comma: ");
String line = scanner.nextLine();
if (line.equals("")) {
break;
}
String[] parts = line.split(",");
String name = parts[0];
int age = Integer.valueOf(parts[1]);
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
The program works as follows:
Enter name and age separated by a comma: virpi,19 Name: virpi Age: 19 Enter name and age separated by a comma: jenna,21 Name: jenna Age: 21 Enter name and age separated by a comma: ada,20 Name: ada Age: 20
Reading the same data from a file called records.txt
would look like so:
try (Scanner scanner = new Scanner(Paths.get("records.txt"))) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] parts = line.split(",");
String name = parts[0];
int age = Integer.valueOf(parts[1]);
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
}
Reading Objects From a File
Creating objects from data that is read from a file is straightforward. Let's assume that we have a class called Person
, as well as the data from before.
Reading objects can be done like so:
ArrayList<Person> people = new ArrayList<>();
try (Scanner scanner = new Scanner(Paths.get("records.txt"))) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] parts = line.split(",");
String name = parts[0];
int age = Integer.valueOf(parts[1]);
people.add(new Person(name, age));
}
}
System.out.println("Total amount of people read: " + people.size());
Reading objects from a file is a clear responsibility in and of itself, and should for that reason be isolated into a method. This is what we'll be doing in the next exercise.
Remember to check your points from the ball on the bottom-right corner of the material!