BigDecimal hasNextBigDecimal поведение - PullRequest
0 голосов
/ 29 октября 2018

Следующий код делает именно то, что ожидается:

import java.math.BigDecimal;
import java.util.Scanner;

public class Test2 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        BigDecimal number = new BigDecimal("0");
        System.out.print("Enter a number: ");
        try {
            number = new BigDecimal(input.next());
        }
        catch(Exception e) {
            System.out.println("Not a number.");
        }
        System.out.println(number);
    }
}
  • Правильно: сбой при вводе строки.
  • Правильно: если я наберу 1,1, произойдет сбой.
  • Правильно: Успешно, если я наберу 1.
  • Исправить: Успешно, если я наберу 1.1

Если я удаляю блок try catch и заменяю его циклом while, как показано ниже, он не делает то, что ожидается:

import java.math.BigDecimal;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter a number: ");
        while (!input.hasNextBigDecimal()) {
            System.out.print("Not a number, try again: ");
            input.next();
        }
        BigDecimal number = input.nextBigDecimal();
        System.out.println(number);
    }
}
  • Правильно: сбой при вводе строки.
  • Не правильно: если я наберу 1.1, произойдет сбой.
  • Правильно: Успешно, если я наберу 1.
  • Неправильно: Успешно, если я наберу 1,1

Вот вывод при запуске: java Test

C:\>java Test
Enter a number: 1,1
1.1

Почему он принимает запятую, но печатает точку?

Если я запускаю следующий код, он печатает: en_ZA

import java.util.Locale;

public class Test3 {
    public static void main(String[] args) {
        System.out.println(Locale.getDefault());
    }
}

Как вы можете видеть по следующей ссылке, десятичный разделитель для моей локали - это период:

http://www.localeplanet.com/java/en-ZA/index.html

Пожалуйста, сообщите, где я иду не так.

Edit:

При дальнейшем расследовании я обнаружил следующее: https://www.sadev.co.za/content/how-correctly-format-currency-south-africa

Так что технически локаль использует запятую. Унесенные разумом:)

1 Ответ

0 голосов
/ 29 октября 2018

Scanner.hasNextBigDecimal() использует группировку и десятичный разделитель по умолчанию для локали (кажется, '.' Для группировки и ',' для десятичной в вашей локали) для проверки соответствия следующего токена decimalpattern:

...
DecimalFormat df =
    (DecimalFormat)NumberFormat.getNumberInstance(locale);
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);

// These must be literalized to avoid collision with regex
// metacharacters such as dot or parenthesis
groupSeparator =   "\\" + dfs.getGroupingSeparator();
decimalSeparator = "\\" + dfs.getDecimalSeparator();
...

этот фрагмент кода относится к классу Scanner и выполняется при инициализации вашего экземпляра scanner. затем groupSeparator и decimalSeparator используются при вызове Scanner.hasNextBigDecimal() для проверки соответствия следующего токена десятичному формату.


new BigDecimal(String) использует непосредственно '.' в качестве десятичного разделителя.


Итак, если вы хотите использовать Scanner.hasNextBigDecimal() и '.' в качестве десятичного разделителя для ввода данных пользователем следует использовать метод Scanner s useLocale(Locale). Ваш код должен выглядеть так:

Scanner input = new Scanner(System.in);
input.useLocale(Locale.ENGLISH);
System.out.print("Enter a number: ");
while (!input.hasNextBigDecimal()) {
    System.out.print("Not a number, try again: ");
    input.next();
}
BigDecimal number = input.nextBigDecimal();
System.out.println(number);

программа принимает запятую в качестве допустимого значения, но затем печатает точку в качестве десятичной точки.

Снова в Scanner метод nextBigDecimal() заменяет все groupSeparator на "" и decimalSeparator на "." перед вызовом 'new BigDecimal (String)'. Код из Scanner класса:

private String processFloatToken(String token) {
    String result = token.replaceAll(groupSeparator, "");
    if (!decimalSeparator.equals("\\."))
        result = result.replaceAll(decimalSeparator, ".");

Вот почему он принимает , и печатает ..

...