Il modo più veloce per verificare se la stringa contiene solo cifre


178

Conosco alcuni modi per verificarlo. regex, int.parse, tryparse, looping.

qualcuno può dirmi qual è il modo più veloce per verificare?

la necessità è di VERIFICARE solo non è necessario analizzare effettivamente.

questa non è la stessa domanda di: Come posso identificare se una stringa è un numero?

la domanda non è solo su come identificare. ma su quale sia il metodo più veloce .


2
senza misurare, immagino int.tryparse
kenny,

Probabilmente un ciclo scritto in assembly che legge blocchi di dati di dimensioni di parole native dalla stringa in un registro e quindi esegue un controllo di intervallo su ogni byte nel registro.
aroth,

35
semplicementereturn str.All(Char.IsDigit);
Mohsen,

2
int.TryParse non controlla se la stringa contiene solo cifre! Stringhe come "-13" (con meno e spazi) verranno analizzate correttamente.
aleyush,

Risposte:


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

    return true;
}

Sarà probabilmente il modo più veloce per farlo.


16
C'è anchechar.IsDigit()
Keith il

30
@Keith IsDigitritorna trueper circa trecento caratteri in più. Comprese cifre decimali a larghezza intera 0123... (comuni in Cina e Giappone) e cifre di altre culture, ad esempio ০১২௧௨௩௪꘤꘥꘦꘧꘨e molto altro.
Codici InCos

62
se qualcuno se ne frega, questo può certamente essere ridotto a un liner ->return str.All(c => c >= '0' && c <= '9');
Jonesopolis

18
Si potrebbe semplicemente fare anche questo: return str.All(char.IsDigit);. Evviva per i gruppi di metodi!
Icemanind

11
Nota che la stringa vuota non è un numero Vaild.
Danon,

64

Ecco alcuni benchmark basati su 1000000 analisi della stessa stringa:

Aggiornato per le releasestatistiche:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

Ecco il codice, sembra IsDigitsOnly è più veloce:

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

Ovviamente vale la pena notare che TryParse consente spazi bianchi iniziali / finali e simboli specifici della cultura. È anche limitato alla lunghezza della stringa.


L'analisi di un numero richiede sicuramente più tempo del semplice controllo di ogni cifra, poiché stai eseguendo la conversione di base.

1
1000 analisi della stessa stringa non dovrebbero richiedere quasi alcun tempo, tra l'altro, ben al di sotto del tempo in cui il rumore naturale rende i risultati insignificanti. Mi aspetto di analizzarlo un milione di volte per ottenere tempi utili.
Jon Skeet,

Downvoted perché il riferimento è modo troppo breve per essere utile e non ha posto che il metodo sta dando la risposta sbagliata anche per il campione si sta testando. La stringa di esempio è composta solo da cifre, ma poiché è troppo lunga per un int, TryParse restituisce false.
Jon Skeet,

È molto più vicino con 1m. Ah buon punto per la lunghezza, mi è mancato.
TheCodeKing

3
Ooh, con / o + sulla compilation, ora è oltre 5 volte più veloce di int. TryParse. Solo per verificare, non sei in esecuzione nel debugger, vero?
Jon Skeet

59

Potresti semplicemente farlo usando LINQ

return str.All(char.IsDigit);

  1. .All restituisce true per stringhe vuote ed eccezione per stringhe null.
  2. char.IsDigit è vero per tutti i caratteri Unicode.

3
char.IsDigit corrisponde a numerose cifre unicode di varie versioni locali (vedere fileformat.info/info/unicode/category/Nd/list.htm ). Inoltre, la tua risposta utilizza LINQ, quindi è improbabile che sia il modo più veloce per farlo. Potrebbe essere sufficiente per la maggior parte dei casi d'uso.
Stephen Holt,

1
@StephenHolt Sì, hai ragione, mi rendo conto che non è necessariamente il più veloce, ma è probabilmente il più semplice da scrivere.
Uday,

Sì, punto giusto. Ho anche scritto una risposta simile (vedi sotto) qualche anno fa, anche se la mia versione ha appena verificato se il carattere era compreso tra '0' e '9' per eliminare i caratteri da altre versioni locali. Ciò dipenderà dai requisiti esatti.
Stephen Holt,

34

Il char ha già un IsDigit (char c) che fa questo:

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

Puoi semplicemente farlo:

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

Se ti interessava controllare le cifre Unicode non avresti dovuto trasmettere un carattere a un int solo perché è un codice errato, anche per un codice più veloce.
user823959,

1
@ user823959: Non sono sicuro di cosa intendi. Char.IsDigit fa parte del mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Gerhard scusa, errore mio.
user823959,

Questo è più conciso del looping, ma sulla mia macchina, oltre un milione di iterazioni, poiché il loop è sempre più veloce di ~ 1,5 volte
Sudhanshu Mishra

23

Può essere più veloce di circa il 20% usando solo un confronto per chare forinvece di 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; 
}

Codice utilizzato per i test (profilare sempre perché i risultati dipendono dall'hardware, dalle versioni, dall'ordine, ecc.):

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

Risultati su Intel i5-3470 a 3,2 GHz, VS 2015 .NET 4.6.1 Modalità di rilascio e ottimizzazioni abilitate:

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 

Per chiunque sia tentato di usare i metodi più brevi, si noti che


14

Se sei preoccupato per le prestazioni, usa né int.TryParsenor Regex- scrivi la tua (semplice) funzione ( DigitsOnlyo DigitsOnly2sotto, ma no) DigitsOnly3 - LINQ sembra incorrere in un sovraccarico significativo).

Inoltre, tenere presente che int.TryParsenon riuscirà se la stringa è troppo lunga per "adattarsi"int .

Questo semplice benchmark ...

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

    }

}

... produce il seguente risultato ...

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

Funzione con validazione vuota:

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

10

Mi piace Linq e per farlo uscire al primo disallineamento puoi farlo

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

8

Probabilmente il modo più veloce è:

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

Nota: restituirà True nel caso in cui la stringa sia vuota, il che è errato (se non si considera vuoto come numero / cifra validi)


7

Questo dovrebbe funzionare:

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

int.Parseo int.TryParsenon funzionerà sempre, perché la stringa potrebbe contenere più cifre che un int può contenere.

Se hai intenzione di fare questo controllo più di una volta, è utile usare una regex compilata: impiega più tempo la prima volta, ma è molto più veloce dopo.


3
questo è sbagliato, restituisce vero se esiste anche una sola cifra. sebbene l'idea rispettata sia fantastica.
Nahum,

1
Questo è di gran lunga il metodo più lento, ma è la migliore soluzione basata su dimensioni sconosciute della stringa. Come accennato, il regex ha anche bisogno di una modifica.
TheCodeKing,

6

È possibile farlo in un'istruzione LINQ a una riga. OK, mi rendo conto che questo non è necessariamente il più veloce, quindi tecnicamente non risponde alla domanda, ma è probabilmente il più semplice da scrivere:

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

4
str.All(char.IsDigit)è ancora più facile da scrivere, ma ovviamente non equivalente al tuo codice.
CodesInChaos,

Ho provato a provare questo: pastebin.com/PuWBp9n1 in fase di rilascio senza debugger ovviamente ... e sembra WAYYYY più veloce. @Jon Skeet puoi fornire alcune informazioni? str.All (c => c> = '0' && c <= '9') sembra MODO più veloce di IsDigit
Nahum

1
@NahumLitvin IsDigitsupporta unicode. Quindi, a seconda di quali compromessi di memoria temporale Microsoft ha scelto durante l'implementazione, il controllo potrebbe essere piuttosto costoso. Presumo che inoltri al codice nativo, che la transizione può anche essere piuttosto costosa.
Codici InCos

@CodesInChaos quando hai detto che "non era equivalente al mio codice" sono andato a controllare cos'altro potrebbe corrispondere e si scopre che le cifre in altre lingue (ad esempio l'arabo) corrisponderebbero nella tua versione. Immagino sia qualcosa che OP dovrebbe prendere in considerazione, sia che tali cifre siano valide o meno. Quando faccio int. TryParse, penso che non accetterebbe stringhe contenenti tali caratteri.
Stephen Holt,

LINQ è il modo più lento per realizzare qualsiasi cosa. Se vuoi applicare una regola generale alla codifica, supponi che più livello e funzionalità offrono qualcosa, più è lento.
TravisO,

3

Questo potrebbe arrivare molto tardi !, ma sono sicuro che aiuterà qualcuno, come mi ha aiutato.

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

1

Puoi provare a usare le espressioni regolari testando la stringa di input per avere solo cifre (0-9) usando il .IsMatch(string input, string pattern)metodo in 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;
        }
    }
}

Saluti


3
Ciao Jason e benvenuto su Stackoverflow. Grazie per aver risposto, ma nota che la domanda riguardava il modo più veloce. Le espressioni regolari sono relativamente lente, questo è stato discusso in altre risposte.
Nahum,

1

funzionerà perfettamente, ci sono molti altri modi ma funzionerebbe

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

Prova questo codice:

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

Puoi spiegare perché la tua soluzione è migliore di quelle già fornite?
Noel Widmer,

Perché l'ordine di tempo per l'esecuzione di questo codice [o (1)] è inferiore rispetto ad altri [o (n)]
H. Borsipour

Sarei molto sorpreso se Convert.ToInt32corresse più veloce di o (n). Hai qualche prova a sostegno di questo assunto?
BDL

1
potrebbe essere più veloce se str è in realtà un numero, ma probabilmente sarebbe più lento in caso di Exeption. Inoltre non risponde alla domanda perché non funzionerà se str ha un numero maggiore di int.MaxValue.
Tomer Wolberg,

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

Sebbene questo codice possa risolvere il problema, è necessario aggiungere una spiegazione del perché / come funziona. E per favore spiega perché pensi che questo codice sia migliore di quelli già forniti.
BDL

1
Inoltre: il codice restituisce True per stringhe vuote.
BDL


-3

Il modo molto intelligente e semplice per rilevare la tua stringa contiene solo cifre o no è così:

string s = "12fg";

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

La condizione if non è necessaria, quindi sono due istruzioni return, puoi semplicemente restituire il s.Tutti ... Ma ci sono altri problemi come con stringhe vuote.
alvarlagerlof,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.