Question Comment vérifier si une chaîne est numérique en Java


Comment vérifieriez-vous si une chaîne était un nombre avant de l'analyser?


701
2017-07-09 09:49


origine


Réponses:


Avec Apache Commons Lang 3.5 et plus: NumberUtils.isCreatable ou StringUtils.isNumeric.

Avec Apache Commons Lang 3.4 et ci-dessous: NumberUtils.isNumber ou StringUtils.isNumeric.

Vous pouvez aussi utiliser StringUtils.isNumericSpace qui revient true pour les chaînes vides et ignore les espaces internes dans la chaîne. (Les javadocs liés contiennent des exemples détaillés pour chaque méthode.)


559
2017-09-24 17:01



Cela se fait généralement avec une simple fonction définie par l'utilisateur (c'est-à-dire la fonction "Numérique" de Roll-your-own).

Quelque chose comme:

public static boolean isNumeric(String str)  
{  
  try  
  {  
    double d = Double.parseDouble(str);  
  }  
  catch(NumberFormatException nfe)  
  {  
    return false;  
  }  
  return true;  
}

Cependant, si vous appelez beaucoup cette fonction et que vous vous attendez à ce qu'un grand nombre de vérifications échouent parce que ce n'est pas un nombre, les performances de ce mécanisme ne seront pas excellentes, car vous utilisez des exceptions pour chaque échec. ce qui est une opération assez coûteuse.

Une approche alternative peut être d'utiliser une expression régulière pour vérifier la validité d'être un nombre:

public static boolean isNumeric(String str)
{
  return str.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.
}

Soyez prudent avec le mécanisme RegEx ci-dessus, car il échouera si vous utilisez des chiffres non arabes (c'est-à-dire des chiffres autres que 0 à 9). Cela est dû au fait que la partie "\ d" du RegEx ne correspondra qu'à [0-9] et n'est effectivement pas internationalement informée numériquement. (Merci à OregonGhost pour le signaler!)

Ou encore une autre alternative consiste à utiliser l'objet java.text.NumberFormat intégré de Java pour voir si, après avoir analysé la chaîne, la position de l'analyseur est à la fin de la chaîne. Si c'est le cas, nous pouvons supposer que la chaîne entière est numérique:

public static boolean isNumeric(String str)
{
  NumberFormat formatter = NumberFormat.getInstance();
  ParsePosition pos = new ParsePosition(0);
  formatter.parse(str, pos);
  return str.length() == pos.getIndex();
}

789
2017-07-09 09:55



Si vous êtes sur Android, alors vous devriez utiliser:

android.text.TextUtils.isDigitsOnly(CharSequence str)

la documentation peut être trouvée ici

rester simple. la plupart du temps tout le monde peut "reprogrammer" (la même chose).


127
2017-11-22 21:34



Comme @CraigTP l'a mentionné dans son excellente réponse, j'ai aussi des problèmes de performance similaires en utilisant Exceptions pour tester si la chaîne est numérique ou non. Donc, je finis par diviser la chaîne et utiliser java.lang.Character.isDigit().

public static boolean isNumeric(String str)
{
    for (char c : str.toCharArray())
    {
        if (!Character.isDigit(c)) return false;
    }
    return true;
}

Selon le Javadoc, Character.isDigit(char) reconnaîtra correctement les chiffres non latins. En ce qui concerne les performances, je pense qu'un simple N nombre de comparaisons où N est le nombre de caractères de la chaîne serait plus efficace sur le plan des calculs que de faire une correspondance regex.

MISE À JOUR: Comme l'a souligné Jean-François Corbett dans le commentaire, le code ci-dessus ne validerait que les entiers positifs, ce qui couvre la majorité de mon cas d'utilisation. Vous trouverez ci-dessous le code mis à jour qui valide correctement les nombres décimaux en fonction des paramètres régionaux par défaut utilisés dans votre système, en supposant que le séparateur décimal ne se trouve qu'une seule fois dans la chaîne.

public static boolean isStringNumeric( String str )
{
    DecimalFormatSymbols currentLocaleSymbols = DecimalFormatSymbols.getInstance();
    char localeMinusSign = currentLocaleSymbols.getMinusSign();

    if ( !Character.isDigit( str.charAt( 0 ) ) && str.charAt( 0 ) != localeMinusSign ) return false;

    boolean isDecimalSeparatorFound = false;
    char localeDecimalSeparator = currentLocaleSymbols.getDecimalSeparator();

    for ( char c : str.substring( 1 ).toCharArray() )
    {
        if ( !Character.isDigit( c ) )
        {
            if ( c == localeDecimalSeparator && !isDecimalSeparatorFound )
            {
                isDecimalSeparatorFound = true;
                continue;
            }
            return false;
        }
    }
    return true;
}

107
2017-08-17 11:30



Java 8 expressions lambda.

String someString = "123123";
boolean isNumeric = someString.chars().allMatch( Character::isDigit );

67
2017-12-13 17:01



La bibliothèque Guava de Google fournit une bonne méthode d'assistance pour cela: Ints.tryParse. Vous l'utilisez comme Integer.parseInt mais ça revient null plutôt que de lancer une exception si la chaîne n'analyse pas un entier valide. Notez qu'il retourne Integer, pas int, donc vous devez convertir / autobox retour à int.

Exemple:

String s1 = "22";
String s2 = "22.2";
Integer oInt1 = Ints.tryParse(s1);
Integer oInt2 = Ints.tryParse(s2);

int i1 = -1;
if (oInt1 != null) {
    i1 = oInt1.intValue();
}
int i2 = -1;
if (oInt2 != null) {
    i2 = oInt2.intValue();
}

System.out.println(i1);  // prints 22
System.out.println(i2);  // prints -1

Cependant, à partir de la version actuelle - Guava r11 - il est encore marqué @Beta.

Je ne l'ai pas comparé. En regardant le code source, il y a des frais généraux de beaucoup de vérification de la santé mentale, mais à la fin, ils utilisent Character.digit(string.charAt(idx)), similaire, mais légèrement différent de, la réponse de @Ibrahim ci-dessus. Il n'y a aucune exception traitant des frais généraux sous les couvertures dans leur exécution.


41
2018-01-29 20:13



N'utilisez pas d'exceptions pour valider vos valeurs. Utilisez plutôt Utilities comme apache NumberUtils:

NumberUtils.isNumber(myStringValue);

modifier:

Veuillez noter que, si votre chaîne commence par 0, NumberUtils interprétera votre valeur comme hexadécimale.

NumberUtils.isNumber("07") //true
NumberUtils.isNumber("08") //false

25
2017-08-31 14:14



Pourquoi tout le monde pousse-t-il pour des solutions d'exception / regex?

Bien que je puisse comprendre la plupart des gens sont d'accord avec essayer / attraper, si vous voulez le faire fréquemment ... il peut être extrêmement taxant.

Ce que j'ai fait ici était de prendre la regex, les méthodes parseNumber (), et la méthode de recherche de tableau pour voir laquelle était la plus efficace. Cette fois, j'ai seulement regardé les nombres entiers.

public static boolean isNumericRegex(String str) {
    if (str == null)
        return false;
    return str.matches("-?\\d+");
}

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    char[] data = str.toCharArray();
    if (data.length <= 0)
        return false;
    int index = 0;
    if (data[0] == '-' && data.length > 1)
        index = 1;
    for (; index < data.length; index++) {
        if (data[index] < '0' || data[index] > '9') // Character.isDigit() can go here too.
            return false;
    }
    return true;
}

public static boolean isNumericException(String str) {
    if (str == null)
        return false;
    try {  
        /* int i = */ Integer.parseInt(str);
    } catch (NumberFormatException nfe) {  
        return false;  
    }
    return true;
}

Les résultats dans la vitesse que j'ai eu étaient:

Done with: for (int i = 0; i < 10000000; i++)...

With only valid numbers ("59815833" and "-59815833"):
    Array numeric took 395.808192 ms [39.5808192 ns each]
    Regex took 2609.262595 ms [260.9262595 ns each]
    Exception numeric took 428.050207 ms [42.8050207 ns each]
    // Negative sign
    Array numeric took 355.788273 ms [35.5788273 ns each]
    Regex took 2746.278466 ms [274.6278466 ns each]
    Exception numeric took 518.989902 ms [51.8989902 ns each]
    // Single value ("1")
    Array numeric took 317.861267 ms [31.7861267 ns each]
    Regex took 2505.313201 ms [250.5313201 ns each]
    Exception numeric took 239.956955 ms [23.9956955 ns each]
    // With Character.isDigit()
    Array numeric took 400.734616 ms [40.0734616 ns each]
    Regex took 2663.052417 ms [266.3052417 ns each]
    Exception numeric took 401.235906 ms [40.1235906 ns each]

With invalid characters ("5981a5833" and "a"):
    Array numeric took 343.205793 ms [34.3205793 ns each]
    Regex took 2608.739933 ms [260.8739933 ns each]
    Exception numeric took 7317.201775 ms [731.7201775 ns each]
    // With a single character ("a")
    Array numeric took 291.695519 ms [29.1695519 ns each]
    Regex took 2287.25378 ms [228.725378 ns each]
    Exception numeric took 7095.969481 ms [709.5969481 ns each]

With null:
    Array numeric took 214.663834 ms [21.4663834 ns each]
    Regex took 201.395992 ms [20.1395992 ns each]
    Exception numeric took 233.049327 ms [23.3049327 ns each]
    Exception numeric took 6603.669427 ms [660.3669427 ns each] if there is no if/null check

Disclaimer: Je ne prétends pas que ces méthodes sont 100% optimisées, elles sont juste pour la démonstration des données

Exceptions gagnées si et seulement si le nombre est de 4 caractères ou moins, et chaque chaîne est toujours un numéro ... dans quel cas, pourquoi même avoir un chèque?

En bref, il est extrêmement pénible si vous rencontrez fréquemment des numéros invalides avec le try / catch, ce qui est logique. Une règle importante que je suis toujours est N'utilisez JAMAIS try / catch pour le déroulement du programme. Ceci est un exemple pourquoi.

Fait intéressant, le simple si char <0 || > 9 était extrêmement simple à écrire, facile à retenir (et devrait fonctionner dans plusieurs langues) et a remporté presque tous les scénarios de test.

Le seul inconvénient est que j'imagine que Integer.parseInt () peut gérer des nombres non ASCII, contrairement à la méthode de recherche de tableau.


Pour ceux qui se demandent pourquoi j'ai dit qu'il est facile de se souvenir du tableau de personnages, si vous savez qu'il n'y a pas de signes négatifs, vous pouvez facilement vous en sortir avec quelque chose de condensé comme ceci:

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    for (char c : str.toCharArray())
        if (c < '0' || c > '9')
            return false;
    return true;

Enfin, en guise de note finale, j'étais curieux au sujet de l'opérateur d'assignation dans l'exemple accepté avec tous les votes vers le haut. Ajout dans l'assignation de

double d = Double.parseDouble(...)

n'est pas seulement inutile puisque vous n'utilisez même pas la valeur, mais cela gaspille du temps de traitement et augmente le temps d'exécution de quelques nanosecondes (ce qui a conduit à une augmentation de 100-200 ms dans les tests). Je ne vois pas pourquoi quelqu'un ferait cela, car c'est un travail supplémentaire pour réduire les performances.

On pourrait penser que ce serait optimisé ... mais peut-être que je devrais vérifier le bytecode et voir ce que fait le compilateur. Cela n'explique pas pourquoi il s'est toujours montré plus long pour moi, même si, d'une certaine façon, il est optimisé ... donc je me demande ce qui se passe. A noter: plus long, je veux dire exécuter le test pour 10000000 itérations, et exécuter ce programme plusieurs fois (10x +) montre toujours qu'il est plus lent.

EDIT: Mise à jour d'un test pour Character.isDigit ()


19
2018-03-29 16:09



public static boolean isNumeric(String str)
{
    return str.matches("-?\\d+(.\\d+)?");
}

L'expression régulière de CraigTP (montrée ci-dessus) produit des faux positifs. Par exemple. "23y4" sera compté comme un nombre parce que '.' correspond à tout caractère qui n'est pas le point décimal.

En outre, il rejettera tout nombre avec un «+»

Une alternative qui évite ces deux problèmes mineurs est
 

public static boolean isNumeric(String str)
{
    return str.matches("[+-]?\\d*(\\.\\d+)?");
}

16
2017-09-17 16:56