If you ask StackOverflow or ChatGPT, how to convert an InputStream to a String in Java, you get archaic constructs with buffered readers and tedious loops. In modern Java, you achieve this task and similar ones with a single line of code. Apparently, these one-liners are not common knowledge, so I list a few. Maybe it'll get me a mention in the the JetBrains Java Annotated Monthly newsletter.
JetBrains has a nice monthly newsletter with Java tidbits. Mostly, these links are really useful. But the April 2023 edition included A Complete Guide on How to Convert InputStream to String In Java with the caption: “Eden Allen gives step-by-step instructions on understanding Inputstream [sic] and converting it to String using BufferedReader.”

Huh? Why do you need to use BufferedReader for this in 2023? And why is the author's web page listed as https://cheapsslweb.com? Maybe ChatGPT had a hand in this? When I asked OpenAI Chat “How to Convert InputStream to String In Java”, it gave me a five-step procedure with exactly the same code as in the article:
public static String convertInputStreamToString(InputStream inputStream) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
bufferedReader.close();
return stringBuilder.toString();
}
Most of the time, when I want to read a string, it's in a file. Java 11, released in 2018, has methods
public static String readString(Path path) // UTF-8 public static String readString(Path path, Charset cs)
in the java.nio.file.Files class. No need for buffered readers or loops.
What if I read the string from a URL? Of course, I can use HttpClient, also a part of Java 11:
HttpClient client = HttpClient.newBuilder().build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://horstmann.com/index.html"))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
Ok, maybe that's a bit heavy-handed. I could just call
InputStream in = new URL("https://horstmann.com/index.html").openStream();
And then I need to turn an InputStream into a string. But not with a loop. In 2017, Java 9 was released, and InputStream gained a method readAllBytes:
var result = new String(in.readAllBytes(), StandardCharsets.UTF_8);
And, thanks to JEP 400, as of Java 18, you don't need StandardCharsets.UTF_8 anymore. (Assuming, of course, that the contents is actually in UTF-8. But hey, it's 2023.)
To read text from a file, use one of these methods:
String contents = Files.readString(path); List<String> lines = Files.readAllLines(path); Stream<String> lineStream = Files.lines(path);
If you need to read numbers, words, or characters instead of lines, then use a scanner:
var in = new Scanner(path);
in.useDelimiter("\\PL+");
Stream<String> words = in.tokens();
If you already have your output in a string or a collection of lines, use:
Files.writeString(path, contents);
Files.write(path, lines); // lines is any Iterable<String>
With binary data, there are
byte[] bytes = Files.readAllBytes(path); Files.write(path, bytes);
The “Complete Guide” article proposes a second method for reading a string from an input stream:
public static String convertInputStreamToString(InputStream inputStream) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
return result.toString("UTF-8");
}
This is also unnecessarily complex. Since Java 9, InputStream has a method transferTo, and the loop can be replaced with:
in.transferTo(result);
It is unlikely that you want to use these classes in 2023. If you write your own lexer and read input a character at a time, a BufferedReader still makes sense. But that's a very specialized task.
For most text processing tasks, it is simpler to read the entire text into a string or a list of strings, with one of the convenience methods that you just saw. If you need to break up each line into fields, call line.split(delimiterRegex) or construct a new Scanner(line) for each line.
I cannot imagine ever needing a BufferedWriter. For hand-crafted output, you are better off with a PrintWriter since it has methods for formatting. Weirdly enough, you still need a File, not a Path, to construct one:
var writer = new PrintWriter(path.toFile());
BufferedReader or BufferedWriter.
Comments powered by Talkyard.