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.