2009-09-02 8 views
17

Dans mon expérience limitée, j'ai été sur plusieurs projets qui ont eu une sorte de classe utilitaire de chaîne avec des méthodes pour déterminer si une chaîne donnée est un nombre. L'idée a toujours été la même, mais la mise en œuvre a été différente. Certains entourent une tentative d'analyse avec try/catchMeilleure implémentation pour une méthode isNumber (chaîne)

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

et d'autres correspondent à regex

public boolean isInteger(String str) { 
    return str.matches("^-?[0-9]+(\\.[0-9]+)?$"); 
} 

Est-ce une de ces méthodes mieux que l'autre? Personnellement, je préfère utiliser l'approche regex, car elle est concise, mais sera-t-elle performante au pair si elle est appelée en parcourant, par exemple, une liste de plusieurs centaines de milliers de chaînes?

Note: Comme je suis un peu nouveau sur le site, je ne comprends pas très bien cette activité Wiki de la communauté, donc si cela doit être le cas, faites-le moi savoir, et je le changerai volontiers.

EDIT: Avec toutes les suggestions que je TryParse le code de référence de Ported Asaph (merci pour un grand poste!) À C# et a ajouté une méthode TryParse. Et comme il semble, le TryParse gagne haut la main. Cependant, l'approche try catch a pris beaucoup de temps. Au point de penser que j'ai fait quelque chose de mal! J'ai également mis à jour regex pour gérer les négatifs et les points décimaux.

Résultats pour mise à jour, C# Code de référence:

00:00:51.7390000 for isIntegerParseInt 
00:00:03.9110000 for isIntegerRegex 
00:00:00.3500000 for isIntegerTryParse 

utilisant:

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

Je pense que TryParse est le meilleur moyen –

+1

Quelles sont vos entrées attendues? Attendez-vous que 99% du temps la chaîne * sera * un nombre, et seulement 1% du temps * ne le sera pas? Ou est-ce l'inverse? Vous voulez optimiser la branche de code qui va réellement être exercée dans la plupart des cas. –

+0

Ajouté, puis supprimez la balise C#. :) –

Répondre

13

Je viens de faire quelques benchmarks sur les performances de ces 2 méthodes (Sur Macbook Pro OSX Leopard Java 6). ParseInt est plus rapide. Voici le résultat:

This operation took 1562 ms. 
This operation took 2251 ms. 

Et voici mon code de référence:


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

Notez également que votre regex rejettera les nombres négatifs et la méthode parseInt les acceptera.

+0

Qu'en est-il de int.TryParse()? Pouvons-nous supposer que c'est exactement la même chose en termes de performance que le premier essai/catch? –

+0

Hélas, ce n'est pas du code C#! lol! –

+6

Je crois comprendre que vous ne devez compiler l'expression régulière qu'une seule fois, puis appeler pattern.matcher (s) .matches(). Cela devrait être plus rapide que de construire l'expression régulière à chaque fois. En outre, votre test peut se comporter différemment selon les chaînes d'entrée. Si la plupart du temps vous ne recevez pas d'ints, je pense que l'expression rationnelle devrait être plus rapide. –

2

Certaines langues, comme C#, ont une TryParse (ou équivalent) qui fonctionne assez bien pour quelque chose comme ça.

public boolean IsInteger(string value) 
{ 
    int i; 
    return Int32.TryParse(value, i); 
} 
2

Personnellement, je le ferais si vous voulez vraiment le simplifier.

public boolean isInteger(string myValue) 
{ 
    int myIntValue; 
    return int.TryParse(myValue, myIntValue) 
} 
3

Si la performance absolue est la clé, et si vous êtes en train de vérifier pour les entiers (non nombres à virgule flottante) Je soupçonne que itérer sur chaque caractère de la chaîne, soit faux si vous rencontrez quelque chose de pas dans la gamme 0- 9, sera le plus rapide. RegEx est une solution plus générale et ne fonctionnera probablement pas aussi rapidement dans ce cas particulier. Une solution qui déclenche une exception aura un surcoût supplémentaire dans ce cas. TryParse sera légèrement plus lent si vous ne vous souciez pas vraiment de la valeur du nombre, que ce soit ou non un nombre, car la conversion en nombre doit également avoir lieu.

Pour tout ce qui n'est pas une boucle interne appelée plusieurs fois, les différences entre toutes ces options doivent être insignifiantes.

0

Je l'utilise mais j'ai aimé la rigueur d'Asaph dans son article.

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

En utilisant .NET, vous pouvez faire quelque chose comme:

private bool isNumber(string str) 
{ 
    return str.Any(c => !char.IsDigit(c)); 
} 
+3

Cela vous dirait que -4 n'est pas un nombre, mais je suis à peu près sûr que c'est le cas. –

2

Vous pouvez créer une méthode d'extension pour une chaîne, et de rendre le processus plus propre ... regarder

public static bool IsInt(this string str) 
{ 
    int i; 
    return int.TryParse(str, out i); 
} 

Vous pourriez alors faire ce qui suit dans votre code actuel ...

if(myString.IsInt()).... 
4

Voici notre façon de le faire:

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

isNumeric n'est pas la même chose que isNumber. D'où la différence dans la mise en œuvre. –

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

je devais factoriser le code comme le vôtre pour se débarrasser de NumberFormatException. Le Code refondus:

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

Comme un gars Java 1.4, je ne savais pas java.util.Scanner. J'ai trouvé cet article intéressant:

http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Java

Je personaly aimé la solution avec le scanner, très compact et toujours lisible.

+0

Vous ne savez pas quelle version de JDK vous utilisez, mais 'Scanner.nextInt()' semble lancer 'InputMismatchException'. (même en 1.5) – ebyrob

0

Pour les numéros longs utilisez: (JAVA)

public static boolean isNumber(String string) { 
    try { 
     Long.parseLong(string); 
    } catch (Exception e) { 
     return false; 
    } 
    return true; 
} 
1

C'est ma mise en œuvre pour vérifier si une chaîne est faite de chiffres:

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; 
} 
0
public static boolean isNumber(String str){ 
     return str.matches("[0-9]*\\.[0-9]+"); 
    } 

pour vérifier si le numéro (y compris float, integer) ou non

1

J'aime le code:

public static boolean isIntegerRegex(String str) { 
    return str.matches("^[0-9]+$"); 
} 

Mais il bien plus quand créer le mode d'utilisation avant d':

public static Pattern patternInteger = Pattern.compile("^[0-9]+$"); 
public static boolean isIntegerRegex(String str) { 
    return patternInteger.matcher(str).matches(); 
} 

Appliquer par test, nous avons résultat:

This operation isIntegerParseInt took 1313 ms. 
This operation isIntegerRegex took 1178 ms. 
This operation isIntegerRegexNew took 304 ms. 

Avec:

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

Je pense Il pourrait être plus rapide que les solutions précédentes si vous faites ce qui suit (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; 
} 

Je courais le même code qui Asaph couru et le résultat était:

Cette opération a pris 28 ms.

Une énorme différence (contre 1691 ms et 2049 ms sur mon ordinateur). Prenez en compte que cette méthode ne valide pas si la chaîne est nulle, de sorte que vous devriez faire auparavant (y compris la garniture à cordes)

0

Une version modifiée de ma précédente réponse:

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

Je pense que les gens ici Il manque un point. L'utilisation du même motif à plusieurs reprises a une optimisation très facile. Utilisez simplement un singleton du motif. Pour ce faire, dans tous mes tests, l'approche try-catch n'a jamais une meilleure référence que l'approche modèle. Avec un test de succès, l'essai-prise prend deux fois le temps, avec un test d'échec c'est 6 fois plus lent.

public static final Pattern INT_PATTERN= Pattern.compile("^-?[0-9]+(\\.[0-9]+)?$"); 

public static boolean isInt(String s){ 
    return INT_PATTERN.matcher(s).matches(); 
} 
0

Je viens d'ajouter cette classe à mes utils:

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

Pour l'utiliser:

TryParseLong valueAsLong = new TryParseLong(value); 

if (valueAsLong.isParsable()) { 
    ... 
    // Do something with valueAsLong.getLong(); 
} else { 
    ... 
} 

Ce ne fait qu'analyser la valeur une fois. Il utilise toujours l'exception et le flux de contrôle par exceptions, mais il encapsule au moins ce type de code dans une classe d'utilitaire, et le code qui l'utilise peut fonctionner de manière plus normale. Le problème avec Java par rapport à C#, c'est que C# a des valeurs et passe par référence, donc il peut effectivement retourner 2 informations; le drapeau pour indiquer que quelque chose est analysable ou non, et la valeur analysée réelle. Quand nous reutrn> 1 valeur en Java, nous devons créer un objet pour les contenir, donc j'ai pris cette approche et mettre le drapeau et la valeur analysée dans un objet. L'analyse d'échappement est susceptible de gérer cela efficacement, de créer la valeur et l'indicateur sur la pile, et de ne jamais créer cet objet sur le tas, donc je pense que cela aura un impact minimal sur les performances. À mon avis, cela donne à propos du compromis optimal entre garder le control-flow-by-exception sur votre code, de bonnes performances, et ne pas analyser l'entier plus d'une fois.

0

statique CheckIfNumber public boolean (nombre String) {

for(int i = 0; i < number.length(); i++){ 
     try{ 
      Double.parseDouble(number.substring(i)); 

     }catch(NumberFormatException ex){ 
      return false; 
     } 
    } 
    return true;  
} 

J'ai eu ce problème avant, mais quand j'avais entrée un numéro, puis un personnage, il serait revenir encore vrai, je pense que cela est la meilleure façon pour le faire. Vérifiez simplement si chaque caractère est un nombre. Un peu plus long mais il prend soin si vous avez la situation d'un utilisateur en entrant "1abc". Pour une raison quelconque, quand j'ai essayé d'essayer d'attraper sans itération, il pensait toujours que c'était un numéro donc ..

Questions connexes