Как проверить, если и какой тип числа представляет строка? - PullRequest
7 голосов
/ 28 января 2012

Как я могу проверить, представляет ли строка длинную, двойную или просто обычную строку? Мне нужно сделать это, потому что это значение должно быть проиндексировано в базе данных в соответствии с его типом. В настоящее время я делаю это, пытаясь разобрать строку и проверяя исключения, но поскольку код вызывается очень часто, мне интересно, есть ли более эффективный способ сделать это. Мой код в настоящее время выглядит так:

String value = ...;
// For example, could be "213678", "654.1236781", or "qwerty12345"

try {
    Long longValue = Long.parseLong(value);
    // Index 'longValue' in the database
} catch (NumberFormatException parseLongException) {
    try {
        Double doubleValue = Double.parseDouble(value);
        // Index 'doubleValue' in the database
    } catch (NumberFormatException parseDoubleException) {
        // Index 'value' in the database
    }
}

EDIT:

Я только что провел быстрое тестирование производительности в соответствии с предложением @ user949300 использовать шаблоны регулярных выражений, и оно показало себя немного лучше, чем код обработки исключений, приведенный выше. Вот код на тот случай, если кто-то найдет его полезным:

Pattern longPattern = Pattern.compile("^[-+]?[0-9]+$");
Pattern doublePattern = Pattern.compile("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$");

// Check for long regex pattern before the double regex pattern
// since the former is a strict subset of the latter
if (longPattern.matcher(value).matches()) {
    // Perform indexing for long in the database
} else if (doublePattern.matcher(value).matches()) {
    // Perform indexing for double in the database
} else {
    // Perform indexing for string in the database
}

Вот результаты сравнительного анализа для проверки 50 000 записей, где приблизительная разбивка типов составляет 50% длинных, 10% двойных, 40% строк (представляющих рабочую нагрузку, которую обрабатывает мое приложение):

--- Exception handling code ---
STRING - actual: 19861, found: 19861
DOUBLE - actual: 4942, found: 4942
LONG - actual: 25197, found: 25197
Time taken: 2561 ms

--- Regex pattern matching code ---
STRING - actual: 19861, found: 19861
DOUBLE - actual: 4942, found: 4942
LONG - actual: 25197, found: 25197
Time taken: 1565 ms

Ответы [ 6 ]

3 голосов
/ 28 января 2012

Рассматривали ли вы регулярные выражения?

Если строка содержит что-либо кроме - (в начале) и 0-9 или., Это строка.(Обратите внимание - это игнорирует интернационализацию и научную нотацию - это проблемы?)

В противном случае, оно содержит., Это двойное число.(Ну, вы должны проверить только для одного., Но это начало)

В противном случае, это длинный.

Из паранойи, я все еще мог бы проверить на исключения, но этоможет быть быстрее.

ПРИМЕЧАНИЕ ДОБАВЛЕНО Я предполагаю, что тестирование регулярного выражения происходит быстрее, чем выбрасывание исключений из различных процедур синтаксического анализа, но на самом деле это может быть не так.Вы должны сделать несколько тестов.

2 голосов
/ 28 января 2012

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

Если у вас есть более 3-х возможных типов, у вас будет глубокое и уродливое try-catch гнездо, но технически это будет быстрее, чем если бы вы разбивали каждую попытку разбора на собственный метод;компромисс здесь заключается в том, хотите ли вы ясности кода или более быстрого выполнения - звучит так, как если бы вы хотели последнее.

1 голос
/ 28 января 2012

Одна из возможностей - java.io.StreamTokenizer:

Reader r = new StringReader(value);
StreamTokenizer st = new StreamTokenizer(r);
int tokenType = st.nextToken();
double number;
String word;
switch (tokenType) {
    case StreamTokenizer.TT_NUMBER: // it's a number
         number = st.nval; break;
    case StreamTokenizer.TT_WORD: // it's a string
         word = st.sval; break;
}

Хотя это может быть довольно сложно использовать.

1 голос
/ 28 января 2012

Ваш код выглядит хорошо.

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

Не пытайтесь оптимизировать свой код перед профилированием. Особенно в таких языках, как Java.

1 голос
/ 28 января 2012

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

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

Не забывайте минус, если это возможно в ваших данных ...

Двойные сложнее, потому что 654.1236871 является действительным, но 654.12.36.87...1 нет, хотя они содержат одинаковый набор символов. Так что полный разбор, вероятно, необходим.

0 голосов
/ 28 января 2012

Если вам не нужно беспокоиться о том, что ваш Longs отрицательный, вы, вероятно, можете использовать NumberUtils.isDigits() и NumberUtils.isNumber() из библиотеки Apache Commons Lang.

if(NumberUtils.isDidgets(string)){
    //Index long
} else if(NumberUtils.isNumber(string)){
    //Index double
} else {
    //Index string
}
...