Qual è il modo migliore per verificare se una stringa rappresenta un numero intero in Java?

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

  •  04-07-2019
  •  | 
  •  

Domanda

Normalmente uso il seguente linguaggio per verificare se una stringa può essere convertita in un numero intero.

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

Sono solo io o sembra un po 'hacker? Qual è il modo migliore?


Vedi la mia risposta (con benchmark, basata sulla risposta precedente di CodingWithSpike ) per capire perché ho invertito la mia posizione e accettato la risposta di Jonas Klemming a questo problema. Penso che questo codice originale verrà utilizzato dalla maggior parte delle persone perché è più rapido da implementare e più gestibile, ma i suoi ordini di grandezza sono più lenti quando vengono forniti dati non interi.

È stato utile?

Soluzione

Se non sei preoccupato per potenziali problemi di overflow, questa funzione si comporterà circa 20-30 volte più velocemente rispetto all'uso di 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;
}

Altri suggerimenti

Ce l'hai, ma dovresti solo prendere NumberFormatException .

Poiché esiste la possibilità che le persone visitino ancora qui e saranno prevenute rispetto a Regex dopo i benchmark ... Quindi fornirò una versione aggiornata del benchmark, con una versione compilata di Regex. Al contrario dei benchmark precedenti, questo dimostra che la soluzione Regex ha prestazioni costantemente buone.

Copiato da Bill the Lizard e aggiornato con la versione compilata:

private final Pattern pattern = Pattern.compile("^-?\\d+
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
quot;); 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+<*>quot;); } 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; }

Risultati:

<*>

Ha fatto un rapido benchmark. Le eccezioni in realtà non sono così costose, a meno che non inizi a ripristinare più metodi e la JVM debba fare molto lavoro per mettere in atto lo stack di esecuzione. Con lo stesso metodo, non sono cattivi.

 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+<*>quot;);
 }

 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;
 }

Output:

  

ByException: 31

     

ByRegex: 453 (nota: ricompilare il pattern ogni volta)

     

ByJonas: 16

Sono d'accordo sul fatto che anche la soluzione di Jonas K sia la più solida. Sembra che vince :)

org.apache.commons.lang.StringUtils.isNumeric 

sebbene la lib standard di Java manchi davvero di tali funzioni di utilità

Penso che Apache Commons sia un "must have" per ogni programmatore Java

peccato che non sia ancora portato su Java5

Dipende in parte dal significato di " può essere convertito in un numero intero " ;.

Se intendi " può essere convertito in un int in Java " allora la risposta di Jonas è un buon inizio, ma non completa del tutto il lavoro. Passerebbe ad esempio 99999999999999999999999999999999. Aggiungerei la normale chiamata try / catch dalla tua stessa domanda alla fine del metodo.

I controlli carattere per carattere rifiuteranno in modo efficiente "non un numero intero". casi, lasciando " è un numero intero ma Java non può gestirlo " casi che devono essere rilevati dalla rotta dell'eccezione più lenta. Anche tu potresti farlo a mano, ma sarebbe un lotto più complicato.

Solo un commento su regexp. Ogni esempio fornito qui è sbagliato !. Se vuoi usare regexp non dimenticare che la compilazione del pattern richiede molto tempo. Questo:

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

e anche questo:

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();
    }
}

causa la compilazione del modello in ogni chiamata di metodo. Per usarlo correttamente segui:

<*>

Ho copiato il codice dalla risposta di rally25rs e ho aggiunto alcuni test per i dati non interi. I risultati sono innegabilmente a favore del metodo pubblicato da Jonas Klemming. I risultati per il metodo Eccezione che ho pubblicato inizialmente sono piuttosto buoni quando hai dati interi, ma sono i peggiori quando non lo fai, mentre i risultati per la soluzione RegEx (che scommetto che molte persone usano) erano costantemente cattivi. Vedi la risposta di Felipe per un esempio di regex compilato, che è molto più veloce.

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+
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
quot;); } 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; }

Risultati:

<*>

Esiste la versione guava:

import com.google.common.primitives.Ints;

Integer intValue = Ints.tryParse(stringValue);

Restituirà null invece di generare un'eccezione se non riesce ad analizzare la stringa.

Questo è più breve, ma più breve non è necessariamente migliore (e non catturerà valori interi che sono fuori intervallo, come sottolineato nel commento di Danatel ):

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

Personalmente, poiché l'implementazione è eliminata in un metodo di supporto e la correttezza supera la lunghezza, vorrei solo andare con qualcosa di simile a quello che hai (meno la cattura della classe Exception di base piuttosto che NumberFormatException ).

Puoi usare il metodo match della classe di stringhe. [0-9] rappresenta tutti i valori che può essere, + indica che deve essere lungo almeno un carattere e * indica che può essere lungo zero o più caratteri.

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

Questa è una variante Java 8 della risposta di Jonas Klemming:

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)));
}

Codice test:

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));
    });
}

Risultati del codice di prova:

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

Se l'array String contiene numeri interi e stringhe puri, il codice seguente dovrebbe funzionare. Devi solo guardare il primo personaggio. per esempio. [& Quot; 4 " " 44 " " abc " " 77 " " legame "]

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

Puoi anche utilizzare Scanner class e usa hasNextInt () - e questo ti consente di provare anche altri tipi, come float, ecc.

Che ne dici di:

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

Basta selezionare 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 
 }  

Puoi provare apache utils

NumberUtils.isNumber( myText)

Vedi il javadoc qui

Probabilmente dovrai prendere in considerazione anche il caso d'uso:

Se la maggior parte delle volte si prevede che i numeri siano validi, la cattura dell'eccezione provoca solo un sovraccarico di prestazioni quando si tenta di convertire numeri non validi. Considerando che chiamare un metodo isInteger () e poi convertirlo usando Integer.parseInt () sempre causerà un sovraccarico prestazionale per i numeri validi - le stringhe sono analizzato due volte, una volta dal controllo e una volta dalla conversione.

Questa è una modifica del codice Jonas che controlla se la stringa è nel range per essere lanciata in un numero intero.

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;
}

Se si utilizza l'API Android è possibile utilizzare:

TextUtils.isDigitsOnly(str);

Un'altra opzione:

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

Se vuoi verificare se la stringa rappresenta un numero intero che si adatta a un tipo int, ho fatto una piccola modifica alla risposta del jonas, in modo che le stringhe che rappresentano numeri interi più grandi di Integer.MAX_VALUE o più piccole di Integer.MIN_VALUE, ora restituirà false. Ad esempio: "3147483647" restituirà false perché 3147483647 è maggiore di 2147483647 e allo stesso modo, "-2147483649" restituirà anche false perché -2147483649 è inferiore a -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;
}

Quello che hai fatto funziona, ma probabilmente non dovresti sempre controllare in questo modo. Le eccezioni generate devono essere riservate a "eccezionale" situazioni (forse che si adatta al tuo caso, però) e sono molto costose in termini di prestazioni.

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

Funzionerebbe solo per numeri interi positivi.

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;        
}

Questo funziona per me. Semplicemente per identificare se una stringa è una primitiva o un numero.

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;
    }

Per controllare tutti i caratteri int, puoi semplicemente usare un doppio negativo.

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

[^ 0-9] + $ controlla se ci sono caratteri che non sono numeri interi, quindi il test fallisce se è vero. Semplicemente NON quello e ti accorgi del successo.

Trova questo può essere utile:

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

Credo che non ci sia rischio di incorrere in un'eccezione, perché come puoi vedere di seguito analizzi sempre in modo sicuro int in String e non viceversa.

  1. controlli se ogni slot di carattere nella tua stringa corrisponde almeno uno dei caratteri {" 0 ", " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ", " 8 " " 9 "} .

    if(aString.substring(j, j+1).equals(String.valueOf(i)))
    
  2. somma tutte le volte che hai incontrato nelle slot sopra caratteri.

    digits++;
    
  3. E infine controlla se i tempi in cui hai incontrato numeri interi sono i caratteri sono uguali alla lunghezza della stringa specificata.

    if(digits == aString.length())
    

E in pratica abbiamo:

    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!!");
    }

E i risultati sono:

  

È un numero intero !!

     

Non è un numero intero !!

Allo stesso modo, puoi confermare se un String è un float o un double ma in quei casi devi trovare solo uno. (punto) nella stringa e ovviamente controlla se digit = = (aString.length () - 1)

  

Ancora una volta, qui non c'è rischio di imbattersi in un'eccezione di analisi, ma se prevedi di analizzare una stringa che è noto che contiene un numero (diciamo int tipo di dati) devi prima verificare se si adatta al tipo di dati. Altrimenti devi lanciarlo.

Spero di aver aiutato

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top