I'm having some trouble with two methods I have been trying to write. Both involve using a Scanner to read in a file, where each line is comma delimited. The methods are a part of my FileIO class. In addition to that class, I also have two other classes, Food and FoodArrayList. Food objects contain several pieces of information from food donations (who donated the items, the reason for the donation, etc.). The FoodArrayList class pretty much has the same functionality as an ArrayList, except that I've created it solely for the purpose to store Food objects.
The two methods I am struggling with are as follows:
What I am struggling with is that I close my Scanner in both of the methods. And I've been struggling with how to write an if
statement to detect if a Scanner was closed. In both of the methods, I have the following at the very beginning of the method:
try{
if(scan.hasNext() == false || scan == null)
scan = new Scanner(new File(this.getFileName()));
}
catch(FileNotFoundException fnfe){
System.err.println("The " + fileName + " could not be found.");
}
catch(IOException ioe){
ioe.printStackTrace();
}
This doesn't seem to work though. If I use method #1 first and then later on try to use method #2 (remember that I close the Scanner at the end of each method), the if
statement is skipped and scan
is never reinstantiated and I am thrown an IllegalStateException
.
A very big point that I should have mentioned earlier is this: I want both methods to be reusable. If method #1 goes on until the end of file and I later on use method #2, I would want the Scanner object to be reading from the beginning of the file again. And it seems that the only way to do this is to reinstantiate the Scanner object.
When I first learned about I/O in Java, I remember being told to always make sure to close Scanner objects when you're done using them. That's something that has been ingrained into my brain almost. Regardless, how do I get around this? One thought I had was to set scan
to null at the end of both of my methods. Is that even necessary? I've read on other posts in StackOverflow that would lead me to not even closing my Scanner object and just let the garbage collector take care of it.
In addition to all of this, I was also wondering what exactly happens to a Scanner once it is closed and why it can't be reopened. I understand that it has something to do with Streams, but I don't exactly know what those are in the context of Java. I tried looking up the Stream class, but was not able to obtain much of an understanding from it.
Here is the code from both of my methods:
Method #1
public int numberOfLines(){
try{
if(scan.hasNext() == false || scan == null)
scan = new Scanner(new File(this.getFileName()));
}
catch(FileNotFoundException fnfe){
System.err.println("The " + fileName + " could not be found.");
}
catch(IOException ioe){
ioe.printStackTrace();
}
int lineCount = 0;
while(scan.hasNextLine()){
scan.nextLine();
lineCount++;
}
scan.close();
return lineCount;
}
Method #2
public void readFile(FoodArrayList fal){
try{
if(scan.hasNext() == false || scan == null)
scan = new Scanner(new File(this.getFileName()));
}
catch(FileNotFoundException fnfe){
System.err.println("The " + fileName + " could not be found.");
}
catch(IOException ioe){
ioe.printStackTrace();
}
while(scan.hasNextLine()){
String stringRead = scan.nextLine();
StringTokenizer tokens = new StringTokenizer(stringRead,",");
Food temp = new Food();
temp.setCorpName(tokens.nextToken());
temp.getContact().setLast(tokens.nextToken());
temp.getContact().setFirst(tokens.nextToken());
temp.setDate(tokens.nextToken());
temp.setProductCode(tokens.nextToken());
temp.setDescription(tokens.nextToken());
temp.setReason(tokens.nextToken());
String numberString = tokens.nextToken();
int number = Integer.parseInt(numberString);
temp.setNumber(number);
temp.setCoP(tokens.nextToken());
fal.insert((Food)temp);
}
scan.close();
}
I'd like to keep these two methods separate. This is because in my client class, I am performing the following:
FoodArrayList list;
FileIO fio = new FileIO();
int listSize = fio.numberOfLines();
list = new FoodArrayList(listSize);
fio.readFile(list);
The default constructor of my FileIO class already considers the particular file I am attempting to read. Of course, the overloaded constructor allows one to use a different filename as an input. Any help on this would be appreciated.
If you do not close the Scanner then Java will not garbage collect the Scanner object and you will have a memory leak in your program: void close(): closes the Scanner and allows Java to reclaim the Scanner's memory.
When a Scanner is closed, it will close its input source if the source implements the Closeable interface.
More specifically, the close() method of the Scanner class is used to close the opened Scanner object. It is recommended to close the Scanner after performing any tasks on the Scanner instance; otherwise, the Scanner will be activated and potentially cause data leaks.
I want both methods to be reusable. If method #1 goes on until the end of file and I later on use method #2, I would want the Scanner object to be reading from the beginning of the file again. And it seems that the only way to do this is to reinstantiate the Scanner object.
It seems like you have your answer here. You want to reinstantiate the Scanner
object irrespective of what state one of them may have been in, so just...do that.
At the beginning of your methods, declare and instantiate your Scanner
reference. You could also use try-with-resources
so that your Scanner
is always closed at the end of its run.
public void countLines() {
try(Scanner scan = new Scanner(new File("file.txt"))) {
// code
} catch (IOException e) {
e.printStackTrace();
}
}
Even though in reality the work you're doing could be accomplished in the same method, if you insist on performing these operations separately, then initializing two different Scanner
s is your best option.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With