Наилучшая реализация для метода isNumber (string)
В моем ограниченном опыте я работал над несколькими проектами, у которых был какой-то класс класса строк с методами, чтобы определить, является ли данная строка числом. Идея всегда была одинаковой, однако реализация была другой. Некоторые окружают попытку разбора с помощью try/catch
public boolean isInteger(String str) {
try {
Integer.parseInt(str);
return true;
} catch (NumberFormatException nfe) {}
return false;
}
и другие соответствуют регулярному выражению
public boolean isInteger(String str) {
return str.matches("^-?[0-9]+(\\.[0-9]+)?$");
}
Является ли один из этих методов лучше другого? Я лично предпочитаю использовать подход с регулярным выражением, как он лаконичен, но будет ли он выполняться по параметру, если он вызван, итерации, скажем, списка из нескольких сотен тысяч строк?
Примечание. Поскольку я новичок на сайте, я не совсем понимаю этот бизнес сообщества Wiki, поэтому, если это относится к нему, дайте мне знать, и я с радостью переведу его.
EDIT:
Со всеми предложениями TryParse я поместил код сравнения Asaph (спасибо за отличный пост!) На С# и добавил метод TryParse. И, как кажется, TryParse выигрывает руки. Тем не менее, подход try catch занял безумное количество времени. Насколько я думаю, я сделал что-то не так! Я также обновил регулярное выражение для обработки отрицательных и десятичных точек.
Результаты для обновленного кода С#:
00:00:51.7390000 for isIntegerParseInt
00:00:03.9110000 for isIntegerRegex
00:00:00.3500000 for isIntegerTryParse
Использование:
static bool isIntegerParseInt(string str) {
try {
int.Parse(str);
return true;
} catch (FormatException e){}
return false;
}
static bool isIntegerRegex(string str) {
return Regex.Match(str, "^-?[0-9]+(\\.[0-9]+)?$").Success;
}
static bool isIntegerTryParse(string str) {
int bob;
return Int32.TryParse(str, out bob);
}
Ответы
Ответ 1
Я только что запустил некоторые тесты производительности этих двух методов (на Macbook Pro OSX Leopard Java 6). ParseInt работает быстрее. Вот результат:
This operation took 1562 ms.
This operation took 2251 ms.
И вот мой тестовый код:
public class IsIntegerPerformanceTest {
public static boolean isIntegerParseInt(String str) {
try {
Integer.parseInt(str);
return true;
} catch (NumberFormatException nfe) {}
return false;
}
public static boolean isIntegerRegex(String str) {
return str.matches("^[0-9]+$");
}
public static void main(String[] args) {
long starttime, endtime;
int iterations = 1000000;
starttime = System.currentTimeMillis();
for (int i=0; i<iterations; i++) {
isIntegerParseInt("123");
isIntegerParseInt("not an int");
isIntegerParseInt("-321");
}
endtime = System.currentTimeMillis();
System.out.println("This operation took " + (endtime - starttime) + " ms.");
starttime = System.currentTimeMillis();
for (int i=0; i<iterations; i++) {
isIntegerRegex("123");
isIntegerRegex("not an int");
isIntegerRegex("-321");
}
endtime = System.currentTimeMillis();
System.out.println("This operation took " + (endtime - starttime) + " ms.");
}
}
Также обратите внимание, что ваше регулярное выражение будет отклонять отрицательные числа, и метод parseInt примет их.
Ответ 2
Вот наш способ сделать это:
public boolean isNumeric(String string) throws IllegalArgumentException
{
boolean isnumeric = false;
if (string != null && !string.equals(""))
{
isnumeric = true;
char chars[] = string.toCharArray();
for(int d = 0; d < chars.length; d++)
{
isnumeric &= Character.isDigit(chars[d]);
if(!isnumeric)
break;
}
}
return isnumeric;
}
Ответ 3
Если абсолютная производительность является ключевой, и если вы просто проверяете целые числа (а не числа с плавающей запятой), я подозреваю, что итерация по каждому символу в строке, возвращая false, если вы встретите что-то не в диапазоне 0-9, будет быстрее всего.
RegEx - это более универсальное решение, поэтому, вероятно, он не будет работать так быстро для этого особого случая. В этом случае решение, которое генерирует исключение, будет иметь дополнительные накладные расходы. TryParse будет немного медленнее, если вы действительно не заботитесь о значении числа, просто будь то число или нет, так как также должно произойти преобразование в число.
Для чего-либо, кроме внутреннего цикла, который вызывается много раз, различия между всеми этими параметрами должны быть незначительными.
Ответ 4
Мне нужно было реорганизовать код как ваш, чтобы избавиться от NumberFormatException. Реорганизованный код:
public static Integer parseInteger(final String str) {
if (str == null || str.isEmpty()) {
return null;
}
final Scanner sc = new Scanner(str);
return Integer.valueOf(sc.nextInt());
}
Как парень Java 1.4, я не знал о java.util.Scanner. Я нашел эту интересную статью:
http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Java
Мне понравилось решение со сканером, очень компактное и по-прежнему читаемое.
Ответ 5
Некоторые языки, такие как С#, имеют TryParse (или эквивалент), который работает достаточно хорошо для чего-то вроде этого.
public boolean IsInteger(string value)
{
int i;
return Int32.TryParse(value, i);
}
Ответ 6
Лично я сделал бы это, если вы действительно хотите его упростить.
public boolean isInteger(string myValue)
{
int myIntValue;
return int.TryParse(myValue, myIntValue)
}
Ответ 7
Вы можете создать метод расширения для строки и сделать весь процесс более чистым...
public static bool IsInt(this string str)
{
int i;
return int.TryParse(str, out i);
}
Затем вы можете сделать следующее в своем фактическом коде...
if(myString.IsInt())....
Ответ 8
Используя .NET, вы можете сделать что-то вроде:
private bool isNumber(string str)
{
return str.Any(c => !char.IsDigit(c));
}
Ответ 9
public static boolean CheckString(String myString) {
char[] digits;
digits = myString.toCharArray();
for (char div : digits) {// for each element div of type char in the digits collection (digits is a collection containing div elements).
try {
Double.parseDouble(myString);
System.out.println("All are numbers");
return true;
} catch (NumberFormatException e) {
if (Character.isDigit(div)) {
System.out.println("Not all are chars");
return false;
}
}
}
System.out.println("All are chars");
return true;
}
Ответ 10
Это моя реализация, чтобы проверить, состоит ли строка из цифр:
public static boolean isNumeric(String string)
{
if (string == null)
{
throw new NullPointerException("The string must not be null!");
}
final int len = string.length();
if (len == 0)
{
return false;
}
for (int i = 0; i < len; ++i)
{
if (!Character.isDigit(string.charAt(i)))
{
return false;
}
}
return true;
}
Ответ 11
Мне нравится код:
public static boolean isIntegerRegex(String str) {
return str.matches("^[0-9]+$");
}
Но при создании шаблона это будет еще больше:
public static Pattern patternInteger = Pattern.compile("^[0-9]+$");
public static boolean isIntegerRegex(String str) {
return patternInteger.matcher(str).matches();
}
Применить по тесту мы имеем результат:
This operation isIntegerParseInt took 1313 ms.
This operation isIntegerRegex took 1178 ms.
This operation isIntegerRegexNew took 304 ms.
С
public class IsIntegerPerformanceTest {
private static Pattern pattern = Pattern.compile("^[0-9]+$");
public static boolean isIntegerParseInt(String str) {
try {
Integer.parseInt(str);
return true;
} catch (NumberFormatException nfe) {
}
return false;
}
public static boolean isIntegerRegexNew(String str) {
return pattern.matcher(str).matches();
}
public static boolean isIntegerRegex(String str) {
return str.matches("^[0-9]+$");
}
public static void main(String[] args) {
long starttime, endtime;
int iterations = 1000000;
starttime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
isIntegerParseInt("123");
isIntegerParseInt("not an int");
isIntegerParseInt("-321");
}
endtime = System.currentTimeMillis();
System.out.println("This operation isIntegerParseInt took " + (endtime - starttime) + " ms.");
starttime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
isIntegerRegex("123");
isIntegerRegex("not an int");
isIntegerRegex("-321");
}
endtime = System.currentTimeMillis();
System.out.println("This operation took isIntegerRegex " + (endtime - starttime) + " ms.");
starttime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
isIntegerRegexNew("123");
isIntegerRegexNew("not an int");
isIntegerRegexNew("-321");
}
endtime = System.currentTimeMillis();
System.out.println("This operation took isIntegerRegexNew " + (endtime - starttime) + " ms.");
}
}
Ответ 12
Я думаю, что это может быть быстрее предыдущих решений, если вы выполните следующее (Java):
public final static boolean isInteger(String in)
{
char c;
int length = in.length();
boolean ret = length > 0;
int i = ret && in.charAt(0) == '-' ? 1 : 0;
for (; ret && i < length; i++)
{
c = in.charAt(i);
ret = (c >= '0' && c <= '9');
}
return ret;
}
Я запустил тот же код, что и Асаф, и результат:
Эта операция заняла 28 мс.
Огромная разница (против 1691 мс и 2049 мс - на моем компьютере). Учтите, что этот метод не проверяет, является ли строка нулевой, поэтому вы должны сделать это ранее (включая обрезку строки)
Ответ 13
Я думаю, что людям здесь не хватает точки. Использование одного и того же шаблона неоднократно имеет очень легкую оптимизацию. Просто используйте одиночный шаблон. Выполняя это, во всех моих тестах подход try-catch никогда не имеет лучшего ориентира, чем шаблонный подход. При успешном тестировании try-catch занимает в два раза больше времени, а тест сбоя - в 6 раз медленнее.
public static final Pattern INT_PATTERN= Pattern.compile("^-?[0-9]+(\\.[0-9]+)?$");
public static boolean isInt(String s){
return INT_PATTERN.matcher(s).matches();
}
Ответ 14
Я использую это, но мне нравилась строгость Асафа на его посту.
public static bool IsNumeric(object expression)
{
if (expression == null)
return false;
double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture), NumberStyles.Any,
NumberFormatInfo.InvariantInfo, out number);
}
Ответ 15
Для длинных номеров используйте это:
(JAVA)
public static boolean isNumber(String string) {
try {
Long.parseLong(string);
} catch (Exception e) {
return false;
}
return true;
}
Ответ 16
public static boolean isNumber(String str){
return str.matches("[0-9]*\\.[0-9]+");
}
чтобы проверить, является ли число (включая float, integer) или нет
Ответ 17
Измененная версия моего предыдущего ответа:
public static boolean isInteger(String in)
{
if (in != null)
{
char c;
int i = 0;
int l = in.length();
if (l > 0 && in.charAt(0) == '-')
{
i = 1;
}
if (l > i)
{
for (; i < l; i++)
{
c = in.charAt(i);
if (c < '0' || c > '9')
return false;
}
return true;
}
}
return false;
}
Ответ 18
Я просто добавил этот класс к моим утилитам:
public class TryParseLong {
private boolean isParseable;
private long value;
public TryParseLong(String toParse) {
try {
value = Long.parseLong(toParse);
isParseable = true;
} catch (NumberFormatException e) {
// Exception set to null to indicate it is deliberately
// being ignored, since the compensating action
// of clearing the parsable flag is being taken.
e = null;
isParseable = false;
}
}
public boolean isParsable() {
return isParseable;
}
public long getLong() {
return value;
}
}
Чтобы использовать его:
TryParseLong valueAsLong = new TryParseLong(value);
if (valueAsLong.isParsable()) {
...
// Do something with valueAsLong.getLong();
} else {
...
}
Это только одно раз анализирует значение.
Он по-прежнему использует исключение и поток управления за исключениями, но по крайней мере он инкапсулирует такой код в класс утилиты, а используемый им код может работать более обычным способом.
Проблема с Java по сравнению с С# заключается в том, что С# имеет значения и передает по ссылке, поэтому может эффективно возвращать 2 части информации; флаг, указывающий, что что-то разбирается или нет, и фактическое анализируемое значение. Когда мы reutrn > 1 значение в Java, нам нужно создать объект для их хранения, поэтому я взял этот подход и поместил флаг и проанализированное значение в объект.
Анализ Escape, скорее всего, справится с этим эффективно и создаст значение и флаг в стеке и никогда не создаст этот объект в куче, поэтому я думаю, что это будет иметь минимальное влияние на производительность.
По моему мнению, это дает оптимальный компромисс между сохранением контроля за потоком по вашему коду, хорошей производительностью и не разбор целого числа более одного раза.
Ответ 19
public static boolean CheckIfNumber (номер строки) {
for(int i = 0; i < number.length(); i++){
try{
Double.parseDouble(number.substring(i));
}catch(NumberFormatException ex){
return false;
}
}
return true;
}
У меня была эта проблема раньше, но когда я вводил число, а затем символ, он все равно вернул бы true, я думаю, что это лучший способ сделать это. Просто проверьте, есть ли каждый char номер. Немного дольше, но он заботится, если у вас есть ситуация, когда пользователь вводит "1abc". По какой-то причине, когда я пытался попытаться поймать без итерации, он все еще считал, что это число так.