Cara tercepat untuk memeriksa apakah string hanya berisi digit


178

Saya tahu beberapa cara bagaimana memeriksanya. regex int.parse,,tryparse , perulangan.

Adakah yang bisa memberi tahu saya apa cara tercepat untuk memeriksa?

yang perlu hanya PERIKSA tidak perlu benar-benar parse.

ini bukan pertanyaan yang sama dengan: Bagaimana cara mengidentifikasi jika string adalah angka?

pertanyaannya bukan hanya tentang bagaimana mengidentifikasi. tetapi tentang apa metode tercepat .


2
tanpa mengukur saya kira int.tryparse
kenny

Mungkin sebuah loop yang ditulis dalam rakitan yang membaca potongan-potongan data berukuran asli dari string ke dalam register dan kemudian melakukan pemeriksaan jangkauan pada setiap byte dalam register.
aroth

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

2
int.TryParse tidak memeriksa apakah string hanya berisi digit! String seperti "-13" (dengan minus dan spasi) akan berhasil diuraikan.
aleyush

Jawaban:


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

    return true;
}

Mungkin akan menjadi cara tercepat untuk melakukannya.


16
Ada jugachar.IsDigit()
Keith

30
@Keith IsDigitkembali trueuntuk sekitar tiga ratus karakter lagi. Termasuk digit desimal lebar penuh 0123... (umum di Cina dan Jepang) dan digit dari budaya lain, misalnya, ০১২௧௨௩௪꘤꘥꘦꘧꘨dan banyak lagi.
CodesInChaos

62
kalau ada yang peduli, ini tentu bisa direduksi menjadi satu-liner ->return str.All(c => c >= '0' && c <= '9');
Jonesopolis

18
Anda hanya bisa melakukan ini juga: return str.All(char.IsDigit);. Hore untuk grup metode!
Icemanind

11
Harap dicatat bahwa string kosong bukan nomor vaild.
Danon

64

Berikut adalah beberapa tolok ukur berdasarkan 1000000 parses dari string yang sama:

Diperbarui untuk releasestatistik:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

Berikut kodenya, sepertinya IsDigitsOnly lebih cepat:

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

Tentu saja patut dicatat bahwa TryParse mengizinkan spasi putih terkemuka / tambahan serta simbol spesifik budaya. Ini juga terbatas pada panjang tali.


Memilah nomor pasti membutuhkan lebih banyak waktu daripada hanya memeriksa setiap digit, saat Anda melakukan konversi basis.

1
1000 parse dari string yang sama seharusnya tidak membutuhkan waktu sama sekali, omong-omong, jauh di bawah waktu di mana noise alami membuat hasil tidak signifikan. Saya berharap harus menguraikannya jutaan kali untuk mendapatkan timing yang berguna.
Jon Skeet

Downvoted karena patokan adalah cara terlalu pendek untuk menjadi berguna dan Anda tidak melihat bahwa metode Anda memberikan jawaban yang salah bahkan untuk sampel Anda pengujian sedang. String sampel yang hanya terdiri dari angka, tapi karena terlalu lama untuk int, TryParse ini kembali palsu.
Jon Skeet

Jauh lebih dekat dengan 1m. Ah poin bagus tentang panjangnya, aku melewatkan itu.
TheCodeKing

3
Ooh, dengan / o + pada kompilasi, sekarang lebih dari 5 kali lebih cepat dari int.TryParse. Hanya untuk memeriksa, Anda tidak menjalankan debugger bukan?
Jon Skeet

59

Anda bisa melakukannya dengan menggunakan LINQ

return str.All(char.IsDigit);

  1. .All mengembalikan true untuk string kosong dan pengecualian untuk string null.
  2. char.IsDigit ini berlaku untuk semua karakter Unicode.

3
char.IsDigit mencocokkan banyak digit unicode dari berbagai lokal (lihat fileformat.info/info/unicode/category/Nd/list.htm ). Juga, jawaban Anda menggunakan LINQ sehingga tidak mungkin menjadi cara tercepat untuk melakukannya. Mungkin cukup untuk sebagian besar usecases.
Stephen Holt

1
@StephenHolt Ya Anda benar, saya menyadari itu belum tentu yang tercepat, tetapi mungkin yang paling mudah untuk ditulis.
Uday

Ya, titik adil. Saya juga menulis jawaban yang sama (lihat di bawah) beberapa tahun yang lalu, walaupun versi saya hanya menguji apakah char itu antara '0' dan '9' untuk menghilangkan karakter dari lokasi lain. Itu akan tergantung pada persyaratan yang tepat.
Stephen Holt

34

Char sudah memiliki IsDigit (char c) yang melakukan ini:

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

Anda bisa melakukan ini:

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

Jika Anda peduli untuk memeriksa angka Unicode, Anda tidak seharusnya melemparkan char ke int hanya karena itu kode yang buruk, bahkan untuk kode yang lebih cepat.
user823959

1
@ user823959: Saya tidak yakin apa yang Anda maksud. Char.IsDigit adalah bagian dari mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Gerhard maaf, kesalahan saya.
user823959

Ini lebih ringkas daripada perulangan, tetapi pada komputer saya, lebih dari satu juta iterasi, untuk perulangan selalu lebih cepat ~ 1,5 kali
Sudhanshu Mishra

23

Dapat sekitar 20% lebih cepat dengan menggunakan hanya satu perbandingan per chardan forbukannya 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; 
}

Kode yang digunakan untuk pengujian (selalu profil karena hasilnya tergantung pada perangkat keras, versi, pesanan, dll.):

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

Hasil pada Intel i5-3470 @ 3.2GHz, VS 2015 .NET 4.6.1 Mode rilis dan pengoptimalan diaktifkan:

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 

Bagi siapa pun yang tergoda untuk menggunakan metode yang lebih pendek, perhatikan itu


14

Jika Anda khawatir tentang kinerja, int.TryParsejangan gunakan atau Regextulis fungsi Anda sendiri (sederhana) ( DigitsOnlyatau di DigitsOnly2bawah, tetapi tidak DigitsOnly3 - LINQ tampaknya mengeluarkan overhead yang signifikan).

Perlu diketahui juga bahwa int.TryParseakan gagal jika string terlalu panjang untuk "cocok"int .

Tolok ukur sederhana ini ...

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

    }

}

... menghasilkan hasil berikut ...

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

Fungsi dengan validasi kosong:

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

10

Saya suka Linq dan untuk membuatnya keluar pada ketidakcocokan pertama Anda bisa melakukan ini

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

8

Mungkin cara tercepat adalah:

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

Catatan: itu akan mengembalikan True jika string Anda kosong yang tidak benar (jika Anda tidak menganggap kosong sebagai angka / digit yang valid)


7

Ini seharusnya bekerja:

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

int.Parse atau int.TryParse tidak akan selalu berfungsi, karena string mungkin berisi lebih banyak digit yang dapat disimpan oleh suatu int.

Jika Anda akan melakukan pemeriksaan ini lebih dari sekali, berguna untuk menggunakan regex yang dikompilasi - ini membutuhkan lebih banyak waktu pertama kali, tetapi jauh lebih cepat setelah itu.


3
ini salah, ia mengembalikan true jika ada satu digit. meskipun ide yang dipatuhi mengagumkan.
Nahum

1
Sejauh ini, ini adalah metode paling lambat, tetapi merupakan solusi terbaik berdasarkan ukuran string yang tidak diketahui. Seperti yang disebutkan regex juga perlu di-tweak.
TheCodeKing

6

Anda dapat melakukan ini dalam pernyataan LINQ satu baris. OK, saya menyadari ini belum tentu yang tercepat, jadi tidak secara teknis menjawab pertanyaan, tetapi mungkin yang paling mudah untuk ditulis:

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

4
str.All(char.IsDigit)bahkan lebih mudah untuk ditulis, tetapi tentu saja tidak setara dengan kode Anda.
CodesInChaos

Saya mencoba untuk menguji ini: pastebin.com/PuWBp9n1 pada rilis tidak ada debugger tentu saja ... dan sepertinya WAYYYY lebih cepat. @ Jon Skeet dapatkah Anda memberikan wawasan? str.All (c => c> = '0' && c <= '9') tampaknya WAY lebih cepat daripada IsDigit
Nahum

1
@NahumLitvin IsDigit mendukung unicode. Jadi, tergantung pada time-memory trade-off yang dipilih Microsoft saat mengimplementasikannya, cek mungkin cukup mahal. Saya berasumsi bahwa itu meneruskan ke kode asli, transisi itu bisa sangat mahal juga.
CodesInChaos

@CodesInChaos ketika Anda mengatakan itu "tidak setara dengan kode saya" Saya pergi untuk memeriksa apa yang mungkin cocok, dan ternyata digit di lokal lain (misalnya bahasa Arab) akan cocok dengan versi Anda. Saya kira itu adalah sesuatu yang perlu dipertimbangkan OP, apakah angka tersebut valid atau tidak. Saat melakukan int.TryParse, saya pikir itu tidak akan menerima string yang mengandung karakter tersebut.
Stephen Holt

LINQ adalah cara paling lambat untuk mencapai apa pun. Jika Anda ingin menerapkan aturan selimut pada pengkodean, anggap semakin tinggi tingkat & fungsionalitas sesuatu yang ditawarkan, semakin lambat.
TravisO

3

Ini mungkin datang sangat terlambat !, tapi saya yakin itu akan membantu seseorang, karena itu membantu saya.

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

1

Anda dapat mencoba menggunakan Ekspresi Reguler dengan menguji string input hanya memiliki digit (0-9) dengan menggunakan .IsMatch(string input, string pattern)metode dalam 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;
        }
    }
}

Salam


3
Hai jason dan selamat datang di Stackoverflow. Terima kasih telah menjawab tetapi perhatikan bahwa pertanyaannya adalah tentang cara tercepat. Ekspresi reguler relatif lambat ini dibahas dalam jawaban lain.
Nahum

1

ini akan bekerja dengan sempurna, ada banyak cara lain tetapi ini akan berhasil

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

Coba kode ini:

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

Bisakah Anda menjelaskan mengapa solusi Anda lebih baik daripada yang sudah disediakan?
Noel Widmer

Karena urutan waktu menjalankan kode ini [o (1)] kurang dari yang lain [o (n)]
H. Borsipour

Saya akan sangat terkejut jika Convert.ToInt32akan berlari lebih cepat dari o (n). Apakah Anda memiliki bukti untuk mendukung asumsi ini?
BDL

1
mungkin lebih cepat jika str sebenarnya adalah angka, tetapi mungkin akan lebih lambat jika Eksepsi. Juga tidak menjawab pertanyaan karena itu tidak akan berfungsi jika str adalah angka lebih besar dari int.MaxValue.
Tomer Wolberg

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

Meskipun kode ini dapat menyelesaikan masalah, Anda harus menambahkan penjelasan mengapa / cara kerjanya. Dan tolong jelaskan mengapa Anda berpikir bahwa kode ini lebih baik daripada yang sudah disediakan.
BDL

1
Selain itu: Kode Anda mengembalikan True untuk string kosong.
BDL


-3

Cara yang sangat cerdik dan mudah mendeteksi string Anda hanya mengandung digit atau tidak, dengan cara ini:

string s = "12fg";

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

Jika kondisi tidak diperlukan, begitu juga dua pernyataan pengembalian, Anda bisa mengembalikan s. Semua ... Tapi ada masalah lain seperti dengan string kosong.
alvarlagerlof
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.