Can I easily convert InputStream to BufferedReader using Guava?
I'm looking for something like:
InputStream inputStream = ...;
BufferedReader br = Streams.newBufferedReader(inputStream);
I can open files using the Files.newReader(File file, Charset charset). That's cool and I want to do the same using the InputStream.
UPDATE:
Using CharStreams.newReaderSupplier seems to verbose for me. Correct me if I'm wrong, but in order to easily convert InputStream to BufferedReader using Guava I have to do something like that:
final InputStream inputStream = new FileInputStream("/etc/fstab");
Reader bufferedReader = new BufferedReader(CharStreams.newReaderSupplier(new InputSupplier<InputStream>(){
public InputStream getInput() throws IOException {
return inputStream;
}
}, Charset.defaultCharset()).getInput());
Of course I can create helper do sth like:
return new BufferedReader(new InputStreamReader(inputStream));
However I think that such helper should be offered by Guava IO. I can do such trick for File instance. Why cannot I for InputStream?
// Guava can do this
Reader r = Files.newReader(new File("foo"), charset);
// but cannot do this
Reader r = SomeGuavaUtil.newReader(inputStream, charset);
Correct me If I'm wrong but it seems to me like lack in the API.
No, there isn't anything quite like that in Guava. CharStreams is the general class for working with Readers and Writers and it has a method
InputSupplier<InputStreamReader> newReaderSupplier(
InputSupplier<? extends InputStream> in, Charset charset)
which could be useful with any kind of supplier of InputStreams.
Obviously, you can just write new BufferedReader(new InputStreamReader(in, charset)) or wrap that in your own factory method as well.
Edit:
Yes, you wouldn't want to use the InputSupplier version when you already have an InputStream. It's sort of like how it's a bad idea to make an Iterable that can actually only work once, such as one that wraps an existing Iterator or Enumeration or some such. In general, using InputSupplier requires thinking about how you do I/O a little different, such as thinking of a File as something that can act as a supplier of FileInputStreams. I've used InputSuppliers that wrap whole requests to a server and return the response content as an InputStream, enabling me to use Guava utilities to copy that to a file, etc.
In any case, I'm not entirely sure why CharStreams doesn't have a method to create a Reader from an InputStream other than perhaps they didn't feel it was needed. You may want to file an issue requesting this.
Related
Is there any way to convert an OutputStream into an InputStream?
So the following would work
InputStream convertOStoIS(OutputStream os) {
}
I do not want to use any libraries, I read that there are some who are able to accomplish this with bytecode manipulation.
Edit
I want to be able to intersect a sink, to analyze the data or redirect the output. I want to place another OutputStream under the on given by some function and redirect the data into another input stream.
The related topics had a ByteArrayOutputStream or a PipedStream which is not the case in my question.
Related:
How to convert OutputStream to InputStream?
Most efficient way to create InputStream from OutputStream
Use a java.io.FilterOutputStream to wrap the existing OutputStream. By overriding the write() method you can intercept output and do whatever you want with it, either send it somewhere else, modify it, or discard it completely.
As to your second question, you cannot change the sink of an OutputStream after the fact, i.e. cause previously written data to "move" somewhere else, but using a FilterOutputStream you can intercept and redirect any data written after you wrap the original `OutputStream.
To answer my own question, yes you can build a redirect like this:
class OutInInputRedirect {
public final transient InputStream is;
public final transient OutputStream os;
public OutInInputRedirect() throws IOException {
this(1024);
}
public OutInInputRedirect(int size) throws IOException {
PipedInputStream is = new PipedInputStream(size);
PipedOutputStream os = new PipedOutputStream(is);
this.is = is;
this.os = os;
}
}
Just use the OutputStream as an replacement and the InputStream in those places you need, be awere that the closing of the OutputStream also closes the InputStream!
It is quite easy and works as expected. Either way you cannot change an already connected stream (without reflection).
I have the following use case:
read from service InputStream
go through the InputStream and replace some stuff, result is stored in OutputStream
now I need to go on working with an InputStream created from the OutputStream
This is the code I use right now:
InputStream resourceStream = service.getStream();
ByteArrayOutputStream output = new ByteArrayOutputStream();
replace(resourceStream, output, ...);
resourceStream.close();
resourceStream = new ByteArrayInputStream(out.toByteArray());
A lot of shifting and converting streams, so I was wondering if there is a cleaner solution. Maybe some OutputStream which can be used as InputStream, or an OutputStream which contains an InputStream where the content is written to.
No matter how good your idea of having a single object to write and read data from, the implementations of InputStream and OutputStream have been made as "Classes" and not "Interfaces".
Also, due to the fact that, in Java, a single subclass cannot extend multiple Super Classes, the dream of having both Input and Output stream operations in a single class remains just a dream.
That said, the only other option left for programmers would be to create a class that has both InputStream and OutputStream exposed to its clients. Something similar to what as java.net.Socket does. It exposes a getInputStream and a getOutputStream which at a logical level reads from and writes to the same "socket".
So, you can do something like this:
public class IOStreamWrapper {
byte[] streamData;
public InputStream getInputStream() {
// return an inputstream that reads from streamData[]
}
public OutputStream getOutputStream() {
// return an outputstream that writes to streamData[]
}
}
References:
Java InputStream
Java OutputStream
Hope this helps!
If I were to create an InputStreamReader with the following code,
new InputStreamReader(anInputStream, "UTF-8")
I would have to catch UnsupportedEncodingException, which is reasonable. I can avoid this by using
new InputStreamReader(anInputStream, StandardCharsets.UTF_8)
which doesn't throw UnsupportedEncodingException as the charset is already known to be valid. All good so far.
Now enter its counterpart, the PrintWriter:
new PrintWriter("filename", StandardCharsets.UTF_8)
doesn't compile because the PrintWriter constructor doesn't take a Charset argument. I can do this:
new PrintWriter("filename", StandardCharsets.UTF_8.name())
but then I can't avoid having to catch UnsupportedEncodingException, even though the charset name has just come from a valid charset.
The StandardCharsets utility class was added later on in Java's lifetime, and when Sun added it, they also added an overload to the InputStreamReader constructor. Why did they add an overload to InputStreamReader but not PrintWriter?
Is there another class I can use instead, which takes a charset instead of a charset name?
The counterpart to InputStreamReader is not PrintWriter.
Use OutputStreamWriter instead.
If you want to use PrintWriter, it's possible to use PrintWriter(new OutputStreamWriter(anOutputStream, StandardCharsets.UTF_8));
The counterpart of java.io.InputStreamReader is java.io.OutputStreamWriter, not java.io.PrintWriter.
That said, you can create the PrintWriter safely like this:
Reader reader = new InputStreamReader(anyOutputStream, StandardCharsets.UTF_8);
Writer writer = new OutputStreamWriter(anyInputStream, StandardCharsets.UTF_8);
PrintWriter printWriter = new PrintWriter(writer);
but then I can't avoid having to catch UnsupportedEncodingException, even though the charset name has just come from a valid charset.
Which makes sense, right? Since it's still a String.
As suggested by Stewart, using the java.io.OutputStreamWriter would be the way to go.
new PrintWriter(new OutputStreamWriter(anOutputStream, StandardCharsets.UTF_8), isAutoFlush)
I have a file that I've been reading into a List via the following method:
List<String> doc = java.nio.file.Files.readAllLines(new File("/path/to/src/resources/citylist.csv").toPath(), StandardCharsets.UTF_8);
Is there any nice (single-line) Java 7/8/nio2 way to pull off the same feat with a file that's inside an executable Jar (and presumably, has to be read with an InputStream)? Perhaps a way to open an InputStream via the classloader, then somehow coerce/transform/wrap it into a Path object? Or some new subclass of InputStream or Reader that contains an equivalent to File.readAllLines(...)?
I know I could do it the traditional way in a half page of code, or via some external library... but before I do, I want to make sure that recent releases of Java can't already do it "out of the box".
An InputStream represents a stream of bytes. Those bytes don't necessarily form (text) content that can be read line by line.
If you know that the InputStream can be interpreted as text, you can wrap it in a InputStreamReader and use BufferedReader#lines() to consume it line by line.
try (InputStream resource = Example.class.getResourceAsStream("resource")) {
List<String> doc =
new BufferedReader(new InputStreamReader(resource,
StandardCharsets.UTF_8)).lines().collect(Collectors.toList());
}
You can use Apache Commons IOUtils#readLines:
List<String> doc = IOUtils.readLines(inputStream, StandardCharsets.UTF_8);
An OutputStream obj can be connected into a PrintWriter obj directly, e.g.,
//either is OK
new PrintWriter(socket.getOutputStream());
new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
But in the case of an InputStream obj, it must be connected to a BufferedReader obj through an InputStreamReader obj, that is,
new BufferedReader(new InputStreamReader(socket.getInputStream())); //OK
new BufferedReader(socket.getInputStream()); //doesnt work
Is there any reason for this inconsistency of API design?
There isn't any inconsistency... you should be comparing BufferedReader and BufferedWriter. They exist to wrap other Readers and Writers respectively.
The basic reason for that is that different types of Readers and Writers may have different ways of being initialized and different ways of functioning, not necessarily wrapping an InputStream or OutputStream at all. In your example of a BufferedReader wrapping an InputStreamReader, InputStreamReader can (and generally should) be initialized with both an InputStream and a Charset. Should BufferedReader have an overload for that, when its only job is to provide buffering?
Java introduced Reader and Writer hierarchy (java 1.1 I think) when Input and output stream classes were already in use. Therefore using a bridge pattern they allow you to have stream classes passed into reader classes.
Further for writer also PritnerWriter is directly the bridge class which is equivalent to InputStreamReader. You will see the same thing for BufferedWriter too
For more info read up http://www.codeguru.com/java/tij/tij0114.shtml
The BufferedReader is probably just decorating the InputReader being passed in. It makes no sense for a BufferedReader to accept a class it can't decorate, like an InputStream.