This question already has an answer here:
java reader vs. stream
(1 answer)
Closed 7 years ago.
I am going over input output section and there are so much classes and interfaces that I am confused a little bit.
As documentation says InputStream is a byte based stream whereas Reader is character-based stream. But as I understood correctly the only difference between them is that Reader reads two bytes per time instead of one byte at a time as InputStream does.
Therefore I don't understand is there any difference:
Using InputStream
byte[] bytes = new byte[2];
InputStream in = new FileInputStream("input.txt");
int bytesRead = in.read(bytes);
while(bytesRead != -1) {
doSomethingWithData(data);
bytesRead = inputstream.read(data);
}
Using Reader
Reader reader = new InputStreamReader(new FileInputStream("input.txt"));
int data = reader.read();
while (data != -1) {
doSomethingWithData(data);
data = reader.read();
}
I am really confusing about these aspects. Explain please in details. Thanks.
You can use a Reader to read text data. And it's supports some character encoding like - ISO, UTF-8. If you want to read a text file with some encoding then you can use Readers like - BufferedReader, StringReader etc.
And you can use Stream (InputStream, OutputStream) to manipulate binary data. For example you want to read a image file then you can use FileInputStream and when you want to save it to disk then you can use FileOutputStream.
Related
I have a servlet which receives via POST method a large JSON string (> 10,000 characters).
If i read the content of the request like this:
try(Reader reader = new InputStreamReader(new BufferedInputStream(request.getInputStream()), StandardCharsets.UTF_8))
{
char[] buffer = new char[request.getContentLength()];
reader.read(buffer);
System.out.println(new String(buffer));
}
i donĀ“t get the entire content! The buffer size is correct. But the length of the created string is not.
But if i do it like this:
try(BufferedInputStream input = new BufferedInputStream(request.getInputStream()))
{
byte[] buffer = new byte[request.getContentLength()];
input.read(buffer);
System.out.println(new String(buffer, StandardCharsets.UTF_8));
}
it works perfectly.
So where am i wrong in the first case?
The way you are using InputStreamReader is not really the intended way. A call to read is not guaranteed to read any specific number of bytes (it depends on the stream you are reading from), which is why the return value of this method is the number of bytes that were read. You would need to keep reading from the stream and buffering until it indicates it has reached the end (it will return -1 as the number of bytes that were read). Some good examples of how to do this can be found here: Convert InputStream to byte array in Java
But since you want this as character data, you should probably use request.getReader() instead. A good example of how to do this can be found here: Retrieving JSON Object Literal from HttpServletRequest
Can someone explain to me why can I use FileInputStream or FileReader for a BufferedReader? What's the difference? And at the same time what is the advantage of a Scanner over a BufferedReader? I was reading that it helps by tokenizing, but what does that mean?
try {
//Simple reading of bytes
FileInputStream fileInputStream = new FileInputStream("path to file");
byte[] arr = new byte[1024];
int actualBytesRead = fileInputStream.read(arr, 0, arr.length);
//Can read characters and lines now
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
String lineRead = bufferedReader.readLine();
char [] charArrr = new char[1024];
int actulCharsRead = bufferedReader.read(charArrr, 0, charArrr.length);
//File reader allows reading of characters from a file
FileReader fileReader = new FileReader("path to file");
actulCharsRead = fileReader.read(charArrr, 0, charArrr.length);
//It is a good idea to wrap a bufferedReader around a fileReader
BufferedReader betterFileReader = new BufferedReader(new FileReader(""));
lineRead = betterFileReader.readLine();
actulCharsRead = betterFileReader.read(charArrr, 0, charArrr.length);
//allows reading int, long, short, byte, line etc. Scanner tends to be very slow
Scanner scanner = new Scanner("path to file");
//can also give inputStream as source
scanner = new Scanner(System.in);
long valueRead = scanner.nextLong();
//might wanna check out javadoc for more info
} catch (IOException e) {
e.printStackTrace();
}
Dexter's answer is already useful, but some extra explanation might still help:
In genereal:
An InputStream only provides access to byte data from a source.
A Reader can be wrapped around a stream and adds proper text encoding, so you can now read chars.
A BufferedReader can be wrapped around a Reader to buffer operations, so instead of 1 byte per call, it reads a bunch at once, thereby reducing system calls and improving performance in most cases.
For files:
A FileInputStream is the most basic way to read data from files.
If you do not want to handle text encoding on your own, you can wrap it into a InputStreamReader, which can be wrapped into a BufferedReader.
Alternatively, you can use a FilerReader, which should basically do the same thing as FileInputStream + InputStreamReader.
Now if you do not want to just read arbitrary text, but specific data types (int, long, double,...) or regular expressions, Scanner is quite useful. But as mentioned, it will add some overhead for building those expressions, so only use it when needed.
Introduced in Java 8 is Files.lines. This supports sufficient simple file manipulation to relieve at least some Perl envy :-)
Files.lines(Paths.get("input.txt"))
.filter(line -> line.startsWith("ERROR:"))
.map(String::toUpperCase).forEach(System.out::println);
How can I convert InputStreamReader to InputStream? I have an InputStream which contains some string and byte data and I want to parse it. So I wrap my InputStream to BufferedReader. Then I read 3 lines from it. After that I want to get the rest of data(bytes) as is. But if I try to get it nothing happens.
Code snippet:
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String endOfData = br.readLine();
String contentDisposition = br.readLine();
String contentType = br.readLine();
file = new File(filename);
if(file.exists()) file.delete();
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
byte[] data = new byte[8192];
int len = 0;
while (-1 != (len = is.read(data)) )
{
fos.write(data, 0, len);
Log.e("len", len+"");
}
fos.flush();
fos.close();
is.close();
The file is empty. If I don't wrap InputStream it works fine, but I need to read 3 lines and remove it.
Thanks.
If you want to mix text and byte data together, you should use OutputStream.writeUTF to write out those 3 lines, this way one single InputStream will be able to retrieve all the data that you need.
Take a look at commons-io's ReaderInputStream: it is a little heavy handed, but you can wrap the BufferedReader with that and read it as an input stream again.
It's pretty hard to mix byte and character input correctly, especially once you start throwing buffered readers / streams into the mix. I'd suggest that you either pick one and stick with it (converting your bytes to strings as necessary; care with the encoding!) or wrap the entire thing in a ZipOutputStream so you can have multiple logical "files" with different contents.
How can I convert DataInput to DataInputStream in java?
I need to know the size of the DataInput.
Since a stream, by definition, really has no begining or end and thus no fool proof way of knowing how much is available, you just have to read from the stream in fixed sized chunks. It almost sounds like you'd be better off with plain old .read() rather than readFully():
DataInputStream dis = new DataInputStream(...);
byte[] buf = new byte[1024];
int lastRead = 0;
do {
lastRead = dis.read(buf);
//do something with 'buf' here
} while (lastRead > 0);
You'll encounter difficulty when you want know how many bytes to be read. Simplest solution is to cast it to a ByteArrayInputStream and use it's available() method to get to know how many bytes are available for reading.
Following example worked for me
DataInput in = (...);
ByteArrayInputStream bis = (ByteArrayInputStream) in;
byte[] buffer = new byte[bis.available()];
in.readFully(buffer);
//use buffer as your wish
I have this piece of code which I'm hoping will be able to tell me how much data I have downloaded (and soon put it in a progress bar), and then parse the results through my Sax Parser. If I comment out basically everything above the //xr.parse(new InputSource(request.getInputStream())); line and swap the xr.parse's over, it works fine. But at the moment, my Sax parser tells me I have nothing. Is it something to do with is.read (buffer) section?
Also, just as a note, request is a HttpURLConnection with various signatures.
/*Input stream to read from our connection*/
InputStream is = request.getInputStream();
/*we make a 2 Kb buffer to accelerate the download, instead of reading the file a byte at once*/
byte [ ] buffer = new byte [ 2048 ] ;
/*How many bytes do we have already downloaded*/
int totBytes,bytes,sumBytes = 0;
totBytes = request.getContentLength () ;
while ( true ) {
/*How many bytes we got*/
bytes = is.read (buffer);
/*If no more byte, we're done with the download*/
if ( bytes <= 0 ) break;
sumBytes+= bytes;
Log.v("XML", sumBytes + " of " + totBytes + " " + ( ( float ) sumBytes/ ( float ) totBytes ) *100 + "% done" );
}
/* Parse the xml-data from our URL. */
// OLD, and works if comment all the above
//xr.parse(new InputSource(request.getInputStream()));
xr.parse(new InputSource(is))
/* Parsing has finished. */;
Can anyone help me at all??
Kind regards,
Andy
'I could only find a way to do that
with bytes, unless you know another
method?'.
But you haven't found a method. You've just written code that doesn't work. And you don't want to save the input to a String either. You want to count the bytes while you're parsing them. Otherwise you're just adding latency, i.e. wasting time and slowing everything down. For an example of how to do it right, see javax.swing.ProgressMonitorInputStream. You don't have to use that but you certainly do have to use a FilterInputStream of some sort, probaby one you write yourself, that is wrapped around the request input stream and passed to the parser.
Your while loop is consuming the input stream and leaving nothing for the parser to read.
For what you're trying to do, you might want to look into implementing a FilterInputStream subclass wrapping the input stream.
You are building an InputStream over another InputStream that consumes its data before.
If you want to avoid reading just single bytes you could use a BufferedInputStream or different things like a BufferedReader.
In any case it's better to obtain the whole content before parsing it! Unless you need to dynamically parse it.
If you really want to keep it on like you are doing you should create two piped streams:
PipedOutputStream pipeOut = new PipedOutputStream();
PipedInputStream pipeIn = new PipedInputStream();
pipeIn.connect(pipeOut);
pipeOut.write(yourBytes);
xr.parse(pipeIn);
Streams in Java, like their name suggest you, doesn't have a precise dimension neither you know when they'll finish so whenever you create an InputStream, if you read from them you cannot then pass the same InputStream to another object because data is already being consumed from the former one.
If you want to do both things (downloading and parsing) together you have to hook between the data received from the HTTPUrlConncection you should:
first know the length of the data being downloaded, this can be obtained from HttpUrlConnection header
using a custom InputStream that decorates (this is how streams work in Java, see here) updading the progressbar..
Something like:
class MyInputStream extends InputStream
{
MyInputStream(InputStream is, int total)
{
this.total = total;
}
public int read()
{
stepProgress(1);
return super.read();
}
public int read(byte[] b)
{
int l = super.read(b);
stepProgress(l);
return l;
}
public int read(byte[] b, int off, int len)
{
int l = super.read(b, off, len);
stepProgress(l);
return l
}
}
InputStream mis= new MyInputStream(request.getInputStream(), length);
..
xr.parse(mis);
You can save your data in a file, and then read them out.
InputStream is = request.getInputStream();
if(is!=null){
File file = new File(path, "someFile.txt");
FileOutputStream os = new FileOutputStream(file);
buffer = new byte[2048];
bufferLength = 0;
while ((bufferLength = is.read(buffer)) > 0)
os.write(buffer, 0, bufferLength);
os.flush();
os.close();
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
FileInputStream fis = new FileInputStream(file);
xpp.setInput(new InputStreamReader(fis));
}