Introduction
In this tutorial, we'll be reading a file into a String in Java. There are a few ways we can read the textual contents of a file.
Here's a list of all the classes and methods we'll go over:
Files.lines()
The Files
class contains static methods for working with files and directories. A useful method is lines()
which returns a stream of strings: Stream<String>
. From this stream, one can obtain lines contained in a file.
The method accepts a Path
to the file we'd like to read with an optional Charset
. We'll use try-with-resources
syntax to automate flushing and closing:
Path path = Paths.get("input.txt");
try (Stream<String> stream = Files.lines(path, StandardCharsets.UTF_8)) {
stream.forEach(System.out::println);
} catch (IOException ex) {
// Handle exception
}
Since the method returns a Stream
, we use its forEach()
method to iterate over the lines, with a method reference for brevity.
Instead of printing each line, a StringBuilder
can be used to append lines:
Path path = Paths.get("input.txt");
StringBuilder sb = new StringBuilder();
try (Stream<String> stream = Files.lines(path)) {
stream.forEach(s -> sb.append(s).append("\n"));
} catch (IOException ex) {
// Handle exception
}
String contents = sb.toString();
With StringBuilder
, the entire file can be represented in a single String
(the contents
variable above). Before performing such iterations, it's important to consider the length of the input file.
If the file is not too large, it's okay to put it in a String, though, if it's hundreds of megabytes in size, it's not so wise.
Files.readString()
Since Java 11, the Files
class introduced us to the readString()
method, which accepts a Path
to the file, as well as a Charset
.
Unlike Files.lines()
, it returns a String
directly, instead of a Stream
object:
Path path = Paths.get("input.txt");
String contents = null;
try {
contents = Files.readString(path, StandardCharsets.ISO_8859_1);
} catch (IOException ex) {
// Handle exception
}
Files.readAllBytes()
A more low-level approach to reading is the Files.readAllBytes()
method, which returns a byte[]
. It's up to the developer to use these bytes - convert them to a String, process them as they are, etc.
This method also accepts a Path
to the file we'd like to read:
Path path = Paths.get("input.txt");
byte[] bytes = null;
try {
bytes = Files.readAllBytes(path);
} catch (IOException ex) {
// Handle exception
}
Now, the bytes
array holds all the information from the input.txt
file. The easiest way to convert it into a String is to put them in a constructor with an optional Charset
:
String str = new String(bytes, StandardCharsets.UTF_8);
Note: Solutions like reading all bytes are only appropriate in circumstances where we're dealing with small file sizes. It's not performance-friendly and it doesn't make much sense to keep large files in the program's memory.
Scanner
Scanner
is a particularly useful class for reading content from streams. Since it works with abstract streams, it can also be used for reading strings. Scanner
works by breaking the input into tokens which are sequentially retrieved from the input stream.
Since we are working with strings, we would like to use methods that return strings. Scanner
has next()
and nextLine()
exactly for that. Both methods return objects of type String
. The former is used to read arbitrary strings, whereas the latter parses and returns entire lines.
If each line contains the right amount of data then nextLine()
is an ideal choice. If there is important information in the file that's broken down into smaller chunks but not necessarily lines (or the file contains, say, a single line) then next()
might be a better option.
Scanner
's constructor accepts many objects - Path
s, InputStream
s, File
s, etc. We'll use a File
:
File file = new File("input.txt");
Scanner sc = new Scanner(file);
while(sc.hasNext()) {
System.out.println(sc.next());
}
We're using a while
loop as long as the sc
has more elements. If we didn't check with hasNext()
, sc
would throw a NoSuchElementexception
if we try accessing an element after the last one.
The idea of using hasNext()
and next()
methods come from the Iterator
interface, as Scanner
internally implements it.
FileReader
The FileReader
is used to read files. It offers the read()
and read(char[])
methods, which return a single character and multiple characters respectively. Also, it accepts a File
or String
into the constructor.
FileReader.read(char[])
Let's open a file using FileReader
and read its contents:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
FileReader in = new FileReader("input.txt");
char[] chars = new char[256];
int n = in.read(chars, 0, chars.length);
String contents = new String(chars);
The read()
method accepts a sequence of characters (that we're storing the read characters in), the starting point and the ending point of what we'd like to read. Specifically, we've decided to read 256 characters at most. If input.txt
has more, we'll read only 256 characters. If it has less, the readable characters are returned.
The return value, stored inside integer n
can be used to check how many characters the method actually read. In case the end of the stream has been reached, the method returns -1
.
Since the method fills a char[]
, we can convert it into a String
. A similar result can be obtained by using String.valueOf(char[])
.
FileReader.read()
The read()
method, without a char[]
reads a single character at a time. We'll want to iterate through the contents and read each character ourselves:
FileReader in = new FileReader("input.txt");
StringBuilder sb = new StringBuilder();
while(in.read() != -1) {
sb.append(in.read());
}
String contents = sb.toString();
in.close();
Here, we check if the read character isn't -1
, which indicates that there are no characters left to read. If not, we append()
it to a StringBuilder
and finally, convert it to a String
.
Note: Both read()
and read(char[])
read bytes, convert them into characters, and return them one by one. This is inefficient and should be done with buffering when possible.
BufferedReader
BufferedReader
is an object designed to read text from a character-input stream. It is buffered, meaning that it uses an internal buffer for temporary storage. As we've seen in the previous section, "regular" Reader
s can sometimes be inefficient.
It's advised to wrap any potentially costly Reader
into a BufferedReader
to increase performance since buffering characters enables more efficient reading of the input text.
Let's instantiate a BufferedReader
:
BufferedReader in = new BufferedReader(new FileReader("input.txt"));
At this point, we have a buffered reader object ready to read contents from input.txt
. In this example, we'll be reading the file line-by-line, although BufferedReader
supports reading single characters individually and also multiple characters into an array.
Let's use this BufferedReader
instance to read a file and store its contents, line-by-line, into a String:
StringBuilder sb = new StringBuilder();
while(in.readLine != null) {
sb.append(in.readLine()).append("\n");
}
String contents = sb.toString();
in.close();
Once again, we are using StringBuilder
to collect all the lines. To separate each line, we append the null-terminator (\n
) between them. Finally, we close the stream.
Conclusion
In this article, we went over some common techniques for reading files into strings in Java. There are many options but most of them have a similar core principle: provide a path to the file, read the contents into a data structure (e.g. char[]
or a String); then perform some final processing to collect all file contents in an appropriate manner.
We've covered the File.lines()
method, the Files.readString()
method, the Files.readAllBytes()
method, as well as the Scanner
, FileReader
and BufferedReader
classes.