Определить, является ли String числом, и конвертировать в Java? - PullRequest
24 голосов
/ 04 мая 2011

Я знаю, что варианты этого вопроса часто задавались ранее (см., Например, здесь и здесь ), но это не и точно дубликат этих.

Я хотел бы проверить, является ли String число, и если да, то я бы хотел сохранить его как double.Есть несколько способов сделать это, но все они кажутся неподходящими для моих целей.

Одним из решений было бы использование Double.parseDouble(s) или аналогично new BigDecimal(s).Тем не менее, эти решения не работают, если присутствуют запятые (поэтому «1234» может вызвать исключение).Конечно, я мог бы удалить все запятые, прежде чем использовать эти методы, но, похоже, это создаст массу проблем в других локалях.

Я смотрел на Apache Commons NumberUtils.isNumber(s), но у него такая же проблема с запятой.

Я считал NumberFormat или DecimalFormat, но они казались слишком снисходительными.Например, «1A» форматируется как «1» вместо того, чтобы указывать, что это не число.Кроме того, что-то вроде «127.0.0.1» будет засчитываться как число 127 вместо того, чтобы указывать, что это не число.

Я чувствую, что мои требования не настолько экзотичны, что я первым делаю это, но ни одно из решений не делает именно то, что мне нужно.Я предполагаю, что даже я не знаю точно , что мне нужно (в противном случае я мог бы написать свой собственный анализатор), но я знаю, что вышеупомянутые решения не работают по указанным причинам.Существует ли какое-либо решение или мне нужно точно определить, что мне нужно, и написать для него собственный код?

Ответы [ 15 ]

1 голос
/ 10 февраля 2012

Если вы хотите преобразовать некоторое строковое число, разделенное запятыми, в двойное, вы можете использовать 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;
}

, давайте проверим его:

    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, ','));

если вы запустите приведенный выше код, вы увидите:

"1.2" = 0.0
"1.2" (with '.' as separator) = 1.2
"2,3" = 2.3
"A1" = 0.0
"127.0.0.1" = 0.0
1 голос
/ 07 февраля 2012

Одним из простых способов взлома было бы использовать replaceFirst для получаемой строки и проверить новую строку, является ли она двойной или нет.Если это двойное - конвертировать обратно (при необходимости)

1 голос
/ 05 мая 2011

Я думаю, что у вас есть многоэтапный процесс, который можно обработать здесь с помощью индивидуального решения, если вы не готовы принять результаты DecimalFormat или уже связанные ответы.

1) Определите десятичный и группирующий разделители. Возможно, вам потребуется определить другие символы формата (например, индикаторы научной нотации).

http://download.oracle.com/javase/1.4.2/docs/api/java/text/DecimalFormat.html#getDecimalFormatSymbols()

2) Уберите все символы группировки (или создайте регулярное выражение, будьте осторожны с другими символами, которые вы принимаете, такими как десятичные, если вы это делаете). Затем удалите первый десятичный символ. Другие символы по мере необходимости.

3) Звоните parse или isNumber.

1 голос
/ 04 мая 2011

Если вы установите правильную локаль, встроенная parseDouble будет работать с запятыми.Пример: здесь .

0 голосов
/ 11 февраля 2012

Этот код должен обрабатывать большинство входных данных, за исключением IP-адресов, где все группы цифр в трех (например, 255.255.255.255 допустимо, но не 255.1.255.255).Он также не поддерживает научную запись

. Он будет работать с большинством вариантов разделителей (",", "." Или пробел).Если обнаружено более одного разделителя, первым считается разделитель тысяч, с дополнительными проверками (допустимость и т. Д.)

Edit: prevDigit используется для проверки того, что число использует тысячуразделители правильно.Если существует более одной группы тысяч, все, кроме первой, должны быть в группах по 3. Я изменил код, чтобы сделать его более понятным, чтобы «3» было не магическим числом, а константой.

Редактировать 2: Я не возражаю против отрицательных голосов, но может кто-нибудь объяснить, в чем проблема?

/* A number using thousand separator must have
   groups of 3 digits, except the first one.
   Numbers following the decimal separator can
   of course be unlimited. */
private final static int GROUP_SIZE=3;

public static boolean isNumber(String input) {
    boolean inThousandSep = false;
    boolean inDecimalSep = false;
    boolean endsWithDigit = false;
    char thousandSep = '\0';
    int prevDigits = 0;

    for(int i=0; i < input.length(); i++) {
        char c = input.charAt(i);

        switch(c) {
            case ',':
            case '.':
            case ' ':
                endsWithDigit = false;
                if(inDecimalSep)
                    return false;
                else if(inThousandSep) {
                    if(c != thousandSep)
                        inDecimalSep = true;
                    if(prevDigits != GROUP_SIZE)
                        return false; // Invalid use of separator
                }
                else {
                    if(prevDigits > GROUP_SIZE || prevDigits == 0)
                        return false;
                    thousandSep = c;
                    inThousandSep = true;
                }
                prevDigits = 0;
                break;

            default:
                if(Character.isDigit(c)) {
                    prevDigits++;
                    endsWithDigit = true;
                }
                else {
                    return false;
                }
        }
    }
    return endsWithDigit;
}

Тестовый код:

public static void main(String[] args) {
    System.out.println(isNumber("100"));               // true
    System.out.println(isNumber("100.00"));            // true
    System.out.println(isNumber("1,5"));               // true
    System.out.println(isNumber("1,000,000.00."));     // false
    System.out.println(isNumber("100,00,2"));          // false
    System.out.println(isNumber("123.123.23.123"));    // false
    System.out.println(isNumber("123.123.123.123"));   // true       
}
...