I have initialized an InputStream in a single method in a class and passing it to next method for processing. The InputStream essentially encapsulates CSV file for processing.
Another method calls 2 different methods passing in same InputStream one for retrieving headers and another for processing contents. The structure looks something as given below:
main() {
FileInputStream fis = new FileInputStream("FileName.CSV");
BufferedInputStream bis = new BufferedInputStream(fis);
InputStreamReader isr = new InputStreamReader(bis);
processCSV(isr);
}
processCSV(Reader isr) {
fetchHeaders(isr);
processContentRows(isr);
}
fetchHeaders(Reader isr) {
//Use BufferedReader to retrieve first line of CSV
//Even tried mark() and reset() here
}
processContentRows(Reader isr) {
//Cannot read the values, fetches null from InputStream :(
}
Am I doing something wrong here? Is there any way I can reuse InputStream across different method calls.
I am putting up complete program that can mimic the issue below:
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class MarkResetTest
{
public static void main(String a[])
{
FileInputStream fis = null;
BufferedInputStream bis = null;
InputStreamReader isr = null;
BufferedReader br = null;
BufferedReader br2 = null;
try {
fis = new FileInputStream("C:/Test/Customers.csv");
bis = new BufferedInputStream(fis);
isr = new InputStreamReader(bis, "Unicode");
System.out.println("BR readLine()");
br = new BufferedReader(isr);
//System.out.println(br.markSupported());
br.mark(1000);
System.out.println(br.readLine());
br.reset();
//System.out.println(br.readLine());
System.out.println("BR2 readLine()");
br2 = new BufferedReader(isr);
System.out.println(br2.readLine());
}
catch(Exception e) {
System.out.println("Exception::" + e);
}
finally {
try {
br.close();
isr.close();
bis.close();
fis.close();
}
catch(Exception e) {
System.out.println("Exception while closing streams :: " + e);
}
}
}
}
The problem is in creating two BufferedReader
s on top of the same Reader
. When you read data from BufferedReader
, it's likely to read more than the data it returns, into its buffer (hence the name). In other words, even though you've only read a single line from the BufferedReader
, the InputStreamReader
may have had a lot more data read from it - so if you read again from that InputStreamReader
then you'll miss that data. The data has effectively been sucked from the InputStreamReader
to the BufferedReader
, so the only way of getting it out to client code is to read it from that BufferedReader
.
In other words, your claim that:
Nope. fetchHeaders() only reads first line of CSV containing Headers.
is incorrect. It only uses that much data, but it reads more from the InputStreamReader
.
As Ilya said, you should only create one BufferedReader
on top of the original InputStreamReader
, and pass that into both methods.
fetchHeaders
can then use that BufferedReader
to read a line, and processContentRows
can do what it likes with the BufferedReader
at that point - it's just a Reader
as far as it needs to know.
So to modify Ilya's example slightly:
public static void main(String[] args) {
FileInputStream fis = new FileInputStream("FileName.CSV");
BufferedInputStream bis = new BufferedInputStream(fis);
InputStreamReader isr = new InputStreamReader(bis);
BufferedReader br = new BufferedReader(isr);
processCSV(br);
}
private static void processCSV(BufferedReader reader) {
fetchHeaders(reader);
processContentRows(reader);
}
private static void fetchHeaders(BufferedReader reader) {
// Use reader.readLine() here directly... do *not* create
// another BufferedReader on top.
}
private static void processContentRows(Reader reader) {
// This could be declared to take a BufferedReader if you like,
// but it doesn't matter much.
}