有没有办法在Java中“重置”扫描仪?

Is there way a "reset" a scanner in Java?

提问人:Connor_G 提问时间:11/28/2022 最后编辑:Jim GarrisonConnor_G 更新时间:11/28/2022 访问量:134

问:

重置在这里可能不是正确的词,但我目前正在构建一个程序,让用户查找名称,并通过扫描包含名称列表后跟数字的 txt 文件,程序然后显示该名称后面的数字。我这样做的方式是通过 .nextLine,但是如果用户输入列表中稍后的名称(例如示例中的 Samantha),然后尝试在列表顶部查找名称(如 Sally),则找不到第二个名称。

作为参考,下面是该 txt 文件的示例:

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"));
        
    }
}

我查找了 .reset 方法,但似乎不起作用。老实说,我在这里被难住了。

Java 列表 文件 java.util.scanner

评论

4赞 Hovercraft Full Of Eels 11/28/2022
为什么要尝试重置任何内容?相反,将文件读数据结构一次,然后根据需要查询该数据结构。也许你想使用 a 作为数据结构,其中第一个 String 是你需要搜索的“键”字符串,第二个或“值”字符串是整行数据。HashMap<String, String>
0赞 Old Dog Programmer 11/28/2022
StackOverflow 上至少有一个人有相同的任务。但是,这个问题仍然没有答案。stackoverflow.com/questions/72064778/......

答:

0赞 EDITH 11/28/2022 #1

通常,Java Scanner 不知道/控制文件中的指针位置。它包装在 InputStream 上,而 InputStream 又在每个 nextLine() 调用时向 Scanner 提供输入。

Scanner systemInput = new Scanner(System.in);
//new Scanner(new FileInputStream("file.t"));

InputStream 具有标记/重置功能,可以帮助我们控制指针位置。

(注意:标记/重置使我们能够标记检查点,您可以稍后跳回检查点。

不幸的是,FileInputStream 不支持它。但是,BufferedInputStream 派上用场了。

让我们为您的问题制定解决方案,

  1. 使用输入文件创建 FileInputStream。
  2. 用 BufferedInputStream 包装它,后者提供 mark() 和 reset() 函数。
 BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("BabyNames.txt"));
  1. 将其作为输入提供给 Scanner 构造函数。
Scanner input = new Scanner (inputStream);
  1. 在文件开头标记检查点。
inputStream.mark(0);//saving a checkpoint
  1. 在内部 while 循环结束后,将指针重置到标记的位置。
inputStream.reset();

现在,您的代码工作正常。

评论

0赞 g00se 11/29/2022
这是行不通的。查看 Javadoc 并注意默认缓冲区大小(内存为 8 或 4 MiB)markBufferedInputStream
1赞 Hovercraft Full Of Eels 11/28/2022 #2

同样,不要尝试“重置”扫描仪或重新读取文件。最好的办法是读取文件一次,然后将所有数据放入某种类型的集合中,这里效果最好。我首先创建一个类来保存一行信息,可能称为 BabyName,它包含名称的 String 字段和名称后面列出的数字的整数列表,如下所示:Map<String, SomeCustomClass>

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
    // ...
    
}

然后,我建议在代码中使用一种方法,该方法采用从文件中提取的一行文本,并将其转换为 BabyName 对象:

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;
}

然后创建一个使用 name 字段作为映射键来保存 BabyName 对象,当您读取文件时(请注意一次),您填充映射:Map<String, BabyName> babyNameMap = new HashMap<>();

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);            
}

然后,您可以使用此映射多次从用户那里获取数据,而无需重新读取文件或重复使用扫描仪。

例如,

String nameEnteredByUser = keyboard.nextLine();
BabyName selectedBabyName = babyNameMap.get(nameEnteredByUser);

// check for null here first
String name = nameEnteredByUser;
List<Integer> numberList = selectedBabyName.getNumberList();