如何让Java扫描器接受空输入?

6

我在让程序响应空输入方面遇到了困难。例如,假设我想提示用户输入一个类型为BigDecimal的金额值以及货币类型。以下是问题程序的示例。

public static void main(String[] args) {
    Scanner input = new Scanner(System.in);

    System.out.print("Enter the amount of money " 
                             + "and specify currency (USD or CNY): ");

    // initialize variable moneyInput
    BigDecimal moneyInput;

    // check if moneyInput is numeric
    try {
         moneyInput = input.nextBigDecimal();
    } catch (InputMismatchException e){
        System.err.println("InputMismatchException: non-numeric value");
        return;
    }

    // specify currency of moneyInput
    String currencyType = input.next();

    ...
}

所以,这里的输入例如 100.00 USD 是可以正常工作的。
Enter the amount of money and specify currency (USD or CNY): 100.00 USD
$100.00 USD ≈ ¥670.17 CNY

一个类似于ASDF USD的输入,应该会适当地显示错误信息。

Enter the amount of money and specify currency (USD or CNY): ASDF USD
InputMismatchException: non-numeric value

但是,我如何强制程序响应空输入,无论是立即按下回车键还是在第一行输入一堆空格?例如:

Enter the amount of money and specify currency (USD or CNY): 





1000.00 USD
$1000.00 USD ≈ ¥6701.70 CNY

在上面的例子中,用户可以一直按回车键,直到输入了可读的内容(有效或无效)。我想实现一种方式来检查用户是否按下回车键而没有输入任何有意义的内容。 另一个我不知道如何解决的结果是,如果用户仅输入货币值,然后在最终输入货币之前多次按[return]。
示例:
Enter the amount of money and specify currency (USD or CNY): 100.00



USD
$100.00 USD ≈ ¥670.17 CNY

如何让程序在用户输入数字并按下[回车]后提示用户输入货币类型,例如请输入货币类型:?类似于:

Enter the amount of money and specify currency (USD or CNY): 100.00
Please specify the currency: USD
$100.00 USD ≈ ¥670.17 CNY

通过更改try-catch块后面的部分,我能够实现上述功能:

input.nextLine();
System.out.print("Please specify the currency: ");
String currencyType = input.nextLine();

但是使用这种方法,程序失去了允许用户在一行中输入moneyInputcurrencyType的能力,就像第一个例子中那样。(而且,仍然存在一个问题,即可以无限制地按下[return]键来为每个提示输入内容,直到最终输入可读内容为止)。
感谢阅读,对于这篇长篇大论的文章表示抱歉。
1个回答

11

Scanner 提供了两种基本的用法,有时候它们是相互矛盾的:

  1. 令牌 读取输入,忽略其中的任何空格(这是您初始代码所做的)。
  2. 读取输入,而不将这些行的任何内容视为特殊内容。

一般来说,混合使用这两种方式是不好的想法(它可以工作,但可能不是您想要的方式)。像next()nextBigDecimal() 这样的令牌读取方法会忽略换行符。

如果您想处理 Enter 键,您需要使用 Scanner.nextLine() 按行读取用户的输入,并逐行解析每个输入行(例如,line.split("\\s+")),而不是使用 Scanner 的令牌读取方法。

有些人喜欢使用嵌套的Scanner,使用一个Scanner 按行读取输入,然后将该行传递到新的Scanner 中,以仅对该行进行标记化。

例如:

try (Scanner in = new Scanner(System.in)) {
  while (in.hasNextLine()) {
    try {
      String line = in.nextLine();
      Scanner lineScan = new Scanner(line);
      BigDecimal moneyInput = lineScan.nextBigDecimal();
      String currency = lineScan.next();
      // do something
    } catch (NoSuchElementException | IllegalStateException e) {
      System.err.print("Please enter the VALUE followed by the CURRENCY");
    }
  }
}
如果您不想使用嵌套的Scanner,还有许多其他大致等效的机制。这是主要思路,但您可能需要添加其他错误处理代码(例如,如果new BigDecimal()引发异常): 使用 String.split()
String[] parts = line.split("\\s+");
if (parts.length == 2) {
  BigDecimal moneyInput = new BigDecimal(parts[0]);
  String currency = parts[1];
  // do something
} else {
  System.err.println("Please enter the VALUE followed by the CURRENCY");
}

使用Pattern

/**
 * Matches one or more digits, optionally followed by a . and more digits,
 * followed by whitespace then one or more uppercase letters.
 */
private static final Pattern MONEY_PATTERN =
    Pattern.compile("(\\d+(?:\\.\\d+))\\s+([A-Z]+)");

那么:

Matcher m = MONEY_PATTERN.matcher(line);
if (m.matches()) {
  BigDecimal moneyInput = new BigDecimal(m.group(1));
  String currency = m.group(2);
// do something
} else {
  System.err.println("Please enter the VALUE followed by the CURRENCY");
}

你好,能否展示一下如何使用 line.split("\\s+") 方法来实现相同的结果呢?我对这个方法不太熟悉,所以在语法上遇到了些困难。谢谢。 - A is for Ambition
1
添加了更多的示例。 - dimo414
好的,我该如何单独检查currency是否有效?我希望它能像moneyInput一样运作,即如果输入类似于100.00 abc100.00之类的内容,则用户将被要求重复输入currency,一旦他们输入了有效的货币类型,例如USD,程序就会正常恢复。我尝试在内部使用另一个do-while循环来检查货币类型,这个方法可以工作,但是当输入为空时,它就会出现问题,这基本上是我在本文开头遇到的同样的问题(这是我的代码:http://pastebin.com/raw/CT0qjBPk和程序截图:http://imgur.com/a/mjfaL)。 - A is for Ambition
我还尝试使用第二组嵌套的扫描器,后跟另一个try块,全部位于while循环内(基本上是镜像您的第一个解决方案)。一切都正常工作,包括能够响应重新提示currency时的空输入,但我遇到了一些新问题(请参见:http://pastebin.com/kEUQSEFF和http://imgur.com/a/W6WWB)。我正在不断地解决这个问题,所以如果我修复了其中一些错误,我一定会更新。非常感谢您的帮助。 - A is for Ambition
如果你想处理货币,我强烈建议使用Joda-Money库——它有一个经常更新的货币数据库,这样你就不必自己进行验证。你所描述的行为(提示输入两个值,但分别验证和重新提示)是非常复杂的,最好在另一个后续问题中讨论,而不是在评论中。如果你把链接发给我,我很乐意试一试。 - dimo414
好主意,感谢你抽出时间来帮助。这是更新后的追问:http://stackoverflow.com/q/38557212/5606164 因为这是一个简单的问题,所以已经有人回答了,但如果可以请你看一下。 - A is for Ambition

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接