6

I am having trouble getting my program to respond to empty inputs. For example, say I wanted to prompt the user to enter a value for money with type BigDecimal as well as a currency type. Here is what the program in question looks like.

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

    ...
}

So here, an input like 100.00 USD, works fine.

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

An input like ASDF USD appropriately results in the error message.

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

But, how do I force the program to respond to blank inputs, either immediately pressing the return key or entering a bunch of spaces in the first line? For example:

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





1000.00 USD
$1000.00 USD ≈ ¥6701.70 CNY

In the above example, the user can just press the return key indefinitely until something readable (valid or invalid) is entered. I want to implement some way to check if the user pressed the return key without entering anything meaningful.

Another outcome that I don't know how to account for is if the user enters ONLY the monetary value, then presses [return] a bunch of times before finally inputting the currency.

Example:

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



USD
$100.00 USD ≈ ¥670.17 CNY

How do I get the program to prompt the user with something like Please specify the currency: after a numeric value is input and the user presses [return]? Something like

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

I was able to achieve the above functionality by changing the part after the try-catch block to:

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

But using this method, the program loses the ability to allow the user to input both moneyInput and currencyType in one line, like in the first example. (And again, there is still the problem of being able to press [return] indefinitely for each of these prompts until something readable is finally input).

Thanks for reading and sorry for the long post.

1 Answers1

11

Scanner offers two basic - and sometimes conflicting - use cases:

  1. Read input by tokens, disregarding any whitespace inbetween (this is what your initial code does)
  2. Read input by line, without treating any contents of those lines specially

Generally speaking mixing the two is a poor idea (it works, but probably not how you intend). The token-reading methods like next() and nextBigDecimal() ignore newlines.

If you want to handle Enter you need to read a user's input line-by-line with Scanner.nextLine() and parse each line individually (i.e. line.split("\\s+")), rather than use Scanner's token-reading methods.

Some people like to use nested Scanners and read input line by line with one Scanner then pass the line into a new Scanner to tokenize just that line.

For example:

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

If you don't want to use a nested Scanner there are a number of other roughly-equivalent mechanisms. Here's the gist, but you'll likely want to add additional error-handling code (e.g. if new BigDecimal() throws an exception:

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

Using 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]+)");

Then:

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");
}
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Hi, can you please show how to use the parse method with `line.split("\\s+")` to achieve the same result? I'm not familiar with that method so I'm having a hard time with the syntax. Thanks – A is for Ambition Jul 20 '16 at 15:30
  • 1
    Added more examples. – dimo414 Jul 20 '16 at 18:26
  • Ok, how could I separately check if `currency` is valid? I want it to act in the same way as with `moneyInput`, i.e. if something like `100.00 abc` or `100.00` is entered, the user is repeatedly prompted for `currency` only, and once they enter a valid currency like `USD`, the program resumes normally. I tried using another do-while loop inside to check currency, and this worked except for when a blank input was entered, which is basically the same problem I had at the beginning of this post (here is my code: http://pastebin.com/raw/CT0qjBPk and program screenshots: http://imgur.com/a/mjfaL). – A is for Ambition Jul 21 '16 at 02:38
  • I also tried using a second set of nested Scanners followed by another `try` block, all inside a `while` loop (pretty much mirroring your first solution). Everything worked fine, including being able to respond to a blank input when re-prompting for `currency`, but I ran into some new problems (see: http://pastebin.com/kEUQSEFF and http://imgur.com/a/W6WWB). I am continually working on this so if I fix some of these errors, I'll be sure to update. Thanks so much for your help – A is for Ambition Jul 21 '16 at 03:32
  • If you're trying to work with money I'd strongly suggest the [Joda-Money](http://www.joda.org/joda-money/) library - among other things it has a regularly updated [database of currencies](https://github.com/JodaOrg/joda-money/blob/master/src/main/resources/org/joda/money/MoneyData.csv) so that you don't have to do validations yourself. The behavior you're describing (prompt for both, but validate and re-prompt individually) is non-trivial, and would be best discussed in a separate followup question rather than in comments. If you link me to it I'd be happy to take a swing at it. – dimo414 Jul 21 '16 at 04:04
  • Good idea, and thanks for taking the time to help. Here's the updated follow-up question: http://stackoverflow.com/q/38557212/5606164 It's already been answered since it's a simple question, but please give it a look if you can. – A is for Ambition Jul 24 '16 at 22:18