I have the below requirement:
input String = "1.00000" should be converted to int ( because actually no fraction at all )
input String = "1" should be converted to int 1
however, input String ="1.0001" should be an Exception (because has fraction )
Previously I was assuming Integer.valueOf("1.00000") should return an integer.However it returns NumberFormatException.
any Java library that can solve this, or at least can check if 1.0000 is actually an integer so I can safely parse as Double and cast it to int?
BigDecimal class
Java itself has a library that does exactly this: BigDecimal
Example:
private static int parse(String input) throws ArithmeticException {
return new BigDecimal(input).intValueExact();
}
The intValueExact method throws an ArithmeticException if the BigDecimal object has a non-zero fractional part.
One way to tackle the problem is, you can use regex to first ensure that the input exactly matches the format of a number with fractional part being optional and if at all then all being zeroes and if yes, then go ahead and parse it else throw Exception or whatever you want to do. Check this Java code,
List<String> list = Arrays.asList("1.00000","1","1.0001");
Pattern intPat = Pattern.compile("(\\d+)(\\.0+)?");
list.forEach(x -> {
Matcher m = intPat.matcher(x);
if (m.matches()) {
int num = Integer.parseInt(m.group(1));
System.out.println(num);
} else {
System.out.println(x +" is not a pure Integer"); // Throw exception or whatever you like
}
});
Outputs,
1
1
1.0001 is not an Integer
Also, as long as the numbers you are working with are confined within integer range, its all good but to ensure the numbers don't cross integer limit, you may want to use a different quantifier.
Another alternate simpler solution you can go for is, parse the number as double and compare it with its int form and if both are equal then it is a pure integer else not. I'll prefer this than my first solution using regex. Java code,
List<String> list = Arrays.asList("1.00000","1","1.0001");
list.forEach(x -> {
double num = Double.parseDouble(x);
if (num == (int)num) {
System.out.println((int)num);
} else {
System.out.println(x + " is not a pure Integer");
}
Prints,
1
1
1.0001 is not a pure Integer
Related
So at the input, a string contains different types of data. Basically, this is a string, int, and float. I check types through Float.parseFloat() and Integer.parseInt(). the Integer.parseInt() works correctly, yet Float.parseFloat() includes all the digits that are checked. How to avoid this?
The problem is that when checking for the float type, it also considers ints (output):
Hello my 50 name 4.5 is James, I 20 years old and I have 5.7 coins
Float 4
Int 2
String 12
public class TypeCountString {
public static void countTypeInString(String string){
String[] value = string.split(" ");
float ifFloat;
int ifInt;
String ifString;
int counter = 0;
// count Floats
for (String i: value){
try{
ifFloat = Float.parseFloat(i);
counter++;
continue;
} catch (NumberFormatException e){
}
}
System.out.println("Float " + counter);
//count ints
counter = 0;
for (String i: value){
try{
ifInt = Integer.parseInt(i);
counter++;
continue;
} catch (NumberFormatException e){
}
}
System.out.println("Int " + counter);
//counts strings
String stringOfStrings = string.replaceAll("[0-9.]","");
stringOfStrings = stringOfStrings.replaceAll(" "," ");
String[] value2 = stringOfStrings.split(" ");
System.out.println("String " + value2.length);
}
public static void main(String[] args) {
String string = "Hello my 50 name 4.5 is James, I 20 years old and i have 5.7 coins";
System.out.println(string);
countTypeInString(string);
}
}
As Integer is a sub-type of Float, Float will count in Integers into your float count. If you solely just want the number of float that is not integer, just take float count minus integer count.
Some tokens (like "50") are parseable both as integer and float. Your code gives both of them a chance, even though your intention is to only count each token as one type or the other (so count "50" as an integer, not as a float).
A simple fix could be to modify your float check to be:
make sure that i can pass Float.parseFloat(), and
confirm that i contains a decimal point
That code edit could look like this:
Float.parseFloat(i);
if (i.contains(".")) {
counter++;
continue;
}
Or, as a more involved alternative, instead of checking all input tokens for floats, then re-checking all input tokens for integers, I would take a different approach.
First, I would change the code to stop checking the entire input string repeatedly. This is helpful for 2nd part below, but also cleans up an unnecessary inefficiency of re-checking tokens even if you've already identified their data type. If you had a large number of tokens, it's wasteful to try parsing all of them as float, then try all of them as integer, then try a third time to count plain strings.
Second, since you would be checking (and counting) each token only one time, you could:
check for integer (before float) – if it passes Integer.parseInt(), increment that counter and move on to the next token
if it doesn't parse as integer, make an attempt to parse that token as float – just Float.parseFloat() without looking for decimal; if it worked then bump counter, move to next token
if it doesn't parse as integer or float, you know it's a String so simply bump the counter, then next token
As others have already said, since int is a subset of float, you can always parse a string representing an int as a float, while the opposite is not true. In fact, if the parsing string contains a decimal separator and you attempt to parse it as an int, you'll get a NumberFormatException.
However, you could use this behavior to your advantage and try to parse each string to the data type with minimal representation (int) and gradually increasing it as the parsing fails, until you reach the most generic representation (String). In your case, you could start to parse each string to int, if it fails proceed to float and ultimately to String if the float parsing doesn't work either.
Here is a possible implementation:
public class Main {
public static void main(String[] args) {
countTypesInString();
}
public static void countTypesInString() {
String str = "Hello my 50 name 4.5 is James, I 20 years old and i have 5.7 coins";
String[] vetStr = str.split("\\s+");
List<Integer> listInt = new ArrayList<>();
List<Float> listFloat = new ArrayList<>();
List<String> listStr = new ArrayList<>();
for (String s : vetStr) {
try {
listInt.add(Integer.parseInt(s));
} catch (NumberFormatException exInt) {
try {
listFloat.add(Float.parseFloat(s));
} catch (NumberFormatException exFloat) {
listStr.add(s);
}
}
}
System.out.println("Number of ints: " + listInt.size() + " => " + listInt);
System.out.println("Number of floats: " + listFloat.size() + " => " + listFloat);
System.out.println("Number of strings: " + listStr.size() + " => " + listStr);
}
}
Output
Number of ints: 2 => [50, 20]
Number of floats: 2 => [4.5, 5.7]
Number of strings: 12 => [Hello, my, name, is, James,, I, years, old, and, i, have, coins]
Side note: for real number representation, the double type is a better choice. In fact, by default Java treats every literal representing a real number as a double rather than a float. In the snippet above I've kept using float to maintain my code as close as possible to the original.
I am loath to reinvent this bicycle and am hoping to be shown the tried and true way to handle this problem.
I'm collecting numeric values from users via some String interface (text input for instance).
I want to make sure that regardless of what type of memory space I'm using for collecting this info, I don't allow the user to enter a number that exceeds this value.
My intuition tells me that the only way to do this is to actually measure the string length of the max value... such as...
if( (userInput + "").length() > (Integer.MAX_VALUE + "").length()){
//user has entered too many digits for an Integer to hold.
}
But this looks ugly to me and I'm guessing there's a cleaner way to handle this.
When you get the userInput for the first time you should verify that what the user enters is valid and if it is, then Integer.parseInt() will work. If it's not valid, i.e. a value greater than Integer.MAX_VALUE, it will throw an exception.
The behavior you're describing leads to using the catch as flow control which is not a good design...
BAD:
try{
Integer.parseInt(max);
//do something with the integer
}catch (NumberFormatException e)
{
//user has entered too many digits for an Integer to hold.
userInput = Integer.MAX_VALUE + "";
}
The constructor of Integer would detect that for you, by throwing a NumberFormatException if the user input is either out of range or not really an integer. See the following test program example:
public class UserInputBigInteger {
public static void main(String[] args) {
String[] inputStrings = {
String.valueOf(Integer.MAX_VALUE)
, String.valueOf(Integer.MAX_VALUE)+"0" // x10
, String.valueOf(Integer.MAX_VALUE)+"a" // not an integer
};
for (String inputString : inputStrings) {
try {
Integer inputInteger = new Integer(inputString);
final int MAX = Integer.MAX_VALUE;
System.out.format("userInput %s is within range %,d%n"
, inputString, MAX);
} catch (NumberFormatException ex) {
System.out.format("userInput does not appear to be valid interger: %s%n"
, ex.getMessage()); }
}
}
}
The output would be:
userInput 2147483647 is within range 2,147,483,647
userInput does not appear to be an interger: For input string: "21474836470"
userInput does not appear to be an interger: For input string: "2147483647a"
You could also try this to get the bit information:
BigInteger userInputCheck=new BigInteger(userInput);
if(userInputCheck.bitLength()>31){
//error : cannot convert the input to primitive int type
}
EDIT
If you are using Apache Commons there is a method createNumber# in the NumberUtils utility class.
From the documentation:
...it starts trying to create successively larger types from the type specified
until one is found that can represent the value...
Source Code for the above method
If there is a string variable containing a number string , is there any function to identify whether the value can be converted to int, or double, or anything else?? i need the function name in java
String sent3 = "123";
System.out.println(sent3.matches("[0-9]+"));
System.out.println(sent3.matches("[0-9]+\\.[0-9]+"));// for double
output :- true
If the output is true then it can be converted into int.
Follow this link for more regex
My solution involves trying to parse the string into the various types, and then looking for exceptions that Java might throw. This is probably an inefficient solution, but the code is relatively short.
public static Object convert(String tmp)
{
Object i;
try {
i = Integer.parseInt(tmp);
} catch (Exception e) {
try {
i = Double.parseDouble(tmp);
} catch (Exception p) {
return tmp; // a number format exception was thrown when trying to parse as an integer and as a double, so it can only be a string
}
return i; // a number format exception was thrown when trying to parse an integer, but none was thrown when trying to parse as a double, so it is a double
}
return i; // no numberformatexception was thrown so it is an integer
}
You can then use this function with the following lines of code:
String tmp = "3"; // or "India" or "3.14"
Object tmp2 = convert(tmp);
System.out.println(tmp2.getClass().getName());
You can convert the function into inline code to test if it is an integer, for example:
String tmp = "3";
Object i = tmp;
try {
i = Integer.parseInt(tmp);
} catch (Exception e) {
// do nothing
}
I was a little sloppy and tried to catch normal Exceptions, which is rather generic - I suggest you use "NumberFormatException" instead.
String test = "1234";
System.out.println(test.matches("-?\\d+"));
test = "-0.98";
System.out.println(test.matches("-?\\d+\\.\\d+"));
The first one matches (ie prints true) any integer (not int, integer) with an optional - sign in front. The second one matches any double value with an optional - sign, at least one digit before a required decimal point, and at least on digit following the decimal point.
Also, the function name is String.matches and it uses regular expressions.
I using:
String str="300.0";
System.out.println(Integer.parseInt(str));
return an exception:
Exception in thread "main" java.lang.NumberFormatException: For input string: "300.0"
How can I parse this String to int?
thanks for help :)
Here's how you do it:
String str = "300.0";
System.out.println((int) Double.parseDouble(str));
The reason you got a NumberFormatException is simply that the string ("300.00", which is a floating point number) could not be parsed as an integer.
It may be worth mentioning, that this solution prints 300 even for input "300.99". To get a proper rounding, you could do
System.out.println(Math.round(Double.parseDouble("300.99"))); // prints 301
I am amazed no one has mentioned BigDecimal.
It's really the best way to convert string of decimal's to int.
Josuha Bloch suggest using this method in one of his puzzlers.
Here is the example run on Ideone.com
class Test {
public static void main(String args[]) {
try {
java.math.BigDecimal v1 = new java.math.BigDecimal("30.0");
java.math.BigDecimal v2 = new java.math.BigDecimal("30.00");
System.out.println("V1: " + v1.intValue() + " V2: " + v2.intValue());
} catch(NumberFormatException npe) {
System.err.println("Wrong format on number");
}
}
}
You should parse it to double first and then cast it to int:
String str="300.0";
System.out.println((int)(Double.parseDouble(str)));
You need to catch NumberFormatExceptions though.
Edit: thanks to Joachim Sauer for the correction.
You can use the Double.parseDouble() method to first cast it to double and afterwards cast the double to int by putting (int) in front of it. You then get the following code:
String str="300.0";
System.out.println((int)Double.parseDouble(str));
Integer.parseInt() has to take a string that's an integer (i.e. no decimal points, even if the number is equivalent to an integer. The exception you're getting there is essentially saying "you've told me this number is an integer, but this string isn't in a valid format for an integer!"
If the number contains a decimal component, then you'll need to use Double.parseDouble(), which will return a double primitive. However, since you're not interested in the decimal component you can safely drop it by just casting the double to an int:
int num = (int)Double.parseDouble(str);
Note however that this will just drop the decimal component, it won't round the number up at all. So casting 1.2, 1.8 or 1.999999 to an int would all give you 1. If you want to round the number that comes back then use Math.round() instead of just casting to an int.
In Europe decimals are separated with ',' and we use optional '.' to separate thousands. I allow currency values with:
US-style 123,456.78 notation
European-style 123.456,78 notation
I use the next regular expression (from RegexBuddy library) to validate the input. I allow optional two-digits fractions and optional thousands separators.
^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\.[0-9]{0,2})?|(?:\.[0-9]{3})*(?:,[0-9]{0,2})?)$
I would like to parse a currency string to a float. For example
123,456.78 should be stored as 123456.78
123.456,78 should be stored as 123456.78
123.45 should be stored as 123.45
1.234 should be stored as 1234
12.34 should be stored as 12.34
and so on...
Is there an easy way to do this in Java?
public float currencyToFloat(String currency) {
// transform and return as float
}
Use BigDecimal instead of Float
Thanks to everyone for the great answers. I have changed my code to use BigDecimal instead of float. I will keep previous part of this question with float to prevent people from doing the same mistakes I was gonna do.
Solution
The next code shows a function which transforms from US and EU currency to a string accepted by BigDecimal(String) constructor. That it is to say a string with no thousand separator and a point for fractions.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestUSAndEUCurrency {
public static void main(String[] args) throws Exception {
test("123,456.78","123456.78");
test("123.456,78","123456.78");
test("123.45","123.45");
test("1.234","1234");
test("12","12");
test("12.1","12.1");
test("1.13","1.13");
test("1.1","1.1");
test("1,2","1.2");
test("1","1");
}
public static void test(String value, String expected_output) throws Exception {
String output = currencyToBigDecimalFormat(value);
if(!output.equals(expected_output)) {
System.out.println("ERROR expected: " + expected_output + " output " + output);
}
}
public static String currencyToBigDecimalFormat(String currency) throws Exception {
if(!doesMatch(currency,"^[+-]?[0-9]{1,3}(?:[0-9]*(?:[.,][0-9]{0,2})?|(?:,[0-9]{3})*(?:\\.[0-9]{0,2})?|(?:\\.[0-9]{3})*(?:,[0-9]{0,2})?)$"))
throw new Exception("Currency in wrong format " + currency);
// Replace all dots with commas
currency = currency.replaceAll("\\.", ",");
// If fractions exist, the separator must be a .
if(currency.length()>=3) {
char[] chars = currency.toCharArray();
if(chars[chars.length-2] == ',') {
chars[chars.length-2] = '.';
} else if(chars[chars.length-3] == ',') {
chars[chars.length-3] = '.';
}
currency = new String(chars);
}
// Remove all commas
return currency.replaceAll(",", "");
}
public static boolean doesMatch(String s, String pattern) {
try {
Pattern patt = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
Matcher matcher = patt.matcher(s);
return matcher.matches();
} catch (RuntimeException e) {
return false;
}
}
}
To answer a slightly different question: don't use the float type to represent currency values. It will bite you. Use a base-10 type instead, like BigDecimal, or an integer type like int or long (representing the quantum of your value - penny, for example, in US currency).
You will not be able to store an exact value - 123.45, say, as a float, and mathematical operations on that value (such as multiplication by a tax percentage) will produce rounding errors.
Example from that page:
float a = 8250325.12f;
float b = 4321456.31f;
float c = a + b;
System.out.println(NumberFormat.getCurrencyInstance().format(c));
// prints $12,571,782.00 (wrong)
BigDecimal a1 = new BigDecimal("8250325.12");
BigDecimal b1 = new BigDecimal("4321456.31");
BigDecimal c1 = a1.add(b1);
System.out.println(NumberFormat.getCurrencyInstance().format(c1));
// prints $12,571,781.43 (right)
You don't want to muck with errors when it comes to money.
With respect to the original question, I haven't touched Java in a little while, but I know that I'd like to stay away from regex to do this kind of work. I see this recommended; it may help you. Not tested; caveat developer.
try {
String string = NumberFormat.getCurrencyInstance(Locale.GERMANY)
.format(123.45);
Number number = NumberFormat.getCurrencyInstance(locale)
.parse("$123.45");
// 123.45
if (number instanceof Long) {
// Long value
} else {
// too large for long - may want to handle as error
}
} catch (ParseException e) {
// handle
}
Look for a locale with rules that match what you expect to see. If you can't find one, use multiple sequentially, or create your own custom NumberFormat.
I'd also consider forcing users to enter values in a single, canonical format. 123.45 and 123.456 look way too similar for my tastes, and by your rules would result in values that differ by a factor of 1000. This is how millions are lost.
As a generalized solution you can try
char[] chars = currency.toCharArray();
chars[currency.lastIndexOf(',')] = '.';
currency = new String(chars);
instead of
if(currency.length()>=3) {
char[] chars = currency.toCharArray();
if(chars[chars.length-2] == ',') {
chars[chars.length-2] = '.';
} else if(chars[chars.length-3] == ',') {
chars[chars.length-3] = '.';
}
currency = new String(chars);
}
so that fractional part can be of any length.
Try this.............
Locale slLocale = new Locale("de","DE");
NumberFormat nf5 = NumberFormat.getInstance(slLocale);
if(nf5 instanceof DecimalFormat) {
DecimalFormat df5 = (DecimalFormat)nf5;
try {
DecimalFormatSymbols decimalFormatSymbols = DecimalFormatSymbols.getInstance(slLocale);
decimalFormatSymbols.setGroupingSeparator('.');
decimalFormatSymbols.setDecimalSeparator(',');
df5.setDecimalFormatSymbols(decimalFormatSymbols);
df5.setParseBigDecimal(true);
ParsePosition pPosition = new ParsePosition(0);
BigDecimal n = (BigDecimal)df5.parseObject("3.321.234,56", pPosition);
System.out.println(n);
}catch(Exception exp) {
exp.printStackTrace();
}
}
A quick a dirty hack could be:
String input = input.replaceAll("\.,",""); // remove *any* , or .
long amount = Long.parseLong(input);
BigDecimal bd = BigDecimal.valueOf(amount).movePointLeft(2);
//then you could use:
bd.floatValue();
//but I would seriously recommended that you don't use floats for monetary amounts.
Note this will only work if the input is in the form ###.00, ie with exactly 2 decimal places. For example input == "10,022" will break this rather naive code.
Alternative is to use the BigDecimal(String) constructor, but you'll need to convert those euro style numbers to use '.' as the decimal separator, in addition to removing the thousand separators for both.