1. введение
Часто при работе с String s нам нужно выяснить, является ли String допустимым числом или нет.
В этом уроке мы рассмотрим несколько способов определить, является ли данная строка числовой , сначала с помощью простой Java, затем регулярных выражений и, наконец, с помощью внешних библиотек.
Как только мы закончим обсуждение различных реализаций, мы будем использовать тесты, чтобы получить представление о том, какие методы являются оптимальными.
Дальнейшее чтение:
Преобразования строк Java
Руководство По API Регулярных Выражений Java
Понимание исключения NumberFormatException в Java
2. Предварительные условия
Давайте начнем с некоторых предварительных условий, прежде чем перейдем к основному содержанию.
В последней части этой статьи мы будем использовать внешнюю библиотеку Apache Commons, чтобы добавить ее зависимость в ваш pom.xml :
org.apache.commons commons-lang3 3.11
Последнюю версию этой библиотеки можно найти на Maven Central .
3. Использование простой Java
Возможно, самый простой и надежный способ проверить, является ли строка числовой или нет,-это проанализировать ее с помощью встроенных методов Java:
- Integer.parseInt(Строка)
- Float.parseFloat(Строка)
- Double.parseDouble(Строка)
- Long.parseLong(Строка)
- новый BigInteger(Строка)
Если эти методы не вызывают никакого NumberFormatException , то это означает, что синтаксический анализ прошел успешно и Строка является числовой:
public static boolean isNumeric(String strNum) { if (strNum == null) { return false; } try { double d = Double.parseDouble(strNum); } catch (NumberFormatException nfe) { return false; } return true; }
Давайте посмотрим на этот метод в действии:
assertThat(isNumeric("22")).isTrue(); assertThat(isNumeric("5.05")).isTrue(); assertThat(isNumeric("-200")).isTrue(); assertThat(isNumeric("10.0d")).isTrue(); assertThat(isNumeric(" 22 ")).isTrue(); assertThat(isNumeric(null)).isFalse(); assertThat(isNumeric("")).isFalse(); assertThat(isNumeric("abc")).isFalse();
В нашем методе IsNumeric() мы просто проверяем значения типа Double ; однако мы также можем изменить этот метод для проверки Integer , Float , Long и больших чисел, используя любой из методов синтаксического анализа, которые мы использовали ранее.
Эти методы также обсуждаются в статье Преобразования строк Java.
4. Использование Регулярных Выражений
Теперь давайте использовать регулярное выражение -?\d+(\.\d+)? для сопоставления числовых Строк , состоящих из положительного или отрицательного целого числа и поплавков.
Само собой разумеется, что мы определенно можем изменить это регулярное выражение, чтобы идентифицировать и обрабатывать широкий спектр правил. Здесь все будет просто.
Давайте разберем это регулярное выражение и посмотрим, как оно работает:
- -? – эта часть определяет, является ли данное число отрицательным, тире ” – “ищет тире буквально и знак вопроса” ? ” отмечает свое присутствие как необязательное
- \d+ – при этом выполняется поиск одной или нескольких цифр
- (\.\d+)? – эта часть регулярного выражения предназначена для идентификации чисел с плавающей запятой. Здесь мы ищем одну или несколько цифр, за которыми следует точка. Знак вопроса, в конце концов, означает, что эта полная группа является необязательной.
Регулярные выражения-это очень широкая тема. Чтобы получить краткий обзор, ознакомьтесь с нашим учебником по API регулярных выражений Java .
А пока давайте создадим метод, используя приведенное выше регулярное выражение:
private Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); public boolean isNumeric(String strNum) { if (strNum == null) { return false; } return pattern.matcher(strNum).matches(); }
Теперь давайте рассмотрим некоторые утверждения для приведенного выше метода:
assertThat(isNumeric("22")).isTrue(); assertThat(isNumeric("5.05")).isTrue(); assertThat(isNumeric("-200")).isTrue(); assertThat(isNumeric(null)).isFalse(); assertThat(isNumeric("abc")).isFalse();
5. Использование Apache Commons
В этом разделе мы обсудим различные методы, доступные в библиотеке Apache Commons.
5.1. NumberUtils.isCreatable(Строка)
NumberUtils из Apache Commons предоставляет статический метод NumberUtils.isCreatable(String) , который проверяет, является ли строка допустимым номером Java или нет.
Этот метод принимает:
- Шестнадцатеричные числа, начинающиеся с 0x или 0X
- Восьмеричные числа, начинающиеся с 0
- Научная нотация (например, 1.05 e-10)
- Числа, помеченные классификатором типа (например, 1L или 2.2 d)
Если предоставленная строка является null или пустой/пустой , то она не считается числом, и метод вернет false .
Давайте проведем несколько тестов, используя этот метод:
assertThat(NumberUtils.isCreatable("22")).isTrue(); assertThat(NumberUtils.isCreatable("5.05")).isTrue(); assertThat(NumberUtils.isCreatable("-200")).isTrue(); assertThat(NumberUtils.isCreatable("10.0d")).isTrue(); assertThat(NumberUtils.isCreatable("1000L")).isTrue(); assertThat(NumberUtils.isCreatable("0xFF")).isTrue(); assertThat(NumberUtils.isCreatable("07")).isTrue(); assertThat(NumberUtils.isCreatable("2.99e+8")).isTrue(); assertThat(NumberUtils.isCreatable(null)).isFalse(); assertThat(NumberUtils.isCreatable("")).isFalse(); assertThat(NumberUtils.isCreatable("abc")).isFalse(); assertThat(NumberUtils.isCreatable(" 22 ")).isFalse(); assertThat(NumberUtils.isCreatable("09")).isFalse();
Обратите внимание, что мы получаем истинные утверждения для шестнадцатеричных чисел, восьмеричных чисел и научных обозначений в строках 6, 7 и 8 соответственно.
Кроме того, в строке 14 строка “09” возвращает false , поскольку предыдущее “0” указывает, что это восьмеричное число, а “09” не является допустимым восьмеричным числом.
Для каждого ввода, возвращающего true с помощью этого метода, мы можем использовать NumberUtils.createNumber(String) , который даст нам действительное число.
5.2. Количество элементов.isParsable(Строка)
NumberUtils.Метод isParsable(String) проверяет, является ли данная Строка анализируемой или нет.
Анализируемые числа-это те, которые успешно анализируются любым методом анализа, например Integer.parseInt(Строка) , Long.parseLong(Строка) , Float.parseFloat(Строка) или Double.parseDouble(Строка) .
В отличие от NumberUtils.isCreatable() , этот метод не принимает шестнадцатеричные числа, научные обозначения или строки, заканчивающиеся любым типом классификатора, таким как ‘f’, ‘F’, ‘d’, ‘D’, ‘l’ или ‘L’ .
Давайте рассмотрим некоторые утверждения:
assertThat(NumberUtils.isParsable("22")).isTrue(); assertThat(NumberUtils.isParsable("-23")).isTrue(); assertThat(NumberUtils.isParsable("2.2")).isTrue(); assertThat(NumberUtils.isParsable("09")).isTrue(); assertThat(NumberUtils.isParsable(null)).isFalse(); assertThat(NumberUtils.isParsable("")).isFalse(); assertThat(NumberUtils.isParsable("6.2f")).isFalse(); assertThat(NumberUtils.isParsable("9.8d")).isFalse(); assertThat(NumberUtils.isParsable("22L")).isFalse(); assertThat(NumberUtils.isParsable("0xFF")).isFalse(); assertThat(NumberUtils.isParsable("2.99e+8")).isFalse();
В строке 4, в отличие от NumberUtils.isCreatable() , число, начинающееся со строки “0” , считается не восьмеричным числом, а обычным десятичным числом, и поэтому оно возвращает true.
Мы можем использовать этот метод в качестве замены того, что мы делали в разделе 3, где мы пытаемся проанализировать число и проверить наличие ошибки.
5.3. StringUtils.IsNumeric(CharSequence)
Метод StringUtils.IsNumeric(CharSequence) строго проверяет наличие цифр в Юникоде. Это означает:
- Любые цифры из любого языка, который является цифрой Unicode, приемлемы
- Поскольку десятичная точка не считается цифрой в Юникоде, она недопустима
- Ведущие знаки (как положительные, так и отрицательные) также неприемлемы
Теперь давайте посмотрим на этот метод в действии:
assertThat(StringUtils.isNumeric("123")).isTrue(); assertThat(StringUtils.isNumeric("١٢٣")).isTrue(); assertThat(StringUtils.isNumeric("१२३")).isTrue(); assertThat(StringUtils.isNumeric(null)).isFalse(); assertThat(StringUtils.isNumeric("")).isFalse(); assertThat(StringUtils.isNumeric(" ")).isFalse(); assertThat(StringUtils.isNumeric("12 3")).isFalse(); assertThat(StringUtils.isNumeric("ab2c")).isFalse(); assertThat(StringUtils.isNumeric("12.3")).isFalse(); assertThat(StringUtils.isNumeric("-123")).isFalse();
Обратите внимание, что входные параметры в строках 2 и 3 представляют собой числа 123 на арабском и деванагари, соответственно. Поскольку они являются допустимыми цифрами Юникода, этот метод возвращает true для них.
5.4. StringUtils.isNumericSpace(CharSequence)
StringUtils.isNumericSpace(CharSequence) строго проверяет наличие цифр Юникода и/или пробелов. Это то же самое, что и StringUtils.IsNumeric () , за исключением того, что он также принимает пробелы, и не только начальные и конечные пробелы, но и если они находятся между числами:
assertThat(StringUtils.isNumericSpace("123")).isTrue(); assertThat(StringUtils.isNumericSpace("١٢٣")).isTrue(); assertThat(StringUtils.isNumericSpace("")).isTrue(); assertThat(StringUtils.isNumericSpace(" ")).isTrue(); assertThat(StringUtils.isNumericSpace("12 3")).isTrue(); assertThat(StringUtils.isNumericSpace(null)).isFalse(); assertThat(StringUtils.isNumericSpace("ab2c")).isFalse(); assertThat(StringUtils.isNumericSpace("12.3")).isFalse(); assertThat(StringUtils.isNumericSpace("-123")).isFalse();
6. Контрольные показатели
Прежде чем мы завершим эту статью, давайте рассмотрим некоторые результаты тестирования, которые помогут нам проанализировать, какой из вышеупомянутых методов лучше всего подходит для нашего варианта использования.
6.1. Простой бенчмарк
Во-первых, мы используем простой подход. Мы выбираем одно строковое значение – для нашего теста мы используем Integer.MAX_VALUE . Затем это значение будет проверено на всех наших реализациях:
Benchmark Mode Cnt Score Error Units Benchmarking.usingCoreJava avgt 20 57.241 ± 0.792 ns/op Benchmarking.usingNumberUtils_isCreatable avgt 20 26.711 ± 1.110 ns/op Benchmarking.usingNumberUtils_isParsable avgt 20 46.577 ± 1.973 ns/op Benchmarking.usingRegularExpressions avgt 20 101.580 ± 4.244 ns/op Benchmarking.usingStringUtils_isNumeric avgt 20 35.885 ± 1.691 ns/op Benchmarking.usingStringUtils_isNumericSpace avgt 20 31.979 ± 1.393 ns/op
Как мы видим, наиболее дорогостоящими операциями являются регулярные выражения. После этого – наше основное решение на основе Java.
Кроме того, обратите внимание, что операции с использованием библиотеки Apache Commons по большому счету одинаковы.
6.2. Усовершенствованный контрольный показатель
Давайте используем более разнообразный набор тестов для более репрезентативного эталона:
- 95 значений являются числовыми (0-94 и Целое число.MAX_VALUE )
- 3 содержат числа, но все еще неправильно отформатированы — ‘ x0 ‘, ‘ 0. .005′, и ‘ -11 ‘
- 1 содержит только текст
- 1-это null
После выполнения тех же тестов мы увидим результаты:
Benchmark Mode Cnt Score Error Units Benchmarking.usingCoreJava avgt 20 10162.872 ± 798.387 ns/op Benchmarking.usingNumberUtils_isCreatable avgt 20 1703.243 ± 108.244 ns/op Benchmarking.usingNumberUtils_isParsable avgt 20 1589.915 ± 203.052 ns/op Benchmarking.usingRegularExpressions avgt 20 7168.761 ± 344.597 ns/op Benchmarking.usingStringUtils_isNumeric avgt 20 1071.753 ± 8.657 ns/op Benchmarking.usingStringUtils_isNumericSpace avgt 20 1157.722 ± 24.139 ns/op
Самое важное различие заключается в том, что два наших теста, решение для регулярных выражений и основное решение на основе Java, поменялись местами.
Из этого результата мы узнаем, что выбрасывание и обработка исключения NumberFormatException , которое происходит только в 5% случаев, оказывает относительно большое влияние на общую производительность. Таким образом, мы можем сделать вывод, что оптимальное решение зависит от наших ожидаемых входных данных.
Кроме того, мы можем с уверенностью заключить, что для оптимальной производительности следует использовать методы из библиотеки Commons или метод, реализованный аналогичным образом.
7. Заключение
В этой статье мы рассмотрели различные способы определения того, является ли строка числовой или нет. Мы рассмотрели оба решения – встроенные методы и внешние библиотеки.
Как всегда, реализацию всех приведенных выше примеров и фрагментов кода, включая код, используемый для выполнения тестов, можно найти на GitHub .