To read a file into a String in Java, we call Files.readString(path), which reads the whole file with the UTF-8 charset and either returns its text or throws an IOException. The method came in Java 11. On Java 7 and 8, new String(Files.readAllBytes(path), StandardCharsets.UTF_8) gives the same result.
We read a file to a string when the code needs the whole text at once, such as an email template or a JSON file with the expected response in a unit test.
The following example reads a small file shopping-list.txt with two lines, apple=5 and banana=3, in several ways. The comment on each line shows the resulting String, with \n for a line break.
Path path = Path.of("shopping-list.txt");
String content = Files.readString(path); // "apple=5\nbanana=3\n" (Java 11+)
String latin = Files.readString(path, StandardCharsets.ISO_8859_1); // "apple=5\nbanana=3\n" (decoded as ISO-8859-1)
String fromBytes = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); // "apple=5\nbanana=3\n" (Java 7+)
String fromLines = String.join("\n", Files.readAllLines(path)); // "apple=5\nbanana=3" (last line break dropped)
String commons = FileUtils.readFileToString(path.toFile(), StandardCharsets.UTF_8); // "apple=5\nbanana=3\n" (Apache Commons IO)
String guava = MoreFiles.asCharSource(path, StandardCharsets.UTF_8).read(); // "apple=5\nbanana=3\n" (Guava)
String missing = Files.readString(Path.of("missing.txt")); // NoSuchFileException: missing.txt
Optional<String> safe = readIfExists(Path.of("missing.txt")); // Optional.empty (helper from section 8)
Notice that the line-based methods drop the line terminators, so fromLines is not equal to content. Every method throws an IOException for a missing file, so real code either catches it or uses a helper such as readIfExists().
Next, we go through each method with its charset and line-ending behavior, starting with Files.readString(). After that, we compare the methods in one table and handle read errors and large files.
1. Using Files.readString() – Java 11
The method Files.readString() came in Java 11. It reads all bytes of the file and decodes them to characters with UTF-8, so one line of code gives us the whole text.
- The method keeps the line separators as they are in the file, including the last one.
- It closes the file when the read is complete or when an exception is thrown, so we don’t need a try-with-resources block.
- It throws an IOException when the file is missing or when the file contains bytes that are not valid UTF-8.
- It throws an OutOfMemoryError when the file is extremely large, for example larger than 2 GB.
Path path = Path.of("shopping-list.txt");
String content = Files.readString(path); // "apple=5\nbanana=3\n"
String latin = Files.readString(path, StandardCharsets.ISO_8859_1); // second overload with a charset
Path nested = Path.of("data", "shopping-list.txt"); // data/shopping-list.txt
The second overload takes a Charset, which we pass when the file was written in another encoding, such as ISO-8859-1 from an old Windows tool. A relative path such as “shopping-list.txt” is resolved against the current working directory, which is the folder from which we started the java command.
A typical use case is an email template. A shop app keeps the welcome email in welcome-email.txt with the text Hello {name}, welcome back!. The app reads the template once and replaces the placeholder for each customer.
Path template = Path.of("welcome-email.txt");
String email = Files.readString(template).replace("{name}", "Lokesh"); // "Hello Lokesh, welcome back!"
To write a String back to a file, we call Files.writeString().
2. Using Files.lines() and Files.readAllLines()
The method Files.lines() reads the file line by line into a Stream of String values. The stream reads the lines lazily, which means that it reads a line only when a terminal operation such as collect() asks for it.
- The method decodes the bytes with UTF-8, or with the charset that we pass as the second argument.
- The returned stream keeps the file open. The file is closed only when we close the stream, so we always call Files.lines() in a try-with-resources block.
- If another program changes the file while the stream reads it, the result is undefined.
Each line in the stream comes without its line terminator. When we join the lines with Collectors.joining(“\n”), the text has no line break at the end, so it differs from the file content by one character.
String joined;
try (Stream<String> lines = Files.lines(path)) {
joined = lines.collect(Collectors.joining("\n")); // "apple=5\nbanana=3"
}
boolean same = joined.equals(Files.readString(path)); // false (no line break at the end)
String withEnd;
try (Stream<String> lines = Files.lines(path)) {
withEnd = lines.collect(Collectors.joining("\n", "", "\n")); // "apple=5\nbanana=3\n"
}
The method Files.readAllLines() (Java 7) reads all lines into a List at once and closes the file, so it needs no try-with-resources block. We join the list with String.join(), which also drops the last line break.
List<String> allLines = Files.readAllLines(path); // [apple=5, banana=3]
String fromList = String.join("\n", allLines); // "apple=5\nbanana=3"
We use the line-based methods when we work with lines anyway, for example to skip comment lines or to read the file into an ArrayList. When we only need the text, Files.readString() is shorter and keeps the file content unchanged.
3. Using Files.readAllBytes() – Java 7
The method Files.readAllBytes() reads all bytes of a file into a byte[] and closes the file when the read is complete or when an exception is thrown. We pass the bytes to the String constructor together with the charset of the file. Do not use readAllBytes() for large files, because it throws an OutOfMemoryError when the file is larger than 2 GB.
byte[] bytes = Files.readAllBytes(path); // 17 bytes
String fromBytes = new String(bytes, StandardCharsets.UTF_8); // "apple=5\nbanana=3\n"
We always pass the charset to the constructor. The constructor new String(bytes) without a charset uses the default charset of the JVM, which is UTF-8 since Java 18 (JEP 400). On Java 17 and earlier, the default comes from the operating system, so the same code can give different text on Windows and Linux.
The String constructor also handles invalid bytes differently from Files.readString(). It replaces each invalid byte sequence with the replacement character U+FFFD and throws no exception, as we will see in section 8.
4. Using BufferedReader
The class BufferedReader reads the file in blocks of characters, and its readLine() method returns one line at a time, or null at the end of the file. We create the reader with Files.newBufferedReader(path), which decodes with UTF-8, and we close it with a try-with-resources block.
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
}
String fromReader = builder.toString(); // "apple=5\nbanana=3\n"
Notice that the loop appends “\n” after every line, including the last one. For a file without a line break at the end, the result has one extra “\n”, and Windows line endings (\r\n) become \n.
A BufferedReader is the right choice when we read a file line by line and build the text from the lines we need. To get a String from a Reader we already have, for example a FileReader, we call reader.transferTo(writer) with a StringWriter and take the text from writer.toString(). Since Java 11, new FileReader(file, StandardCharsets.UTF_8) accepts a charset.
5. Commons IO’s FileUtils
The Apache Commons IO library has the class FileUtils, whose method readFileToString() reads a whole file into a String in a single statement. The method takes a java.io.File, so we convert a Path with toFile().
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.22.0</version>
</dependency>
File file = new File("shopping-list.txt");
String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8); // "apple=5\nbanana=3\n"
We always pass the charset. The overload readFileToString(File) without a charset is deprecated, because it uses the default charset of the JVM. Like the String constructor, FileUtils replaces invalid bytes with U+FFFD instead of throwing an exception.
6. Guava’s Files and MoreFiles
Guava reads a file through a CharSource, an object that knows how to open the file and decode it. The class com.google.common.io.Files creates the CharSource from a File, and the class MoreFiles creates it from a Path. The method read() returns the whole text and closes the file.
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.7.2-jre</version>
</dependency>
String fromPath = MoreFiles.asCharSource(path, StandardCharsets.UTF_8).read(); // "apple=5\nbanana=3\n"
String fromFile = com.google.common.io.Files.asCharSource(file, StandardCharsets.UTF_8).read(); // "apple=5\nbanana=3\n"
We write the full name com.google.common.io.Files because the class has the same simple name as java.nio.file.Files. Older examples call Files.toString(file, charset) or pass Guava’s Charsets.UTF_8. Both are deprecated in Guava 33, so we use asCharSource() with StandardCharsets.UTF_8 from the JDK. Adding Guava only to read a file is not worth it, because Files.readString() does the same job.
7. Choosing a Method
All methods give the same text for a UTF-8 file that ends with a line break. They differ in how they treat line terminators and bytes that are not valid in the charset, and some of them need a newer Java version or a library.
| Method | Since | Line terminators | Invalid bytes |
|---|---|---|---|
| Files.readString(path) | Java 11 | kept as in the file | MalformedInputException |
| new String(Files.readAllBytes(path), UTF_8) | Java 7 | kept as in the file | replaced with U+FFFD |
| Files.lines(path) + Collectors.joining(“\n”) | Java 8 | replaced with \n, last one dropped | UncheckedIOException |
| Files.readAllLines(path) + String.join(“\n”, …) | Java 7 | replaced with \n, last one dropped | MalformedInputException |
| BufferedReader.readLine() loop | Java 1.1 | replaced with \n, always one at the end | MalformedInputException |
| FileUtils.readFileToString(file, UTF_8) | Commons IO | kept as in the file | replaced with U+FFFD |
| MoreFiles.asCharSource(path, UTF_8).read() | Guava | kept as in the file | replaced with U+FFFD |
On Java 11 and later, we use Files.readString(). On Java 7 and 8, we use new String(Files.readAllBytes(path), StandardCharsets.UTF_8). We use Files.lines() or a BufferedReader only when we process the file line by line.
The line terminator column matters when a file comes from Windows. For a file with \r\n line endings, Files.readString() returns “apple=5\r\nbanana=3\r\n”. When the code compares the text or splits it into lines, we normalize the line endings, or we call String.lines(), which splits on \n, \r and \r\n.
Path windows = Path.of("windows-list.txt"); // written with \r\n line endings
String windowsText = Files.readString(windows); // "apple=5\r\nbanana=3\r\n"
String normalized = windowsText.replace("\r\n", "\n"); // "apple=5\nbanana=3\n"
List<String> lines = windowsText.lines().toList(); // [apple=5, banana=3]
8. Handling Missing Files and Wrong Encodings
Reading a file can fail for reasons outside our code, so every method we saw throws an IOException, which is a checked exception. The subclass of the exception tells us the cause.
| Situation | Exception thrown by Files.readString() |
|---|---|
| The file does not exist | NoSuchFileException: missing.txt |
| The file contains bytes that are not valid UTF-8 | MalformedInputException: Input length = 1 |
| The path points to a directory | IOException: Is a directory (Linux) |
| The process has no read permission | AccessDeniedException |
For example, an app reads an optional file settings-override.txt at startup. When the file is missing, the app uses its defaults, but any other read error is a real problem that must stop the app. The helper readIfExists() returns an Optional that is empty only for a missing file and passes every other IOException to the caller.
public static Optional<String> readIfExists(Path path) throws IOException {
try {
return Optional.of(Files.readString(path));
} catch (NoSuchFileException e) {
return Optional.empty();
}
}
Optional<String> found = readIfExists(Path.of("shopping-list.txt")); // Optional[apple=5\nbanana=3\n]
Optional<String> notFound = readIfExists(Path.of("missing.txt")); // Optional.empty
We catch NoSuchFileException inside the method instead of calling Files.exists() first, because another process can delete the file between the check and the read.
The second common error is a wrong charset. Say a file cafe.txt contains the word cafe with an accent on the e, saved by an old tool in ISO-8859-1. In ISO-8859-1, the accented e is the single byte 0xE9, which is not a valid UTF-8 sequence. The method Files.readString() reads with UTF-8, so it throws a MalformedInputException, and the String constructor returns the text with a replacement character.
Path cafe = Path.of("cafe.txt"); // the word cafe with an accented e (byte 0xE9), saved as ISO-8859-1
String strict = Files.readString(cafe); // MalformedInputException: Input length = 1
String replaced = new String(Files.readAllBytes(cafe), StandardCharsets.UTF_8); // "caf" + U+FFFD (wrong character, no exception)
String correct = Files.readString(cafe, StandardCharsets.ISO_8859_1); // "caf" + accented e (correct)
When we know the encoding of the file, we pass it as the charset, and we don’t hide a MalformedInputException by switching to a method that replaces bad bytes. The exception tells us that the text would be wrong.
9. Reading Large Files
The methods Files.readString() and Files.readAllBytes() load the whole file into memory, so the file size limits them. A String or a byte[] holds at most about 2 GB, and a large file also needs that much free heap. The method readString() is not intended for reading very large files).
For example, a nightly job scans a 5 GB access log for failed logins. The job does not need the whole log as one String, so it streams the lines and keeps only the count. The stream from Files.lines() keeps only a small block of the file in memory at a time.
long bananaLines;
try (Stream<String> lines = Files.lines(path)) {
bananaLines = lines.filter(line -> line.startsWith("banana")).count(); // 1
}
For more ways to handle big files, read reading large files efficiently and reading a file line by line.
10. Read File to String FAQs
10.1. How Do I Read a File From the Resources Folder Into a String?
We read it as a classpath resource, not as a Path, because inside a JAR file the resource is not a file on disk. The method getResourceAsStream() returns an InputStream, or null when the resource does not exist, and InputStream.readAllBytes() (Java 9) reads all its bytes.
try (InputStream in = ReadFileToString.class.getResourceAsStream("/shopping-list.txt")) {
if (in == null) {
throw new FileNotFoundException("shopping-list.txt not found on the classpath");
}
String content = new String(in.readAllBytes(), StandardCharsets.UTF_8); // "apple=5\nbanana=3\n"
}
The leading / means the path starts at the root of the classpath, which is src/main/resources in a Maven project. More options are in reading a file from the resources folder and reading a file from the classpath.
10.2. How Do I Fix a MalformedInputException When Reading a File?
We pass the charset in which the file was saved. The exception, with the message Input length = 1, means that the file contains bytes that are not valid in the charset used for reading, which is UTF-8 by default for Files.readString(), Files.readAllLines() and Files.newBufferedReader(). Files from old Windows tools often use windows-1252 or ISO-8859-1, so Files.readString(path, Charset.forName(“windows-1252”)) reads them, as we saw in section 8.
10.3. How Do I Read a File to a String in Java 8?
We call new String(Files.readAllBytes(path), StandardCharsets.UTF_8). The methods Path.of() and Files.readString() need Java 11, so on Java 8 we create the path with Paths.get(“shopping-list.txt”).
10.4. What Is the Difference Between Files.readString() and Files.readAllBytes()?
The method Files.readString() returns the decoded text, whereas Files.readAllBytes() returns the raw bytes of the file. We use readString() for text files, because it decodes with a known charset and throws a MalformedInputException for invalid bytes. We use readAllBytes() for binary files such as images or PDF files, and for text on Java 7 and 8, where we decode the bytes with new String(bytes, StandardCharsets.UTF_8).
11. Conclusion
On Java 11 and later, Files.readString(path) reads a whole file into a String with UTF-8 and keeps the file content unchanged. When the file uses another encoding, we pass the charset as the second argument. On Java 7 and 8, new String(Files.readAllBytes(path), StandardCharsets.UTF_8) does the same job.
The line-based methods Files.lines(), Files.readAllLines() and BufferedReader.readLine() drop or change the line terminators, so we use them when we work with lines. Commons IO and Guava give the same result as the JDK methods but add a dependency.
A missing file throws a NoSuchFileException, and a wrong charset throws a MalformedInputException, so we catch them where the app can react. For files larger than a few hundred megabytes, we stream the lines instead of reading the whole file into one String.
12. References
- Files JavaDoc (Java 25)
- String JavaDoc (Java 25)
- MalformedInputException JavaDoc (Java 25)
- JEP 400: UTF-8 by Default
- FileUtils JavaDoc (Apache Commons IO)
- MoreFiles JavaDoc (Guava)
Happy Learning !!