Reputation: 219
I'm new to Java streams, I would like to read a specific files content then need to read it from the beginning. I have created a BufferedInputStream and i'm confused about the documentation of BufferedInputStream.mark(int markLimit)
Documentation says:
public void mark(int readlimit)
This method marks a position in the input to which the stream can be "reset" by calling the reset() method. The parameter readlimit is the number of bytes that can be read from the stream after setting the mark before the mark becomes invalid. For example, if mark() is called with a read limit of 10, then when 11 bytes of data are read from the stream before the reset() method is called, then the mark is invalid and the stream object instance is not required to remember the mark.
Note that the number of bytes that can be remembered by this method can be greater than the size of the internal read buffer. It is also not dependent on the subordinate stream supporting mark/reset functionality.
Overrides: mark in class FilterInputStream
Parameters: readlimit - The number of bytes that can be read before the mark becomes invalid**
My code is:
public class Test {
public static void main(String[] args) throws IOException {
File resource = new File("beforeFix.txt");
FileInputStream fileInputStream = new FileInputStream(resource);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
int i = bufferedInputStream.read();
bufferedInputStream.mark(1);
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
bufferedInputStream.reset();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
i = bufferedInputStream.read();
bufferedInputStream.reset();
}
}
In the above code I have set the marklimit as 1 but the mark is not goes into invalid as per the documentation.
Can anyone clearly explain me what is the actual purpose of setting this with small example?
Thanks in advance
Upvotes: 6
Views: 8901
Reputation: 11
As the Oracle documentation says about reset() method of InputStream (which is override by FilterInputStream and further by BufferedInputStream).
The general contract of reset is:
The general contract of reset is:
-If the method markSupported returns true, then:
-If the method mark has not been called since the stream was created, or
the number of bytes read from the stream since mark was last called is
larger than the argument to mark at that last call, then an IOException
**might be** thrown.
(note that IOException might be thrown)
-If such an IOException is not thrown, then the stream is reset to a state
such that all the bytes read since the most recent call to mark (or since
the start of the file, if mark has not been called) will be resupplied to
subsequent callers of the read method, followed by any bytes that
otherwise would have been the next input data as of the time of the call
to reset.
I hope, your question is solved and help this for future programmers.
Upvotes: 0
Reputation: 322
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class Main {
static final String fileAbsolutePath = "file.txt";
public static void main(String[] args) {
// file contains 3 chars 'a', 'b', and 'c'
try (FileInputStream fileInputStream = new FileInputStream(fileAbsolutePath);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {
System.out.println((char) bufferedInputStream.read()); // prints a
bufferedInputStream.mark(2); // place mark at second byte
System.out.println((char) bufferedInputStream.read()); // prints b
System.out.println((char) bufferedInputStream.read()); // prints c
System.out.println(bufferedInputStream.read()); // prints -1
bufferedInputStream.reset(); // meaning start again from where I placed the mark
System.out.println((char) bufferedInputStream.read()); // prints b
System.out.println((char) bufferedInputStream.read()); // prints c
System.out.println( bufferedInputStream.read()); // prints -1
} catch (IOException ie) { ie.printStackTrace();}
}
}
Upvotes: 0
Reputation: 21
Please read the below documentation to better understand it. I had the same doubt as you and then decided to read about it in detail.
From the first point, it is now very much clear that an IOException is not guaranteed to be thrown. So, if you are reading more than the allowed number of bytes (the specified argument of mark method) after calling the mark method, then it's a risky operation and not recommended.
From the second point, you can understand what happens if IOException is not thrown.
The link to the documentation: https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html#reset--
Upvotes: 0
Reputation: 298579
By calling mark
with a specified limit, you are requesting the capability to support resetting after reading up to the specified limit, you are not denying a capability beyond that. The specification clearly says:
However, the stream is not required to remember any data at all if more than
readlimit
bytes are read from the stream beforereset
is called.
“is not required to” does not imply “is not allowed to”. The specification simply states what you can not expect to always work, it doesn’t state what you can expect to always fail.
In the case of BufferedInputStream
, it’s easy to explain what happens under the hood. Each BufferedInputStream
has a capacity, the default is 8192, and it always can reset past as many bytes as its current buffer capacity. By specifying a higher limit, you will cause it to allocate a larger buffer when needed, to fulfill the guaranty.
Since you can’t query a stream for its current buffer capacity, you can only rely on the guaranty that reset works as long as you don’t read more bytes than the specified limit.
It’s easy to change your example to make it fail reproducible:
File resource = new File("beforeFix.txt");
FileInputStream fileInputStream = new FileInputStream(resource);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 1);
int i = bufferedInputStream.read();
bufferedInputStream.mark(1);
i = bufferedInputStream.read();
i = bufferedInputStream.read();
bufferedInputStream.reset(); // will fail
Upvotes: 2
Reputation: 8074
In order for reset to work and go back to the position you marked, the data that was read after you marked needs to be buffered in memory. The value you specify when marking is the amount of memory that should be reserved for this.
So if you intend to read 100 bytes before calling reset, then your buffer needs to be atleast 100 bytes, and so that is what you have to call mark with.
bufferedInputStream.mark(200);
... read no more than 200 bytes ...
bufferedInputStream.reset(); // reset back to marked position
Update
It looks like the documentation for mark
is not matching the actual behaviour. The documentation states:
the maximum limit of bytes that can be read before the mark position becomes invalid
However, it looks like it should be the minimum limit
, or at the very least the underlying implementations are not required to discard the mark as soon as the read limit is exceeded if they can still support resetting to the marked position.
Upvotes: 1