Home > Blockchain >  Is there way a "reset" a scanner in Java?
Is there way a "reset" a scanner in Java?

Time:11-28

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,

  1. Create a FileInputStream with your input file.
  2. Wraps it with BufferedInputStream which provides mark() and reset() functions.
 BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("BabyNames.txt"));
  1. Provide it as input to the Scanner constructor.
Scanner input = new Scanner (inputStream);
  1. Mark a checkpoint at the beginning of the file.
inputStream.mark(0);//saving a checkpoint
  1. After the end of the inner while loop, reset the pointer to the marked position.
inputStream.reset();

Now, your code works fine.

  • Related