Reputation: 125
The following code does exactly what is expected:
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);
}
}
If I remove the try catch block and replace it with a while loop, as follows, it does not do what is expected:
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);
}
}
Here is the output when I run: java Test
C:\>java Test
Enter a number: 1,1
1.1
Why would it accept a comma, but print a period?
If I run the following code, it prints out: en_ZA
import java.util.Locale;
public class Test3 {
public static void main(String[] args) {
System.out.println(Locale.getDefault());
}
}
As you can see at the following link, the decimal separator for my locale is the period:
http://www.localeplanet.com/java/en-ZA/index.html
Please advise where I am going wrong.
Edit:
On further investigation, I found the following: https://www.sadev.co.za/content/how-correctly-format-currency-south-africa
So the locale technically does use the comma. Mind blown :)
Upvotes: 3
Views: 269
Reputation: 2734
Scanner.hasNextBigDecimal()
uses default locale's grouping and decimal separator (it seems '.' for grouping and ',' for decimal in your locale) to validate if next token matches 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();
...
this piece of code is from Scanner
class and executed when initializing your scanner
instance. then groupSeparator
and decimalSeparator
are used when call to Scanner.hasNextBigDecimal()
for checking if the next token matches a decimal format.
new BigDecimal(String)
uses directly '.' as the decimal separator.
So, if you want to use Scanner.hasNextBigDecimal()
and '.' as decimal separator for user inputs then you should use Scanner
s useLocale(Locale)
method. Your code shold look like:
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);
the program accepts a comma as valid input, but then prints a period as the decimal point.
Again in Scanner
's nextBigDecimal()
method replaces all groupSeparator
's with ""
and decimalSeparator
with "."
before calling 'new BigDecimal(String)'. The piece of code from Scanner
class is:
private String processFloatToken(String token) {
String result = token.replaceAll(groupSeparator, "");
if (!decimalSeparator.equals("\\."))
result = result.replaceAll(decimalSeparator, ".");
This is why it is accepting ,
and printing .
.
Upvotes: 2