Determine if a String is a number and convert in J

2020-05-17 04:52发布

I know variants of this question have been asked frequently before (see here and here for instance), but this is not an exact duplicate of those.

I would like to check if a String is a number, and if so I would like to store it as a double. There are several ways to do this, but all of them seem inappropriate for my purposes.

One solution would be to use Double.parseDouble(s) or similarly new BigDecimal(s). However, those solutions don't work if there are commas present (so "1,234" would cause an exception). I could of course strip out all commas before using these techniques, but that would seem to pose loads of problems in other locales.

I looked at Apache Commons NumberUtils.isNumber(s), but that suffers from the same comma issue.

I considered NumberFormat or DecimalFormat, but those seemed far too lenient. For instance, "1A" is formatted to "1" instead of indicating that it's not a number. Furthermore, something like "127.0.0.1" will be counted as the number 127 instead of indicating that it's not a number.

I feel like my requirements aren't so exotic that I'm the first to do this, but none of the solutions does exactly what I need. I suppose even I don't know exactly what I need (otherwise I could write my own parser), but I know the above solutions do not work for the reasons indicated. Does any solution exist, or do I need to figure out precisely what I need and write my own code for it?

15条回答
成全新的幸福
2楼-- · 2020-05-17 05:50

If you want to convert some string number which is comma separated decimal to double, you could use DecimalSeparator + DecimalFormalSymbols:

final double strToDouble(String str, char separator){
    DecimalFormatSymbols s = new DecimalFormatSymbols();
    s.setDecimalSeparator(separator);
    DecimalFormat df = new DecimalFormat();

    double num = 0;
    df.setDecimalFormatSymbols(s);
    try{
        num = ((Double) df.parse(str)).doubleValue();
    }catch(ClassCastException | ParseException ex){
        // if you want, you could add something here to 
        // indicate the string is not double
    }  
    return num;
}

well, lets test it:

    String a = "1.2";
    String b = "2,3";
    String c = "A1";
    String d = "127.0.0.1";

    System.out.println("\"" + a + "\" = " + strToDouble(a, ','));
    System.out.println("\"" + a + "\" (with '.' as separator) = " 
            + strToDouble(a, '.'));
    System.out.println("\"" + b + "\" = " + strToDouble(b, ','));
    System.out.println("\"" + c + "\" = " + strToDouble(c, ','));
    System.out.println("\"" + d + "\" = " + strToDouble(d, ','));

if you run the above code, you'll see:

"1.2" = 0.0
"1.2" (with '.' as separator) = 1.2
"2,3" = 2.3
"A1" = 0.0
"127.0.0.1" = 0.0
查看更多
我欲成王,谁敢阻挡
3楼-- · 2020-05-17 05:52

My understanding is that you want to cover Western/Latin languages while retaining as much strict interpretation as possible. So what I'm doing here is asking DecimalFormatSymbols to tell me what the grouping, decimal, negative, and zero separators are, and swapping them out for symbols Double will recognize.

How does it perform?

In the US, it rejects: "1A", "127.100.100.100" and accepts "1.47E-9"

In Germany it still rejects "1A"

It ACCEPTS "1,024.00" but interprets it correctly as 1.024. Likewise, it accepts "127.100.100.100" as 127100100100.0

In fact, the German locale correctly identifies and parses "1,47E-9"

Let me know if you have any trouble in a different locale.

import java.util.Locale;
import java.text.DecimalFormatSymbols;

public class StrictNumberFormat {

public static boolean isDouble(String s, Locale l) {
    String clean = convertLocaleCharacters(s,l);

    try {
        Double.valueOf(clean);
        return true;
    } catch (NumberFormatException nfe) {
        return false;
    }
}

public static double doubleValue(String s, Locale l) {
    return Double.valueOf(convertLocaleCharacters(s,l));
}

public static boolean isDouble(String s) {
    return isDouble(s,Locale.getDefault());
}

public static double doubleValue(String s) {
    return doubleValue(s,Locale.getDefault());
}

private static String convertLocaleCharacters(String number, Locale l) {
    DecimalFormatSymbols symbols = new DecimalFormatSymbols(l);
    String grouping = getUnicodeRepresentation( symbols.getGroupingSeparator() );
    String decimal = getUnicodeRepresentation( symbols.getDecimalSeparator() );
    String negative = getUnicodeRepresentation( symbols.getMinusSign() );
    String zero = getUnicodeRepresentation( symbols.getZeroDigit() );

    String clean = number.replaceAll(grouping, "");
    clean = clean.replaceAll(decimal, ".");
    clean = clean.replaceAll(negative, "-");
    clean = clean.replaceAll(zero, "0");

    return clean;
}

private static String getUnicodeRepresentation(char ch) {
    String unicodeString = Integer.toHexString(ch); //ch implicitly promoted to int
    while(unicodeString.length()<4) unicodeString = "0"+unicodeString;

    return "\\u"+unicodeString;
}

}
查看更多
Anthone
4楼-- · 2020-05-17 05:53

One of the easy hacks would be to use replaceFirst for String you get and check the new String whether it is a double or not. In case it's a double - convert back (if needed)

查看更多
登录 后发表回答