Reset may not be the right word here, but I am currently building a program that lets the user look up a name, and by scanning a txt file containing a list of names followed by numbers, the program then displays the numbers following said name. The way I am doing this is via .nextLine, but if the user inputs a name that's later in the list (say Samantha in the example) and then tries to look up a name at the top of the list (like Sally), the second name isn't found.
For reference, here is an example of that txt file:
Sally 0 0 0 0 0 0 0 0 0 0 886
Sam 58 69 99 131 168 236 278 380 467 408 466
Samantha 0 0 0 0 0 0 272 107 26 5 7
Samir 0 0 0 0 0 0 0 0 920 0 798
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
public class BabyNames {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Scanner input = new Scanner (new File("BabyNames.txt"));
Scanner keyboard = new Scanner (System.in);
System.out.println("This program allows you to search through the data from the "
"Social Security Administration to see how popular a particular name has "
"been since 1900.");
System.out.print("Name? ");
String name = keyboard.nextLine();
do {
while(input.hasNextLine()) {
String text = input.nextLine();
String[] words = text.split(" ");
if (text.contains(name)) {
System.out.println("Statistics on name \"" name "\"");
for (int i = 1; i < words.length; i ) {
System.out.println((1900 (i-1)*10) ": " words[i]);
}
System.out.println("Enter another name or type quit to exit.");
name = keyboard.nextLine();
break;
}
else if (name.contains("quit") || name.contains("quit")){
System.exit(0);
}
else {
continue;
}
System.out.print("Error, name not found.");
}
} while (!name.contains("quit") || name.contains("quit"));
}
}
I looked up the .reset method but that hasn't seemed to work. Honestly, I'm stumped here.
CodePudding user response:
Again, don't try to "reset" the Scanner or re-read the file. Your best bet is to read the file once and place all the data into a collection of some type, here a Map<String, SomeCustomClass>
would work best. I'd first create a class to hold a row of information, perhaps called BabyName, one that holds a String field for name and an Integer List for the numbers listed after the name, something like:
import java.util.*;
public class BabyName {
String name;
List<Integer> numberList = new ArrayList<>();
public BabyName(String name, List<Integer> numberList) {
this.name = name;
this.numberList = numberList;
}
public String getName() {
return name;
}
public List<Integer> getNumberList() {
return numberList;
}
@Override
public String toString() {
return "BabyName [name=" name ", numberList=" numberList "]";
}
// consider adding hashCode and equals methods that are based on the name field alone
// ...
}
And then I'd recommend in your code a method that takes a line of text that has been extracted from the file, and converts it into a BabyName object:
private static BabyName createBabyName(String line) {
String[] tokens = line.split("\\s ");
String name = "";
List<Integer> numberList = new ArrayList<>();
// ... code to extract the data and that fills the numberList here
// ... left blank for you to fill in
BabyName babyName = new BabyName(name, numberList);
return babyName;
}
And then create a Map<String, BabyName> babyNameMap = new HashMap<>();
to hold BabyName objects using the name field as the map key, and when you read the file (just once mind you), you fill the map:
Scanner fileScanner = null;
try {
fileScanner = new Scanner(new File(FILE_PATH_NAME));
} catch (FileNotFoundException e) {
e.printStackTrace();
System.exit(-1);
}
// read file and fill the map
while (fileScanner.hasNextLine()) {
String line = fileScanner.nextLine();
BabyName babyName = createBabyName(line);
babyNameMap.put(babyName.getName(), babyName);
}
Then you can use this map to get the data from the user multiple times without having to re-read the file or reuse a Scanner.
e.g.,
String nameEnteredByUser = keyboard.nextLine();
BabyName selectedBabyName = babyNameMap.get(nameEnteredByUser);
// check for null here first
String name = nameEnteredByUser;
List<Integer> numberList = selectedBabyName.getNumberList();
CodePudding user response:
Java Scanner, in general, doesn't know/control the pointer position in the file. It wraps over InputStream which in turn provides the input to the Scanner w.r.t every nextLine() call.
Scanner systemInput = new Scanner(System.in);
//new Scanner(new FileInputStream("file.t"));
InputStream has Mark/Reset feature which will help us to control the pointer position.
(Note: mark/ reset enables us to mark a checkpoint and you can jump back to the checkpoint later.)
Unfortunately, FileInputStream doesn't support it. But, BufferedInputStream came to the rescue.
Let's cook a solution for your problem,
- Create a FileInputStream with your input file.
- Wraps it with BufferedInputStream which provides mark() and reset() functions.
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("BabyNames.txt"));
- Provide it as input to the Scanner constructor.
Scanner input = new Scanner (inputStream);
- Mark a checkpoint at the beginning of the file.
inputStream.mark(0);//saving a checkpoint
- After the end of the inner while loop, reset the pointer to the marked position.
inputStream.reset();
Now, your code works fine.