Как лучше всего проверить, представляет ли строка целое число в Java?

StackOverflow https://stackoverflow.com/questions/237159

  •  04-07-2019
  •  | 
  •  

Вопрос

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

public boolean isInteger( String input ) {
    try {
        Integer.parseInt( input );
        return true;
    }
    catch( Exception e ) {
        return false;
    }
}

Мне кажется, или это кажется немного хакерским?Какой лучший способ?


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

Это было полезно?

Решение

Если вас не беспокоят потенциальные проблемы переполнения, эта функция будет работать примерно в 20-30 раз быстрее, чем при использовании Integer.parseInt () .

public static boolean isInteger(String str) {
    if (str == null) {
        return false;
    }
    int length = str.length();
    if (length == 0) {
        return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
        if (length == 1) {
            return false;
        }
        i = 1;
    }
    for (; i < length; i++) {
        char c = str.charAt(i);
        if (c < '0' || c > '9') {
            return false;
        }
    }
    return true;
}

Другие советы

У вас это есть, но вы должны ловить только NumberFormatException .

Поскольку существует вероятность того, что люди все еще посещают это место и будут предвзято относиться к Regex после тестов...Итак, я собираюсь предоставить обновленную версию теста с скомпилированной версией Regex.В отличие от предыдущих тестов, этот показывает, что решение Regex действительно имеет стабильно хорошую производительность.

Скопировано из Bill the Lizard и дополнено скомпилированной версией:

private final Pattern pattern = Pattern.compile("^-?\\d+$");

public void runTests() {
    String big_int = "1234567890";
    String non_int = "1234XY7890";

    long startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByException(big_int);
    long endTime = System.currentTimeMillis();
    System.out.print("ByException - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByException(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByException - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByRegex(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByRegex - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByRegex(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByRegex - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
            IsInt_ByCompiledRegex(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByCompiledRegex - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
            IsInt_ByCompiledRegex(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByCompiledRegex - non-integer data: ");
    System.out.println(endTime - startTime);


    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByJonas(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByJonas - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByJonas(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByJonas - non-integer data: ");
    System.out.println(endTime - startTime);
}

private boolean IsInt_ByException(String str)
{
    try
    {
        Integer.parseInt(str);
        return true;
    }
    catch(NumberFormatException nfe)
    {
        return false;
    }
}

private boolean IsInt_ByRegex(String str)
{
    return str.matches("^-?\\d+$");
}

private boolean IsInt_ByCompiledRegex(String str) {
    return pattern.matcher(str).find();
}

public boolean IsInt_ByJonas(String str)
{
    if (str == null) {
            return false;
    }
    int length = str.length();
    if (length == 0) {
            return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
            if (length == 1) {
                    return false;
            }
            i = 1;
    }
    for (; i < length; i++) {
            char c = str.charAt(i);
            if (c <= '/' || c >= ':') {
                    return false;
            }
    }
    return true;
}

Полученные результаты:

ByException - integer data: 45
ByException - non-integer data: 465

ByRegex - integer data: 272
ByRegex - non-integer data: 131

ByCompiledRegex - integer data: 45
ByCompiledRegex - non-integer data: 26

ByJonas - integer data: 8
ByJonas - non-integer data: 2

Сделал быстрый тест.Исключения на самом деле не так уж и дороги, если только вы не начнете возвращать несколько методов, и JVM придется проделать большую работу, чтобы получить стек выполнения на месте.Придерживаясь одного и того же метода, они неплохо работают.

 public void RunTests()
 {
     String str = "1234567890";

     long startTime = System.currentTimeMillis();
     for(int i = 0; i < 100000; i++)
         IsInt_ByException(str);
     long endTime = System.currentTimeMillis();
     System.out.print("ByException: ");
     System.out.println(endTime - startTime);

     startTime = System.currentTimeMillis();
     for(int i = 0; i < 100000; i++)
         IsInt_ByRegex(str);
     endTime = System.currentTimeMillis();
     System.out.print("ByRegex: ");
     System.out.println(endTime - startTime);

     startTime = System.currentTimeMillis();
     for(int i = 0; i < 100000; i++)
         IsInt_ByJonas(str);
     endTime = System.currentTimeMillis();
     System.out.print("ByJonas: ");
     System.out.println(endTime - startTime);
 }

 private boolean IsInt_ByException(String str)
 {
     try
     {
         Integer.parseInt(str);
         return true;
     }
     catch(NumberFormatException nfe)
     {
         return false;
     }
 }

 private boolean IsInt_ByRegex(String str)
 {
     return str.matches("^-?\\d+$");
 }

 public boolean IsInt_ByJonas(String str)
 {
     if (str == null) {
             return false;
     }
     int length = str.length();
     if (length == 0) {
             return false;
     }
     int i = 0;
     if (str.charAt(0) == '-') {
             if (length == 1) {
                     return false;
             }
             i = 1;
     }
     for (; i < length; i++) {
             char c = str.charAt(i);
             if (c <= '/' || c >= ':') {
                     return false;
             }
     }
     return true;
 }

Выход:

По исключению:31

Порегексу:453 (примечание:перекомпиляция шаблона каждый раз)

Автор: Йонас:16

Я согласен, что решение Йонаса К. также является самым надежным.Похоже, он выиграл :)

org.apache.commons.lang.StringUtils.isNumeric 

хотя в стандартной библиотеке Java такие служебные функции действительно отсутствуют

Я думаю, что Apache Commons "должен иметь" для каждого Java-программиста

Жаль, что он еще не перенесен на Java5

Отчасти это зависит от того, что вы подразумеваете под "можно преобразовать в целое число".

Если вы имеете в виду " можно преобразовать в int в Java " тогда ответ от Йонаса - хорошее начало, но не совсем заканчивает работу. Например, он пройдет 999999999999999999999999999999. Я бы добавил обычный вызов try / catch из вашего собственного вопроса в конце метода.

Посимвольные проверки будут эффективно отклонять " не целое число " в случаях, оставляя " это целое число, но Java не может его обработать " случаи, которые будут пойманы более медленным маршрутом исключения. Вы могли бы сделать этот бит тоже вручную, но это было бы много сложнее.

Только один комментарий о регулярном выражении. Каждый приведенный здесь пример неверен! Если вы хотите использовать регулярные выражения, не забывайте, что компиляция шаблона занимает много времени. Это:

str.matches("^-?\\d+
Pattern.matches("-?\\d+", input);
quot;)

а также это:

import java.util.regex.Pattern;

/**
 * @author Rastislav Komara
 */
public class NaturalNumberChecker {
    public static final Pattern PATTERN = Pattern.compile("^\\d+<*>quot;);

    boolean isNaturalNumber(CharSequence input) {
        return input != null && PATTERN.matcher(input).matches();
    }
}

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

<*>

Я скопировал код из ответа rally25rs и добавил несколько тестов для нецелочисленных данных.Результаты, несомненно, говорят в пользу метода, предложенного Йонасом Клеммингом.Результаты для метода Exception, который я изначально опубликовал, довольно хороши, когда у вас есть целочисленные данные, но они худшие, когда у вас их нет, в то время как результаты для решения RegEx (которое, я уверен, многие люди используют) были последовательно плохой.Видеть Ответ Фелипе для скомпилированного примера регулярного выражения, который работает намного быстрее.

public void runTests()
{
    String big_int = "1234567890";
    String non_int = "1234XY7890";

    long startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByException(big_int);
    long endTime = System.currentTimeMillis();
    System.out.print("ByException - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByException(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByException - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByRegex(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByRegex - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByRegex(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByRegex - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByJonas(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByJonas - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByJonas(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByJonas - non-integer data: ");
    System.out.println(endTime - startTime);
}

private boolean IsInt_ByException(String str)
{
    try
    {
        Integer.parseInt(str);
        return true;
    }
    catch(NumberFormatException nfe)
    {
        return false;
    }
}

private boolean IsInt_ByRegex(String str)
{
    return str.matches("^-?\\d+$");
}

public boolean IsInt_ByJonas(String str)
{
    if (str == null) {
            return false;
    }
    int length = str.length();
    if (length == 0) {
            return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
            if (length == 1) {
                    return false;
            }
            i = 1;
    }
    for (; i < length; i++) {
            char c = str.charAt(i);
            if (c <= '/' || c >= ':') {
                    return false;
            }
    }
    return true;
}

Полученные результаты:

ByException - integer data: 47
ByException - non-integer data: 547

ByRegex - integer data: 390
ByRegex - non-integer data: 313

ByJonas - integer data: 0
ByJonas - non-integer data: 16

Существует версия гуавы:

import com.google.common.primitives.Ints;

Integer intValue = Ints.tryParse(stringValue);

Он вернет ноль вместо генерации исключения, если не сможет разобрать строку.

Это короче, но короче не обязательно лучше (и оно не будет ловить целочисленные значения, выходящие за пределы диапазона, , как указано в комментарии danatel ):

input.matches("^-?\\d+<*>quot;);

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

Вы можете использовать метод совпадений строкового класса. [0-9] представляет все возможные значения, + означает, что длина должна быть не менее одного символа, а * означает, что длина может быть равна нулю или более.

boolean isNumeric = yourString.matches("[0-9]+"); // 1 or more characters long, numbers only
boolean isNumeric = yourString.matches("[0-9]*"); // 0 or more characters long, numbers only

Это вариант ответа Джонаса Клемминга на Java 8:

public static boolean isInteger(String str) {
    return str != null && str.length() > 0 &&
         IntStream.range(0, str.length()).allMatch(i -> i == 0 && (str.charAt(i) == '-' || str.charAt(i) == '+')
                  || Character.isDigit(str.charAt(i)));
}

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

public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    Arrays.asList("1231231", "-1232312312", "+12313123131", "qwqe123123211", "2", "0000000001111", "", "123-", "++123",
            "123-23", null, "+-123").forEach(s -> {
        System.out.printf("%15s %s%n", s, isInteger(s));
    });
}

Результаты тестового кода:

        1231231 true
    -1232312312 true
   +12313123131 true
  qwqe123123211 false
              2 true
  0000000001111 true
                false
           123- false
          ++123 false
         123-23 false
           null false
          +-123 false

Если ваш массив String содержит чистые целые числа и строки, приведенный ниже код должен работать. Вам нужно только взглянуть на первого персонажа. например [& Quot; 4 & Quot;, & Quot; 44 & Quot;, & Quot; аЬс & Quot;, & Quot; 77 & Quot;, & Quot; связь & Quot;]

if (Character.isDigit(string.charAt(0))) {
    //Do something with int
}

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

Как насчет:

return Pattern.matches("-?\\d+", input);

Вы просто отметили NumberFormatException : -

 String value="123";
 try  
 {  
    int s=Integer.parseInt(any_int_val);
    // do something when integer values comes 
 }  
 catch(NumberFormatException nfe)  
 {  
          // do something when string values comes 
 }  

Вы можете попробовать утилиты apache

NumberUtils.isNumber( myText)

Смотрите здесь Javadoc

Возможно, вам также необходимо учесть вариант использования:

Если большую часть времени вы ожидаете, что числа будут действительными, то перехват исключения приводит только к снижению производительности при попытке преобразовать недопустимые числа. Принимая во внимание, что вызов некоторого метода isInteger () , а затем преобразование с использованием Integer.parseInt () всегда приведет к снижению производительности для допустимых чисел - строки анализируется дважды, один раз чеком и один раз конверсией.

Это модификация кода Jonas ', который проверяет, находится ли строка в пределах диапазона, который будет приведен к целому числу.

public static boolean isInteger(String str) {
    if (str == null) {
        return false;
    }
    int length = str.length();
    int i = 0;

    // set the length and value for highest positive int or lowest negative int
    int maxlength = 10;
    String maxnum = String.valueOf(Integer.MAX_VALUE);
    if (str.charAt(0) == '-') { 
        maxlength = 11;
        i = 1;
        maxnum = String.valueOf(Integer.MIN_VALUE);
    }  

    // verify digit length does not exceed int range
    if (length > maxlength) { 
        return false; 
    }

    // verify that all characters are numbers
    if (maxlength == 11 && length == 1) {
        return false;
    }
    for (int num = i; num < length; num++) {
        char c = str.charAt(num);
        if (c < '0' || c > '9') {
            return false;
        }
    }

    // verify that number value is within int range
    if (length == maxlength) {
        for (; i < length; i++) {
            if (str.charAt(i) < maxnum.charAt(i)) {
                return true;
            }
            else if (str.charAt(i) > maxnum.charAt(i)) {
                return false;
            }
        }
    }
    return true;
}

Если вы используете API Android, вы можете использовать:

TextUtils.isDigitsOnly(str);

Другой вариант:

private boolean isNumber(String s) {
    boolean isNumber = true;
    for (char c : s.toCharArray()) {
        isNumber = isNumber && Character.isDigit(c);
    }
    return isNumber;
}

Если вы хотите проверить, представляет ли строка целое число, которое соответствует типу int, я немного изменил ответ jonas, чтобы строки, представляющие целые числа, большие, чем Integer.MAX_VALUE, или меньше, чем Integer.MIN_VALUE, теперь вернет false. Например: «3147483647» вернет false, потому что 3147483647 больше, чем 2147483647, а также "-2147483649". также вернет false, потому что -2147483649 меньше, чем -2147483648.

public static boolean isInt(String s) {
  if(s == null) {
    return false;
  }
  s = s.trim(); //Don't get tricked by whitespaces.
  int len = s.length();
  if(len == 0) {
    return false;
  }
  //The bottom limit of an int is -2147483648 which is 11 chars long.
  //[note that the upper limit (2147483647) is only 10 chars long]
  //Thus any string with more than 11 chars, even if represents a valid integer, 
  //it won't fit in an int.
  if(len > 11) {
    return false;
  }
  char c = s.charAt(0);
  int i = 0;
  //I don't mind the plus sign, so "+13" will return true.
  if(c == '-' || c == '+') {
    //A single "+" or "-" is not a valid integer.
    if(len == 1) {
      return false;
    }
    i = 1;
  }
  //Check if all chars are digits
  for(; i < len; i++) {
    c = s.charAt(i);
    if(c < '0' || c > '9') {
      return false;
    }
  }
  //If we reached this point then we know for sure that the string has at
  //most 11 chars and that they're all digits (the first one might be a '+'
  // or '-' thought).
  //Now we just need to check, for 10 and 11 chars long strings, if the numbers
  //represented by the them don't surpass the limits.
  c = s.charAt(0);
  char l;
  String limit;
  if(len == 10 && c != '-' && c != '+') {
    limit = "2147483647";
    //Now we are going to compare each char of the string with the char in
    //the limit string that has the same index, so if the string is "ABC" and
    //the limit string is "DEF" then we are gonna compare A to D, B to E and so on.
    //c is the current string's char and l is the corresponding limit's char
    //Note that the loop only continues if c == l. Now imagine that our string
    //is "2150000000", 2 == 2 (next), 1 == 1 (next), 5 > 4 as you can see,
    //because 5 > 4 we can guarantee that the string will represent a bigger integer.
    //Similarly, if our string was "2139999999", when we find out that 3 < 4,
    //we can also guarantee that the integer represented will fit in an int.
    for(i = 0; i < len; i++) {
      c = s.charAt(i);
      l = limit.charAt(i);
      if(c > l) {
        return false;
      }
      if(c < l) {
        return true;
      }
    }
  }
  c = s.charAt(0);
  if(len == 11) {
    //If the first char is neither '+' nor '-' then 11 digits represent a 
    //bigger integer than 2147483647 (10 digits).
    if(c != '+' && c != '-') {
      return false;
    }
    limit = (c == '-') ? "-2147483648" : "+2147483647";
    //Here we're applying the same logic that we applied in the previous case
    //ignoring the first char.
    for(i = 1; i < len; i++) {
      c = s.charAt(i);
      l = limit.charAt(i);
      if(c > l) {
        return false;
      }
      if(c < l) {
        return true;
      }
    }
  }
  //The string passed all tests, so it must represent a number that fits
  //in an int...
  return true;
}
is_number = true;
try {
  Integer.parseInt(mystr)
} catch (NumberFormatException  e) {
  is_number = false;
}

То, что вы сделали, работает, но вы, вероятно, не всегда должны проверять это. Бросающие исключения должны быть зарезервированы для «исключительных» ситуации (может быть, это подходит в вашем случае, однако), и очень дорого с точки зрения производительности.

Number number;
try {
    number = NumberFormat.getInstance().parse("123");
} catch (ParseException e) {
    //not a number - do recovery.
    e.printStackTrace();
}
//use number

Это будет работать только для натуральных чисел.

public static boolean isInt(String str) {
    if (str != null && str.length() != 0) {
        for (int i = 0; i < str.length(); i++) {
            if (!Character.isDigit(str.charAt(i))) return false;
        }
    }
    return true;        
}

Это работает для меня. Просто чтобы определить, является ли String примитивом или числом.

private boolean isPrimitive(String value){
        boolean status=true;
        if(value.length()<1)
            return false;
        for(int i = 0;i<value.length();i++){
            char c=value.charAt(i);
            if(Character.isDigit(c) || c=='.'){

            }else{
                status=false;
                break;
            }
        }
        return status;
    }

Чтобы проверить все int-символы, вы можете просто использовать двойной минус.

if (! searchString.matches (" [^ 0-9] + $ ")) ...

[^ 0-9] + $ проверяет, есть ли какие-либо символы, не являющиеся целыми числами, поэтому тест не пройден, если он истинен. Просто НЕ так, и вы добьетесь успеха.

Это может быть полезно:

public static boolean isInteger(String self) {
    try {
        Integer.valueOf(self.trim());
        return true;
    } catch (NumberFormatException nfe) {
        return false;
    }
}

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

Так:

  1. Ты проверять если каждый слот символов в вашей строке совпадает по крайней мере один из персонажей {"0","1","2","3","4","5","6","7","8","9"}.

    if(aString.substring(j, j+1).equals(String.valueOf(i)))
    
  2. Ты сумма все времена, что вы столкнулись в слотах выше персонажи.

    digits++;
    
  3. И, наконец, ты проверять если времена, которые вы столкнулись с целыми числами как Meme it символы равны длине данной строки.

    if(digits == aString.length())
    

А на практике имеем:

    String aString = "1234224245";
    int digits = 0;//count how many digits you encountered
    for(int j=0;j<aString.length();j++){
        for(int i=0;i<=9;i++){
            if(aString.substring(j, j+1).equals(String.valueOf(i)))
                    digits++;
        }
    }
    if(digits == aString.length()){
        System.out.println("It's an integer!!");
        }
    else{
        System.out.println("It's not an integer!!");
    }

    String anotherString = "1234f22a4245";
    int anotherDigits = 0;//count how many digits you encountered
    for(int j=0;j<anotherString.length();j++){
        for(int i=0;i<=9;i++){
            if(anotherString.substring(j, j+1).equals(String.valueOf(i)))
                    anotherDigits++;
        }
    }
    if(anotherDigits == anotherString.length()){
        System.out.println("It's an integer!!");
        }
    else{
        System.out.println("It's not an integer!!");
    }

И результаты:

Это целое число!!

Это не целое число!!

Аналогичным образом вы можете проверить, является ли String это float или double но в таких случаях вам придется столкнуться только один . (точка) в строке и, конечно, проверьте, есть ли digits == (aString.length()-1)

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

надеюсь, я помог

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top