Le moyen le plus rapide de vérifier si la chaîne ne contient que des chiffres


178

Je connais plusieurs façons de vérifier cela. regex, int.parse, tryparse, en boucle.

quelqu'un peut-il me dire quel est le moyen le plus rapide de vérifier?

le besoin est de vérifier seulement pas besoin d'analyser réellement.

ce n'est pas la même question que: Comment identifier si une chaîne est un nombre?

la question n'est pas seulement de savoir comment identifier. mais quelle est la méthode la plus rapide .


2
w / o juste mesurer je suppose int.tryparse
kenny

Probablement une boucle écrite dans l'assemblage qui lit des morceaux de données de la taille d'un mot natif de la chaîne dans un registre, puis effectue une vérification de plage sur chaque octet du registre.
aroth

35
simplementreturn str.All(Char.IsDigit);
Mohsen

2
int.TryParse ne vérifie pas si la chaîne ne contient que des chiffres! Les chaînes comme "-13" (avec moins et des espaces) seront analysées avec succès.
aleyush

Réponses:


261
bool IsDigitsOnly(string str)
{
    foreach (char c in str)
    {
        if (c < '0' || c > '9')
            return false;
    }

    return true;
}

Sera probablement le moyen le plus rapide de le faire.


16
Il y a aussichar.IsDigit()
Keith

30
@Keith IsDigitrevient truepour environ trois cents caractères supplémentaires. Y compris les chiffres décimaux pleine largeur 0123... (commun en Chine et au Japon) et les chiffres d'autres cultures, par exemple ০১২௧௨௩௪꘤꘥꘦꘧꘨et bien plus encore.
CodesInChaos

62
si quelqu'un s'en soucie, cela peut certainement être réduit à un one-liner ->return str.All(c => c >= '0' && c <= '9');
Jonesopolis

18
Vous pouvez simplement faire cela aussi: return str.All(char.IsDigit);. Hourra pour les groupes de méthode!
Icemanind

11
Veuillez noter que la chaîne vide n'est pas un nombre valide.
Danon

64

Voici quelques points de repère basés sur 1000000 analyses de la même chaîne:

Mis à jour pour les releasestatistiques:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

Voici le code, ressemble à IsDigitsOnly est plus rapide:

class Program
{
    private static Regex regex = new Regex("^[0-9]+$", RegexOptions.Compiled);

    static void Main(string[] args)
    {
        Stopwatch watch = new Stopwatch();
        string test = int.MaxValue.ToString();
        int value;

        watch.Start();
        for(int i=0; i< 1000000; i++)
        {
            int.TryParse(test, out value);
        }
        watch.Stop();
        Console.WriteLine("TryParse: "+watch.ElapsedTicks);

        watch.Reset();
        watch.Start();
        for (int i = 0; i < 1000000; i++)
        {
            IsDigitsOnly(test);
        }
        watch.Stop();
        Console.WriteLine("IsDigitsOnly: " + watch.ElapsedTicks);

        watch.Reset();
        watch.Start();
        for (int i = 0; i < 1000000; i++)
        {
            regex.IsMatch(test);
        }
        watch.Stop();
        Console.WriteLine("Regex: " + watch.ElapsedTicks);

        Console.ReadLine();
    }

    static bool IsDigitsOnly(string str)
    {
        foreach (char c in str)
        {
            if (c < '0' || c > '9')
                return false;
        }

        return true;
    }
}

Bien sûr, il convient de noter que TryParse autorise les espaces de début / de fin ainsi que les symboles spécifiques à la culture. Il est également limité sur la longueur de la chaîne.


L'analyse d'un nombre prend certainement plus de temps que la simple vérification de chaque chiffre, car vous effectuez une conversion de base.

1
1000 analyses de la même chaîne ne devraient prendre presque pas de temps du tout, bien en dessous du temps où le bruit naturel rend les résultats insignifiants. Je m'attendrais à devoir l'analyser un million de fois pour obtenir des horaires utiles.
Jon Skeet

Évalué à la baisse parce que le benchmark est bien trop court pour être utile et que vous n'avez pas remarqué que votre méthode donne la mauvaise réponse, même pour l'échantillon que vous testez. L'exemple de chaîne est composé uniquement de chiffres, mais comme il est trop long pour un int, TryParse renvoie false.
Jon Skeet

C'est beaucoup plus proche avec 1m. Ah bon point sur la longueur, ça m'a manqué.
TheCodeKing

3
Ooh, avec / o + lors de la compilation, c'est maintenant plus de 5 fois plus rapide que int.TryParse. Juste pour vérifier, vous n'êtes pas en cours d'exécution dans le débogueur, n'est-ce pas?
Jon Skeet

59

Vous pouvez simplement le faire en utilisant LINQ

return str.All(char.IsDigit);

  1. .All renvoie true pour les chaînes vides et exception pour les chaînes nulles.
  2. char.IsDigit est vrai pour tous les caractères Unicode.

3
char.IsDigit correspond à de nombreux chiffres unicode de différents paramètres régionaux (voir fileformat.info/info/unicode/category/Nd/list.htm ). En outre, votre réponse utilise LINQ, il est donc peu probable que ce soit le moyen le plus rapide de le faire. Cela pourrait cependant être suffisant pour la plupart des cas d'utilisation.
Stephen Holt

1
@StephenHolt Oui vous avez raison, je me rends compte que ce n'est pas forcément le plus rapide, mais c'est probablement le plus facile à écrire.
Uday

Oui, bon point. J'ai également écrit une réponse similaire (voir ci-dessous) il y a quelques années, bien que ma version vienne juste de tester si le caractère était entre «0» et «9» pour éliminer les caractères d'autres paramètres régionaux. Cela dépendra des exigences exactes.
Stephen Holt

34

Le char a déjà un IsDigit (char c) qui fait ceci:

 public static bool IsDigit(char c)
    {
      if (!char.IsLatin1(c))
        return CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber;
      if ((int) c >= 48)
        return (int) c <= 57;
      else
        return false;
    }

Vous pouvez simplement faire ceci:

var theString = "839278";
bool digitsOnly = theString.All(char.IsDigit);

Si vous vouliez vérifier les chiffres Unicode, vous n'auriez pas dû convertir un caractère en int simplement parce que c'est un mauvais code, même pour un code plus rapide.
user823959

1
@ user823959: Je ne suis pas sûr de ce que vous voulez dire. Char.IsDigit fait partie du mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Gerhard désolé, mon erreur.
user823959

C'est plus concis que la boucle, mais sur ma machine, plus d'un million d'itérations, la boucle for est toujours plus rapide d'environ 1,5 fois
Sudhanshu Mishra

23

Peut être environ 20% plus rapide en utilisant une seule comparaison par charet forau lieu de foreach:

bool isDigits(string s) 
{ 
    if (s == null || s == "") return false; 

    for (int i = 0; i < s.Length; i++) 
        if ((s[i] ^ '0') > 9) 
            return false; 

    return true; 
}

Code utilisé pour les tests (toujours profiler car les résultats dépendent du matériel, des versions, de l'ordre, etc.):

static bool isDigitsFr(string s) { if (s == null || s == "") return false; for (int i = 0; i < s.Length; i++) if (s[i] < '0' || s[i] > '9') return false; return true; }
static bool isDigitsFu(string s) { if (s == null || s == "") return false; for (int i = 0; i < s.Length; i++) if ((uint)(s[i] - '0') > 9) return false; return true; }
static bool isDigitsFx(string s) { if (s == null || s == "") return false; for (int i = 0; i < s.Length; i++) if ((s[i] ^ '0') > 9) return false; return true; }
static bool isDigitsEr(string s) { if (s == null || s == "") return false; foreach (char c in s) if (c < '0' || c > '9') return false; return true; }
static bool isDigitsEu(string s) { if (s == null || s == "") return false; foreach (char c in s) if ((uint)(c - '0') > 9) return false; return true; }
static bool isDigitsEx(string s) { if (s == null || s == "") return false; foreach (char c in s) if ((c ^ '0') > 9) return false; return true; }
static void test()
{
    var w = new Stopwatch(); bool b; var s = int.MaxValue + ""; int r = 12345678*2; var ss = new SortedSet<string>(); //s = string.Concat(Enumerable.Range(0, 127).Select(i => ((char)i ^ '0') < 10 ? 1 : 0));
    w.Restart(); for (int i = 0; i < r; i++) b = s.All(char.IsDigit); w.Stop(); ss.Add(w.Elapsed + ".All .IsDigit"); 
    w.Restart(); for (int i = 0; i < r; i++) b = s.All(c => c >= '0' && c <= '9'); w.Stop(); ss.Add(w.Elapsed + ".All <>"); 
    w.Restart(); for (int i = 0; i < r; i++) b = s.All(c => (c ^ '0') < 10); w.Stop(); ss.Add(w.Elapsed + " .All ^"); 
    w.Restart(); for (int i = 0; i < r; i++) b = isDigitsFr(s); w.Stop(); ss.Add(w.Elapsed + " for     <>");
    w.Restart(); for (int i = 0; i < r; i++) b = isDigitsFu(s); w.Stop(); ss.Add(w.Elapsed + " for     -");
    w.Restart(); for (int i = 0; i < r; i++) b = isDigitsFx(s); w.Stop(); ss.Add(w.Elapsed + " for     ^");
    w.Restart(); for (int i = 0; i < r; i++) b = isDigitsEr(s); w.Stop(); ss.Add(w.Elapsed + " foreach <>");
    w.Restart(); for (int i = 0; i < r; i++) b = isDigitsEu(s); w.Stop(); ss.Add(w.Elapsed + " foreach -");
    w.Restart(); for (int i = 0; i < r; i++) b = isDigitsEx(s); w.Stop(); ss.Add(w.Elapsed + " foreach ^");
    MessageBox.Show(string.Join("\n", ss)); return;
}

Résultats sur Intel i5-3470 @ 3,2 GHz, VS 2015 .NET 4.6.1 Mode de sortie et optimisations activées:

time    method          ratio
0.7776  for     ^       1.0000 
0.7984  foreach -       1.0268 
0.8066  foreach ^       1.0372 
0.8940  for     -       1.1497 
0.8976  for     <>      1.1543 
0.9456  foreach <>      1.2160 
4.4559  .All <>         5.7303 
4.7791  .All ^          6.1458 
4.8539  .All. IsDigit   6.2421 

Pour toute personne tentée d'utiliser les méthodes plus courtes, notez que


14

Si vous êtes préoccupé par les performances, n'utilisez ni int.TryParseni Regex- écrivez votre propre (simple) fonction ( DigitsOnlyou DigitsOnly2ci-dessous, mais pas DigitsOnly3 - LINQ semble entraîner une surcharge importante).

Sachez également que int.TryParsecela échouera si la chaîne est trop longue pour "rentrer" int.

Cette simple référence ...

class Program {

    static bool DigitsOnly(string s) {
        int len = s.Length;
        for (int i = 0; i < len; ++i) {
            char c = s[i];
            if (c < '0' || c > '9')
                return false;
        }
        return true;
    }

    static bool DigitsOnly2(string s) {
        foreach (char c in s) {
            if (c < '0' || c > '9')
                return false;
        }
        return true;
    }

    static bool DigitsOnly3(string s) {
        return s.All(c => c >= '0' && c <= '9');
    }

    static void Main(string[] args) {

        const string s1 = "916734184";
        const string s2 = "916734a84";

        const int iterations = 1000000;
        var sw = new Stopwatch();

        sw.Restart();
        for (int i = 0 ; i < iterations; ++i) {
            bool success = DigitsOnly(s1);
            bool failure = DigitsOnly(s2);
        }
        sw.Stop();
        Console.WriteLine(string.Format("DigitsOnly: {0}", sw.Elapsed));

        sw.Restart();
        for (int i = 0; i < iterations; ++i) {
            bool success = DigitsOnly2(s1);
            bool failure = DigitsOnly2(s2);
        }
        sw.Stop();
        Console.WriteLine(string.Format("DigitsOnly2: {0}", sw.Elapsed));

        sw.Restart();
        for (int i = 0; i < iterations; ++i) {
            bool success = DigitsOnly3(s1);
            bool failure = DigitsOnly3(s2);
        }
        sw.Stop();
        Console.WriteLine(string.Format("DigitsOnly3: {0}", sw.Elapsed));

        sw.Restart();
        for (int i = 0; i < iterations; ++i) {
            int dummy;
            bool success = int.TryParse(s1, out dummy);
            bool failure = int.TryParse(s2, out dummy);
        }
        sw.Stop();
        Console.WriteLine(string.Format("int.TryParse: {0}", sw.Elapsed));

        sw.Restart();
        var regex = new Regex("^[0-9]+$", RegexOptions.Compiled);
        for (int i = 0; i < iterations; ++i) {
            bool success = regex.IsMatch(s1);
            bool failure = regex.IsMatch(s2);
        }
        sw.Stop();
        Console.WriteLine(string.Format("Regex.IsMatch: {0}", sw.Elapsed));

    }

}

... produit le résultat suivant ...

DigitsOnly: 00:00:00.0346094
DigitsOnly2: 00:00:00.0365220
DigitsOnly3: 00:00:00.2669425
int.TryParse: 00:00:00.3405548
Regex.IsMatch: 00:00:00.7017648

11

Fonction avec validation vide:

public static bool IsDigitsOnly(string str)
  {             
        return !string.IsNullOrEmpty(str) && str.All(char.IsDigit);
  }

10

J'aime Linq et pour le faire sortir au premier décalage, vous pouvez le faire

string str = '0129834X33';
bool isAllDigits = !str.Any( ch=> ch < '0' || ch > '9' );

8

Le moyen le plus rapide est probablement:

myString.All(c => char.IsDigit(c))

Remarque: il renverra True si votre chaîne est vide, ce qui est incorrect (si vous ne considérez pas vide comme un nombre / chiffre valide)


7

Cela devrait fonctionner:

Regex.IsMatch("124", "^[0-9]+$", RegexOptions.Compiled)

int.Parseou int.TryParsene fonctionnera pas toujours, car la chaîne peut contenir plus de chiffres qu'un int peut contenir.

Si vous comptez faire cette vérification plus d'une fois, il est utile d'utiliser une expression régulière compilée - cela prend plus de temps la première fois, mais c'est beaucoup plus rapide après cela.


3
c'est faux, il retourne vrai s'il y a même un chiffre. bien que l'idée respectée soit géniale.
Nahum

1
C'est de loin la méthode la plus lente, mais c'est la meilleure solution basée sur une taille de chaîne inconnue. Comme mentionné, le regex a également besoin d'un ajustement.
TheCodeKing

6

Vous pouvez le faire dans une instruction LINQ d'une ligne. OK, je me rends compte que ce n'est pas forcément le plus rapide, donc ça ne répond pas techniquement à la question, mais c'est probablement le plus facile à écrire:

str.All(c => c >= '0' && c <= '9')

4
str.All(char.IsDigit)est encore plus facile à écrire, mais bien sûr pas équivalent à votre code.
CodesInChaos

J'ai essayé de tester ceci: pastebin.com/PuWBp9n1 à la sortie sans débogueur bien sûr ... et cela semble WAYYYY plus rapide. @Jon Skeet pouvez-vous nous donner un aperçu? str.All (c => c> = '0' && c <= '9') semble BEAUCOUP plus rapide que IsDigit
Nahum

1
@NahumLitvin IsDigitprend en charge l'unicode. Ainsi, en fonction des compromis temps-mémoire choisis par Microsoft lors de sa mise en œuvre, la vérification peut être assez coûteuse. Je suppose qu'il est transmis au code natif, cette transition peut également être assez coûteuse.
CodesInChaos

@CodesInChaos quand vous avez dit que ce n'était "pas équivalent à mon code", je suis allé vérifier ce qui pouvait correspondre, et il s'est avéré que les chiffres dans d'autres paramètres régionaux (par exemple en arabe) correspondraient dans votre version. Je suppose que c'est quelque chose que OP devrait prendre en compte, que ces chiffres soient valides ou non. Lorsque vous faites int.TryParse, je pense que cela n'accepterait pas les chaînes contenant de tels caractères.
Stephen Holt

LINQ est le moyen le plus lent d'accomplir quoi que ce soit. Si vous souhaitez appliquer une règle générale au codage, supposez que plus le niveau et les fonctionnalités proposés sont élevés, plus il est lent.
TravisO

3

Cela pourrait arriver très tard !, mais je suis sûr que cela aidera quelqu'un, car cela m'a aidé.

        private static bool IsDigitsOnly(string str)
        {
            return str.All(c => c >= '0' && c <= '9');
        }

1

Vous pouvez essayer d'utiliser des expressions régulières en testant la chaîne d'entrée pour qu'elle ne contienne que des chiffres (0-9) à l'aide de la .IsMatch(string input, string pattern)méthode en C #.

using System;
using System.Text.RegularExpression;

public namespace MyNS
{
    public class MyClass
    {
        public void static Main(string[] args)
        {
             string input = Console.ReadLine();
             bool containsNumber = ContainsOnlyDigits(input);
        }

        private bool ContainOnlyDigits (string input)
        {
            bool containsNumbers = true;
            if (!Regex.IsMatch(input, @"/d"))
            {
                containsNumbers = false;
            }
            return containsNumbers;
        }
    }
}

Cordialement


3
Salut Jason et bienvenue sur Stackoverflow. Merci d'avoir répondu mais notez que la question portait sur le moyen le plus rapide. Les expressions régulières sont relativement lentes, cela a été discuté dans d'autres réponses.
Nahum

1

cela fonctionnera parfaitement, il existe de nombreuses autres façons, mais cela fonctionnerait

bool IsDigitsOnly(string str)
    {
        if (str.Length > 0)//if contains characters
        {
            foreach (char c in str)//assign character to c
            {
                if (c < '0' || c > '9')//check if its outside digit range
                    return false;
            }
        }else//empty string
        {
            return false;//empty string 
        }

        return true;//only digits
    }

0

Essayez ce code:

bool isDigitsOnly(string str)
{
   try
   {
      int number = Convert.ToInt32(str);
      return true;
   }
   catch (Exception)
   {
      return false;
   }
}

Pouvez-vous expliquer pourquoi votre solution est meilleure que celles déjà fournies?
Noel Widmer

Parce que l'ordre temporel d'exécution de ce code [o (1)] est inférieur à celui des autres [o (n)]
H. Borsipour

Je serais très surpris de Convert.ToInt32courir plus vite que o (n). Avez-vous des preuves pour étayer cette hypothèse?
BDL

1
cela pourrait être plus rapide si str est en fait un nombre, mais ce serait probablement plus lent en cas d'exeption. De plus, cela ne répond pas à la question car cela ne fonctionnera pas si str est un nombre plus grand que int.MaxValue.
Tomer Wolberg

-2
public bool CheckforDigits(string x)
{    
    int tr;  
    return x.All(r=> int.TryParse(r.ToString(), out tr));
}

Bien que ce code puisse résoudre le problème, vous devez ajouter une explication pourquoi / comment cela fonctionne. Et expliquez pourquoi vous pensez que ce code est meilleur que ceux déjà fournis.
BDL

1
De plus: votre code renvoie True pour les chaînes vides.
BDL


-3

Un moyen très intelligent et facile de détecter votre chaîne ne contient que des chiffres ou non:

string s = "12fg";

if(s.All(char.IsDigit))
{
   return true; // contains only digits
}
else
{
   return false; // contains not only digits
}

La condition if n'est pas nécessaire, de même que deux instructions return, vous pouvez simplement renvoyer le s.All ... Mais il y a d'autres problèmes tels que les chaînes vides.
alvarlagerlof
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.