Dizenin yalnızca rakam içerip içermediğini kontrol etmenin en hızlı yolu


178

Bunu kontrol etmenin birkaç yolunu biliyorum. regex, int.parse, tryparse, döngü.

kimse kontrol etmenin en hızlı yolunun ne olduğunu söyleyebilir mi?

sadece ayrıştırmaya gerek kalmadan KONTROL ETMEK gerekir.

Bu şu soru ile aynı değildir: Bir dizenin sayı olup olmadığını nasıl belirleyebilirim?

soru sadece nasıl tanımlanacağı ile ilgili değildir. ancak en hızlı yöntem nedir?


2
w / o sadece ölçme int.tryparse tahmin ediyorum
kenny

Muhtemelen dizede yerel sözcük boyutlu veri parçalarını bir kayıt defterine okuyan ve daha sonra kayıttaki her bayt üzerinde bir aralık denetimi gerçekleştiren derlemede yazılmış bir döngü.
aroth

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

2
int.TryParse dizgenin yalnızca rakam içerip içermediğini kontrol etmez! "-13" gibi dizeler (eksi ve boşluklu) başarıyla ayrıştırılır.
aleyush

Yanıtlar:


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

    return true;
}

Muhtemelen bunu yapmanın en hızlı yolu olacak.


16
Ayrıcachar.IsDigit()
Keith

30
@ Keith yaklaşık üç yüz karakter daha IsDigitdöndürür true. Tam genişlikte ondalık basamaklar 0123... (Çin ve Japonya'da yaygın) ve diğer kültürlerden basamaklar ০১২௧௨௩௪꘤꘥꘦꘧꘨ve çok daha fazlası dahil.
CodesInChaos

62
Herkes return str.All(c => c >= '0' && c <= '9');
umursarsa

18
Sadece çok yapabilirsiniz: return str.All(char.IsDigit);. Yöntem grupları için Yaşasın!
Icemanind

11
Boş dizenin geçerli bir sayı olmadığını lütfen unutmayın.
Danon

64

Aynı dizenin 1000000 ayrışımına dayanan bazı karşılaştırmalar:

releaseİstatistikler için güncellendi :

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

İşte kod, IsDigitsOnly daha hızlı görünüyor:

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

Tabii ki TryParse'ın kültüre özgü sembollerin yanı sıra önde gelen / sondaki boşluklara da izin verdiğini belirtmek gerekir. Ayrıca ipin uzunluğu ile de sınırlıdır.


Bir sayıyı ayrıştırmak, temel dönüşüm gerçekleştirdiğiniz için her basamağı kontrol etmekten daha fazla zaman alır.

1
Aynı dizenin 1000 ayrımı, doğal gürültünün sonuçları önemsiz kıldığı zamanın altında, neredeyse hiç zaman almamalıdır . Yararlı zamanlamalar elde etmek için milyonlarca kez ayrıştırmayı beklerdim .
Jon Skeet

Çünkü downvoted ölçüttür yolu yararlı olamayacak kadar kısa ve size yöntem bile numunenin sen test için yanlış cevap verdiğini nokta değildi. Numune dize olan sadece rakamlardan oluşan, ancak bir için çok uzun olduğu için int, TryParse sahte dönüyor.
Jon Skeet

1m ile çok daha yakın. Uzunluk hakkında iyi bir nokta, bunu kaçırdım.
TheCodeKing

3
Ooh, derlemede / o + ile, artık int'den 5 kat daha hızlı. Sadece kontrol etmek için hata ayıklayıcıda koşmuyorsun değil mi?
Jon Skeet

59

Bunu sadece LINQ kullanarak yapabilirsiniz

return str.All(char.IsDigit);

  1. .All boş dizeler için true ve boş dizeler için istisna döndürür.
  2. char.IsDigit tüm Unicode karakterler için geçerlidir.

3
char.IsDigit, çeşitli yerel ayarlardan gelen çok sayıda unicode basamakla eşleşir (bkz. fileformat.info/info/unicode/category/Nd/list.htm ). Ayrıca, cevabınız LINQ kullanır, bu nedenle bunu yapmanın en hızlı yolu olması olası değildir . Yine de çoğu kullanım için yeterli olabilir.
Stephen Holt

1
@StephenHolt Evet haklısın, bunun en hızlı olması gerekmediğinin farkındayım, ama muhtemelen en kolay yazım.
Uday

Evet, doğru nokta. Birkaç yıl önce de benzer bir cevap yazdım (aşağıya bakın), ancak sürümüm, karakterin diğer yerlerden karakterleri ortadan kaldırmak için '0' ile '9' arasında olup olmadığını test etti. Bu kesin gereksinimlere bağlı olacaktır.
Stephen Holt

34

Karakter, zaten bunu yapan bir IsDigit'e (karakter c) sahiptir:

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

Bunu basitçe yapabilirsiniz:

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

Unicode basamaklarını kontrol etmeye özen gösterdiyseniz, daha hızlı kod için bile kötü kod olduğu için bir int'e karakter atamamalısınız.
user823959

1
@ user823959: Ne demek istediğinden emin değilim. Char.IsDigit, mscorelib'in bir parçasıdır: msdn.microsoft.com/en-us/library/0t641e58.aspx
38'de flayn

Gerhard özür dilerim, hatam.
user823959

Bu, döngüden daha özlüdür, ancak makinemde, bir milyondan fazla yineleme, döngü için her zaman ~ 1.5 kat daha hızlı
Sudhanshu Mishra

23

Başına sadece bir karşılaştırma kullanarak daha hızlı% 20 hakkında olabilir charve foryerine 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; 
}

Test için kullanılan kod (sonuçlar donanıma, sürümlere, sıraya vb. Bağlı olduğu için daima profil verin):

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

Intel i5-3470 @ 3.2GHz, VS 2015 .NET 4.6.1 Sürüm modu ve optimizasyonlar etkinleştirildi:

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 

Daha kısa yöntemleri kullanmaya cazip gelen herkes için,


14

Performans konusunda endişeleriniz varsa, ne kullanmayın - ne int.TryParsede Regexkendi (basit) fonksiyonunuzu yazın ( DigitsOnlyveya DigitsOnly2aşağıya, ama değil DigitsOnly3 - LINQ önemli bir ek yüke neden görünüyor).

Ayrıca, int.TryParsedize "sığmayacak" kadar uzun olursa başarısız olacağını unutmayın int.

Bu basit kriter ...

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

    }

}

... aşağıdaki sonucu verir ...

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

Boş doğrulama ile fonksiyon:

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

10

Linq'i seviyorum ve ilk uyumsuzluktan çıkmak için bunu yapabilirsiniz

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

8

Muhtemelen en hızlı yol:

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

Not: dizenizin boş olması durumunda yanlış olan True değerini döndürür (geçersiz sayı / basamak olarak boş olarak düşünmüyorsanız)


7

Bu çalışmalı:

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

int.Parseveya int.TryParseher zaman çalışmaz, çünkü dize bir int'in tutabileceği daha fazla rakam içerebilir.

Bu kontrolü bir kereden fazla yapacaksanız, derlenmiş bir normal ifadeyi kullanmak yararlı olacaktır - ilk kez daha fazla zaman alır, ancak bundan sonra çok daha hızlıdır.


3
bu yanlıştır, eğer bir rakam olsa bile doğru döner. buna rağmen fikir harika.
Nahum

1
Bu, en yavaş yöntemdir, ancak bilinmeyen dize boyutuna dayanan en iyi çözümdür. Belirtildiği gibi, normal ifade de bir tweak gerekiyor.
TheCodeKing

6

Bunu tek satırlık bir LINQ deyiminde yapabilirsiniz. Tamam, bunun mutlaka en hızlı olmadığını fark ediyorum, bu yüzden teknik olarak soruyu cevaplamıyor, ancak muhtemelen en kolayı:

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

4
str.All(char.IsDigit)yazmak daha da kolaydır, ancak elbette kodunuza eşdeğer değildir.
CodesInChaos

Bunu test etmeye çalıştım: pastebin.com/PuWBp9n1 sürümünde elbette hata ayıklayıcı yok ... ve WAYYYY daha hızlı görünüyor. @Jon Skeet biraz fikir verebilir misiniz? str.All (c => c> = '0' && c <= '9'), IsDigit'ten daha hızlı görünüyor
Nahum

1
@NahumLitvin unicode'u IsDigitdestekler. Bu nedenle, Microsoft bunu uygularken hangi zaman belleği ödünleşimlerine bağlı olarak, kontrol oldukça pahalı olabilir. Ben yerel kod ileriye, bu geçiş de oldukça pahalı olabilir varsayalım.
CodesInChaos

@CodesInChaos, "koduma eşdeğer değil" dediğinde, başka nelerin eşleşebileceğini kontrol etmeye gittim ve diğer yerel ayarlardaki (örneğin Arapça) rakamların sürümünüzde eşleşeceği ortaya çıktı. Sanırım OP bu tür rakamlar geçerli olsun ya da olmasın dikkate alması gereken bir şey. İnt.TryParse yaparken, bu tür karakterler içeren dizeleri kabul etmeyeceğini düşünüyorum.
Stephen Holt

LINQ her şeyi başarmanın en yavaş yoludur. Kodlamaya bir battaniye kuralı uygulamak istiyorsanız, bir şeyin sunduğu daha yüksek düzeyde ve işlevselliği varsayalım, daha yavaştır.
TravisO

3

Bu çok geç geliyor olabilir!, Ama eminim bana yardım ettiği için birine yardım edecek.

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

1

.IsMatch(string input, string pattern)C # 'daki yöntemi kullanarak giriş dizesini yalnızca rakamlara (0-9) sahip olacak şekilde test ederek Normal İfadeleri kullanmayı deneyebilirsiniz .

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

Saygılarımızla


3
Merhaba jason ve Stackoverflow'a hoş geldiniz. Yanıtladığınız için teşekkür ederiz, ancak sorunun en hızlı yolla ilgili olduğuna dikkat edin. Düzenli ifadeler nispeten yavaştır, bu diğer cevaplarda tartışılmıştır.
Nahum

1

bu mükemmel bir şekilde çalışacak, başka birçok yol var ama bu işe yarayacak

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

Bu kodu deneyin:

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

Çözümünüzün neden sağladığınızdan daha iyi olduğunu açıklayabilir misiniz?
Noel Widmer

Bu kodun çalıştırılma zamanı [o (1)] diğerlerinden daha az olduğu için [o (n)]
H. Borsipour

Convert.ToInt32O (n) 'den daha hızlı çalışırsam çok şaşırırdım . Bu varsayımı destekleyecek kanıtınız var mı?
BDL

1
str aslında bir sayı ise daha hızlı olabilir, ancak Exeption durumunda muhtemelen daha yavaş olur. Ayrıca soruyu cevaplamıyor çünkü str int int'den büyükse sayı çalışmaz.
Tomer Wolberg

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

Bu kod sorunu çözse de, neden / nasıl çalıştığına dair bir açıklama eklemelisiniz. Ve lütfen bu kodun neden sağlandığından daha iyi olduğunu düşündüğünüzü açıklayın.
BDL

1
Ayrıca: Kodunuz boş dizeler için True değerini döndürür.
BDL


-3

Dizenizi tespit etmenin çok akıllı ve kolay yolu sadece rakam içeriyor mu değil mi:

string s = "12fg";

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

İf koşulu gereksizdir, bu yüzden iki döndürme ifadesi de, s'yi geri döndürebilirsiniz.
alvarlagerlof
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.