How to convert formatted strings to float?
-
12-06-2021 - |
Frage
I have a list of strings and I'd like to convert them to float if a pattern is matched.
Here are some values and the expected result:
1000 -> 1000.0
1.000 -> 1000.0
1.000,000 -> 1000.0
-1.000,000 -> -1000.0
9,132 -> 9.132
1,000.00 -> invalid
30.10.2010 -> invalid
1,000.000,00 -> invalid
I tried this code for checking if a number is valid, but the pattern is never matched:
Pattern pattern = Pattern.compile("#.###,###");
for(String s : list){
Matcher m = pattern.matcher(s);
if(m.matches()){
//convert
}
}
Beside that I've tried to use this code:
DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance();
for(String s : list){
try {
Number num = df.parse(s);
//..
} catch (ParseException e) {
}
}
The problem with this code is, that no pattern-based validation is performed. E.g. a date like 2012/05/30
is converted to 2012
.
So how can I either define a valid pattern or configure DecimalFormat
for my needs?
Lösung
I think this is what you want. The comments should explain it.
@Test
public void testAllValues() {
testValue("1000", "1000");
testValue("1.000,000", "1000");
testValue("-1.000,000", "-1000");
testValue("9,132", "9.132");
testValue("1,000.00", null);
testValue("30.10.2010", null);
testValue("1,000.000,00", null);
}
private void testValue(String germanString, String usString) {
BigDecimal germanDecimal = (BigDecimal) parse(germanString);
if (usString != null) {
BigDecimal usDecimal = new BigDecimal(usString);
assertEquals("German " + germanString + " did not equal US " + usString, 0, germanDecimal.compareTo(usDecimal));
} else {
assertEquals("German " + germanString + " should not have been pareseable", null, germanDecimal);
}
}
public BigDecimal parse(String s) {
// Patch because parse doesn't enforce the number of digits between the
// grouping character (dot).
if (!Pattern.matches("[^.]*(\\.\\d{3})*[^.]*", s)) {
return null;
}
DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(Locale.GERMANY);
df.setParseBigDecimal(true);
// Have to use the ParsePosition API or else it will silently stop
// parsing even though some of the characters weren't part of the parsed
// number.
ParsePosition position = new ParsePosition(0);
BigDecimal parsed = (BigDecimal) df.parse(s, position);
// getErrorIndex() doesn't seem to accurately reflect errors, but
// getIndex() does reflect how far we successfully parsed.
if (position.getIndex() == s.length()) {
return parsed;
} else {
return null;
}
}
Andere Tipps
The Pattern
class works with regular expressions. You probably want this:
Pattern pattern = Pattern.compile("-?\d\.\d{1,3}(,\d{1,3})?");
You probably want to tune this regex depending on exactly what formats you want or don't want to match.
Try
System.out.println("1,000.000,00".matches("^[+-]?\\d+(\\.\\d{3})*(,\\d+)?"));
I am not sure if your number can start with + so added it just in case. Also don't know if 0100000.000.000,1234 should be valid. If not tell why and regex will be corrected.
If the pattern is the comma try:
String[] splitted = string.split(",")
If size of splitted > 2
--> invalid.
If splitted.size == 2 && splitted[1].split(".") > 0
--> invalid also.
If the format is fine --> remove all points, replace comma with point, parse string after comma into int and connect the pieces.
A very simple approach but it works...