java.util.NoSuchElementException - 读取用户输入的扫描程序

java.util.NoSuchElementException - Scanner reading user input

提问人:fortune 提问时间:10/24/2012 最后编辑:Ivarfortune 更新时间:3/4/2023 访问量:156015

问:

我是使用 Java 的新手,但我以前有一些 C# 经验。我遇到的问题来自从控制台读取用户输入。

我在这部分代码中遇到了“java.util.NoSuchElementException”错误:

payment = sc.next(); // PromptCustomerPayment function

我有两个获取用户输入的函数:

  • 提示客户数量
  • PromptCustomerPayment

如果我不调用 PromptCustomerQty,那么我就不会收到此错误,这让我相信我在扫描仪上做错了什么。下面是我的完整代码示例。我感谢任何帮助。

public static void main (String[] args) {   
    
    // Create a customer
    // Future proofing the possabiltiies of multiple customers
    Customer customer = new Customer("Will");
    
    // Create object for each Product
    // (Name,Code,Description,Price)
    // Initalize Qty at 0
    Product Computer = new Product("Computer","PC1003","Basic Computer",399.99); 
    Product Monitor = new Product("Monitor","MN1003","LCD Monitor",99.99);
    Product Printer = new Product("Printer","PR1003x","Inkjet Printer",54.23);
    
    // Define internal variables 
    // ## DONT CHANGE 
    ArrayList<Product> ProductList = new ArrayList<Product>(); // List to store Products
    String formatString = "%-15s %-10s %-20s %-10s %-10s %n"; // Default format for output

    // Add objects to list
    ProductList.add(Computer);
    ProductList.add(Monitor);
    ProductList.add(Printer);
    
    // Ask users for quantities 
    PromptCustomerQty(customer, ProductList);
    
    // Ask user for payment method
    PromptCustomerPayment(customer);
    
    // Create the header
    PrintHeader(customer, formatString);
    
    // Create Body
    PrintBody(ProductList, formatString);   
}

public static void PromptCustomerQty(Customer customer, ArrayList<Product> ProductList) {
    // Initiate a Scanner
    Scanner scan = new Scanner(System.in);
    
    // **** VARIABLES ****
    int qty = 0;
    
    // Greet Customer
    System.out.println("Hello " + customer.getName());
    
    // Loop through each item and ask for qty desired
    for (Product p : ProductList) {

        do {
        // Ask user for qty
        System.out.println("How many would you like for product: " + p.name);
        System.out.print("> ");
        
        // Get input and set qty for the object
        qty = scan.nextInt();
        
        }
        while (qty < 0); // Validation
        
        p.setQty(qty); // Set qty for object
        qty = 0; // Reset count
    }
    
    // Cleanup
    scan.close();
}

public static void PromptCustomerPayment (Customer customer) {
    // Initiate Scanner 
    Scanner sc = new Scanner(System.in);
    
    // Variables
    String payment = "";

    // Prompt User
    do {
    System.out.println("Would you like to pay in full? [Yes/No]");
    System.out.print("> ");
    
    payment = sc.next();
    
    } while ((!payment.toLowerCase().equals("yes")) && (!payment.toLowerCase().equals("no")));
    
    // Check/set result
    if (payment.toLowerCase().equals("yes")) {
        customer.setPaidInFull(true);
    }
    else {
        customer.setPaidInFull(false);
    }
    
    // Cleanup
    sc.close(); 
}
Java 输入 java.util.scanner nosuchelementexception

评论


答:

27赞 user1766873 10/24/2012 #1

问题是

当 Scanner 关闭时,如果源实现了 Closeable 接口,它将关闭其输入源。

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Scanner.html

因此关闭 .scan.close()System.in

要修复它,您可以制作

Scanner scan static并且不要在 PromptCustomerQty 中关闭它。下面的代码有效。

public static void main (String[] args) {   

// Create a customer
// Future proofing the possabiltiies of multiple customers
Customer customer = new Customer("Will");

// Create object for each Product
// (Name,Code,Description,Price)
// Initalize Qty at 0
Product Computer = new Product("Computer","PC1003","Basic Computer",399.99); 
Product Monitor = new Product("Monitor","MN1003","LCD Monitor",99.99);
Product Printer = new Product("Printer","PR1003x","Inkjet Printer",54.23);

// Define internal variables 
// ## DONT CHANGE 
ArrayList<Product> ProductList = new ArrayList<Product>(); // List to store Products
String formatString = "%-15s %-10s %-20s %-10s %-10s %n"; // Default format for output

// Add objects to list
ProductList.add(Computer);
ProductList.add(Monitor);
ProductList.add(Printer);

// Ask users for quantities 
PromptCustomerQty(customer, ProductList);

// Ask user for payment method
PromptCustomerPayment(customer);

// Create the header
PrintHeader(customer, formatString);

// Create Body
PrintBody(ProductList, formatString);   
}

static Scanner scan;

public static void PromptCustomerQty(Customer customer, ArrayList<Product> ProductList)               {
// Initiate a Scanner
scan = new Scanner(System.in);

// **** VARIABLES ****
int qty = 0;

// Greet Customer
System.out.println("Hello " + customer.getName());

// Loop through each item and ask for qty desired
for (Product p : ProductList) {

    do {
    // Ask user for qty
    System.out.println("How many would you like for product: " + p.name);
    System.out.print("> ");

    // Get input and set qty for the object
    qty = scan.nextInt();

    }
    while (qty < 0); // Validation

    p.setQty(qty); // Set qty for object
    qty = 0; // Reset count
}

// Cleanup

}

public static void PromptCustomerPayment (Customer customer) {
// Variables
String payment = "";

// Prompt User
do {
System.out.println("Would you like to pay in full? [Yes/No]");
System.out.print("> ");

payment = scan.next();

} while ((!payment.toLowerCase().equals("yes")) && (!payment.toLowerCase().equals("no")));

// Check/set result
if (payment.toLowerCase() == "yes") {
    customer.setPaidInFull(true);
}
else {
    customer.setPaidInFull(false);
}
}

顺便说一句,您不应该用于字符串比较,而应改用。==.equals

评论

0赞 Bhesh Gurung 10/24/2012
但是他们采用不同的方法,对吧?创建和关闭它们的位置。
0赞 10/24/2012
在原始代码中,它们是。在固定代码中,我对这两种方法都使用一个 Scanner 实例。
0赞 Yogendra Singh 10/24/2012
我仍然感到困惑,为什么甚至在第二种方法中导致异常。sc.next()sc.nextLine()
0赞 Bhesh Gurung 10/24/2012
@YogendraSingh:nextLine() 是否也抛出相同的异常?
0赞 Yogendra Singh 10/24/2012
@BheshGurung:对我来说,它说java.util.NoSuchElementException: No line found
190赞 Yogendra Singh 10/24/2012 #2

这真的让我困惑了一段时间,但这就是我最终发现的。

当您调用时,在第一种方法中,它不仅会关闭扫描程序,还会关闭输入流。您可以通过在第二种方法的最顶部打印其状态来验证它:sc.close()System.in

    System.out.println(System.in.available());

因此,现在当您重新实例化时,在第二种方法中,它不会找到任何开放流,因此会出现异常。ScannerSystem.in

我怀疑是否有任何方法可以重新开放,因为:System.in

public void close() throws IOException --> Closes this input stream and releases any system resources associated with this stream. The general contract of close is that it closes the input stream. A closed stream cannot perform input operations and **cannot be reopened.**

解决您的问题的唯一好方法是在 main 方法中启动,在两个方法中将其作为参数传递,然后在 main 方法中再次关闭它,例如:Scanner

main方法相关代码块:

Scanner scanner = new Scanner(System.in);  

// Ask users for quantities 
PromptCustomerQty(customer, ProductList, scanner );

// Ask user for payment method
PromptCustomerPayment(customer, scanner );

//close the scanner 
scanner.close();

您的方法:

 public static void PromptCustomerQty(Customer customer, 
                             ArrayList<Product> ProductList, Scanner scanner) {

    // no more scanner instantiation
    ...
    // no more scanner close
 }


 public static void PromptCustomerPayment (Customer customer, Scanner sc) {

    // no more scanner instantiation
    ...
    // no more scanner close
 }

希望这能让您对故障和可能的解决方案有所了解。

评论

14赞 fortune 10/24/2012
感谢您的回复。你对幕后实际发生的事情的解释非常有帮助
0赞 d3vpasha 4/29/2016
感谢您的回复。这实际上是真的!
0赞 Jake Stokes 8/10/2017
哦,我的上帝,我被困了这么久,直到我找到这个帖子。非常感谢!你真棒。
0赞 JedaiCoder 8/5/2020
虽然关闭扫描仪是一种很好的做法,但为了避免此错误,最好永远不要关闭扫描仪。它避免了所有这些错误。我们始终可以禁止显示警告
2赞 vitrums 11/29/2020
我想知道这是否是一个误导性例外的情况。即,封闭的输入流有一个明确的条件,但行为是如果它没有任何问题,- 只是一个没有这样的元素......让我想起了 NullPointerException 引起的运行时混乱,只是形式较温和。
2赞 Shady 8/18/2020 #3

您需要删除扫描仪的结束行:scan.close();

它以前发生在我身上,这就是原因。

2赞 Eduardo Salvadinha 11/12/2020 #4

异常的原因已经解释过,但是建议的解决方案并不是最好的。

您应该创建一个类,该类使用单一实例模式将 Scanner 保持为私有,从而使该扫描程序在您的代码中是唯一的。

然后你可以实现你需要的方法,或者你可以创建一个getScanner(不推荐),你可以用一个私有的布尔值来控制它,比如alreadyClosed。

如果您不知道如何使用单例模式,下面是一个示例:

public class Reader {
    
    
    private Scanner reader;
    private static Reader singleton = null;
    private boolean alreadyClosed;
    
    private Reader() {
        alreadyClosed = false;
        reader = new Scanner(System.in);
    }
    
    public static Reader getInstance() {
        if(singleton == null) {
            singleton = new Reader();
        }
        return singleton;
    }
    
    public int nextInt() throws AlreadyClosedException {
        if(!alreadyClosed) {
            return reader.nextInt();
        }
        throw new AlreadyClosedException(); //Custom exception
    }
    
    public double nextDouble() throws AlreadyClosedException {
        if(!alreadyClosed) {
            return reader.nextDouble();
        }
        throw new AlreadyClosedException();
    }
    
    public String nextLine() throws AlreadyClosedException {
        if(!alreadyClosed) {
            return reader.nextLine();
        }
        throw new AlreadyClosedException();
    }
    
    public void close() {
        alreadyClosed = true;
        reader.close();
    }   
}

1赞 Alex Ertl 3/14/2022 #5

对于在 HackerRank 等网站上参加在线考试时来到这里的任何人——

如果您尝试通过单击按钮以使用自定义输入执行 main() 来测试您的(可能)完美无缺的代码,您可能会收到此消息。

在这种情况下,您需要单击另一个按钮,例如“运行单元测试”。你可能只根据代码是否通过了他们编写的单元测试来评估,而不是你是否有能力将代码重构为更少的 LOC 或你的编码风格。

0赞 Maheeka Kossinna 3/4/2023 #6

找到这个线程是因为我遇到了同样的问题。从这里找到原因后,我发现一种有用的方法是将单独的扫描仪对象用于不同的用途(因为如果使用单个扫描仪对象进行多个输入,则会有问题),然后在所有扫描仪对象使用完毕后关闭所有扫描仪对象。为更清晰的代码而制作,之后没有遇到任何问题。