Files
Table of contents
- Creating files and folders
- Paths
- Reading from a text file
- Writing to a text file
- Tokenization of strings
- Full I/O control - pointers
- Serialization
Creating files and folders
To create a file, we use the java.io.File
module. First, we have to create an object of the File
class, check if a file with a given name already exists, and if not, create it. We can check its properties, like the date of its last modifications, with the self-explanatory methods listed below. To prevent errors, we have to assign throws IOException
to the methods in which we create files.
import java.io.File;
import java.io.IOException;
import java.util.Date;
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("file.txt");
if (!file.exists())
file.createNewFile();
System.out.println(file.canWrite());
System.out.println(file.canExecute());
System.out.println(file.canRead());
System.out.println(file.isHidden());
System.out.println(file.isFile()); // if it is a file and not a folder
System.out.println(new Date(file.lastModified()));
System.out.println(file.length());
file.delete();
}
}
We create folders using the same class but with the mkdir()
method. If we want to make many folders inside each other, we use the mkdirs()
method. File.separator
substitutes the separator occurring in paths (in Windows it is \
). It adjusts automatically to the system it is run on.
File folder = new File("folder");
folder.mkdir();
System.out.println(folder.isDirectory());
File folders = new File("folder2" + File.separator + "folder3");
folders.mkdirs();
System.out.println(System.getProperty("user.dir")); // the path to this file
File file = new File("folder" + File.separator + "file.txt");
if (!file.exists())
file.createNewFile();
Paths
The displayPaths()
method will list all paths in a given location. It will run through a list of files and folders in the given catalog, ignore the files, and execute itself for each encountered folder separately (so that the folders inside of these folders are also included).
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("folder" + File.separator + "file.txt");
if (!file.exists())
file.createNewFile();
System.out.println(file.getPath()); // this path is not absolute
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath()); // it is better because it uses File.separator and not the \ sign
displayPaths(new File(System.getProperty("user.dir")));
}
static void displayPaths(File pathName) {
String[] fileAndFolderNames = pathName.list();
System.out.println(pathName.getPath());
for (int i = 0; i < fileAndFolderNames.length; i++) {
File f = new File(pathName.getPath(), fileAndFolderNames[i]);
if (f.isFile())
System.out.println(f.getPath());
if (f.isDirectory())
displayPaths(new File(f.getPath()));
}
}
}
Reading from a text file
To read from a file, we have to create an instance of a BufferedReader
class, which uses a FileReader
object (with our file's name assigned). The BufferedReader
speeds up the whole reading process done by FileReader
. When we finish the operations on a file, we have to close it (using the close()
method). The loop in the example below will read all of the lines from the file and display them. It will stop when there are no more lines to read.
import java.io.*; // all
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader x = new BufferedReader(new FileReader("file.txt"));
// System.out.println(reader.read()); - one character
// System.out.println(reader.readLine()); - one line
String content = "";
while ((content = x.readLine()) != null)
System.out.println(content);
x.close();
}
}
When we don't know the number of arguments we will give to a method, we can use something like in the example below. It may come in handy while reading from a file because we don't know how many lines it has.
static void method(Object... a) {
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);
}
In the example below, it is shown how we can read only one particular line from a file.
int lineNumber = 2;
BufferedReader x = new BufferedReader(new FileReader("file.txt"));
for (int i = 0; i < lineNumber - 1; i++)
x.readLine();
String line = x.readLine();
System.out.println(line);
The simplest way to read all lines from a file to a list goes as follows: List
.
Writing to a text file
To write to a file, we have to create an instance of a PrintWriter
class, which uses a FileWriter
object with a file name given. A file can be opened in two modes for saving: append mode, which adds new content at the end of the file, and write mode, which overrides the existing content with new data. If the file doesn’t exist, it creates a new one (the append mode does too). It is specified using the second argument of FileWriter
(true
- append, false
- write). The PrintWriter
object simplifies the file writing process and allows for easier handling of formatted output compared to using FileWriter
alone. The printf()
method is a version of the print()
method that supports formatting (println()
is also a variation of print()
that adds an enter). We can incorporate variables into it through symbols with the %
sign. Each symbol represents a specific data type (%d
- integer, %.2f
- float with two decimal numbers, %s
- string), e.g., System.out.printf("%.2f + %.2f", num1, num2);
. The variables are substituted in order with the arguments of this method that stand after the main string.
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
PrintWriter x = new PrintWriter(new FileWriter("file.txt", true));
x.println(1234);
x.write("abcd");
x.println();
x.printf("He weights %d kg and is %.2f %s tall.", 50, 60.5, "cm");
x.close();
}
}
Tokenization of strings
Tokenization means breaking a string into smaller parts using delimiters (characters treated as separators). It can be used to write a few values in the same line of a file.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Example[] example = new Example[3];
example[0] = new Example();
example[1] = new Example(0, 10);
example[2] = new Example(0, 20);
PrintWriter x = new PrintWriter(new FileWriter("file.txt"));
Example.writeToFile(example, x);
x.close();
BufferedReader y = new BufferedReader(new FileReader("file.txt"));
Example[] example2 = Example.readFromFile(y);
for (int i = 0; i < example2.length; i++)
System.out.println(example2[i]);
y.close();
}
}
class Example {
int x, y;
Example() {
this.x = 0;
this.y = 0;
}
Example(int x, int y) {
this(); // calling the constructor without arguments ("this(x, y)" would call the two-parameter constructor)
this.x = x;
this.y = y;
}
static void writeToFile(Example[] example, PrintWriter z) {
z.println(example.length);
for (int i = 0; i < example.length; i++)
z.println(example[i].x + "|" + example[i].y);
}
static Example[] readFromFile(BufferedReader z) throws IOException {
int t = Integer.parseInt(z.readLine());
Example[] example = new Example[t];
for (int i = 0; i < t; i++) {
String line = z.readLine();
StringTokenizer tokens = new StringTokenizer(line, "|");
int x = Integer.parseInt(tokens.nextToken());
int y = Integer.parseInt(tokens.nextToken());
example[i] = new Example(x, y);
}
return example;
}
public String toString() {return this.x + " " + this.y;}
}
Full I/O control - pointers
If, instead of the previously mentioned classes, we create an instance of the RandomAccessFile
object, we will have more control over what we do, thanks to the pointer inside the file. This pointer is always used to perform operations on the file without our intervention, but by using the seek()
method, we can control it directly. In the beginning, as the getFilePointer()
method shows, the pointer is located at the end of the file because it just finished writing to it. The number shown by this method is the number of bits the file has (in this case, 24 because there are two double variables and one string with four chars, which gives us 8*2+4*2). The example below uses the class from the previous example.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Example[] example = new Example[3];
example[0] = new Example();
example[1] = new Example(0, 10);
example[2] = new Example(0, 20);
RandomAccessFile raf = new RandomAccessFile("file.txt", "rw"); // r - read, w - write, rw - read & write
raf.writeDouble(123.45);
raf.writeDouble(12.34);
raf.writeChars("ABCD");
System.out.println(raf.getFilePointer());
raf.seek(18);
System.out.println(raf.readChar());
raf.close();
}
}
Serialization
Serialization means converting the state of an object (its data) into a format that can be easily stored and reused. We can serialize an instance of a class. The class that is to be serialized has to implement the Serializable
interface. The example below uses the class from the previous example but with added implements Serializable
. We can later import the object from the file in another program.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Example[] example = new Example[3];
example[0] = new Example();
example[1] = new Example(0, 10);
example[2] = new Example(0, 20);
ObjectOutputStream x = new ObjectOutputStream(new FileOutputStream("file.txt"));
x.writeObject(example);
x.close();
ObjectInputStream y = new ObjectInputStream(new FileInputStream("file.txt"));
Example[] z = (Example[])y.readObject();
for (int i = 0; i < z.length; i++)
System.out.println(z[i].toString());
y.close();
}
}