Cách nhanh nhất để kiểm tra nếu chuỗi chỉ chứa chữ số


178

Tôi biết một vài cách để kiểm tra điều này. regex, int.parse, tryparse, vòng lặp.

bất cứ ai có thể cho tôi biết cách nhanh nhất để kiểm tra là gì?

nhu cầu là chỉ KIỂM TRA không cần phải phân tích cú pháp.

đây không phải là câu hỏi giống như: Làm thế nào để tôi xác định nếu một chuỗi là một số?

câu hỏi không chỉ là về cách xác định. Nhưng về phương pháp nhanh nhất là gì .


2
w / o chỉ cần đo tôi sẽ đoán int.tryparse
kenny

Có lẽ là một vòng lặp được viết trong cụm để đọc các khối dữ liệu có kích thước từ gốc từ chuỗi vào một thanh ghi và sau đó thực hiện kiểm tra phạm vi trên mỗi byte trong thanh ghi.
aroth

35
đơn giảnreturn str.All(Char.IsDigit);
Mohsen

2
int.TryPude không kiểm tra nếu chuỗi chỉ chứa các chữ số! Các chuỗi như "-13" (có dấu trừ và dấu cách) sẽ được phân tích cú pháp thành công.
aleyush

Câu trả lời:


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

    return true;
}

Có lẽ sẽ là cách nhanh nhất để làm điều đó.


16
Cũng cóchar.IsDigit()
Keith

30
@Keith IsDigittrả lại truecho khoảng ba trăm ký tự nữa. Bao gồm các chữ số thập phân có chiều rộng đầy đủ 0123... (phổ biến ở Trung Quốc và Nhật Bản) và các chữ số từ các nền văn hóa khác, vd ০১২௧௨௩௪꘤꘥꘦꘧꘨và nhiều hơn nữa.
CodeInChaos

62
nếu bất cứ ai quan tâm, điều này chắc chắn có thể được giảm xuống thành một lớp lót ->return str.All(c => c >= '0' && c <= '9');
Jonesopolis

18
Bạn chỉ có thể làm điều này quá : return str.All(char.IsDigit);. Hoan hô cho các nhóm phương pháp!
Icemanind

11
Xin lưu ý rằng chuỗi rỗng không phải là số vaild.
Danon

64

Dưới đây là một số điểm chuẩn dựa trên 1000000 phân tích cú pháp của cùng một chuỗi:

Cập nhật cho releasesố liệu thống kê:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

Đây là mã, có vẻ như IsDigitsOnly nhanh hơn:

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

Tất nhiên, đáng chú ý là TryPude không cho phép khoảng trắng hàng đầu / dấu cũng như các biểu tượng cụ thể về văn hóa. Nó cũng bị giới hạn về độ dài của chuỗi.


Phân tích một số chắc chắn mất nhiều thời gian hơn là chỉ kiểm tra từng chữ số, khi bạn đang thực hiện chuyển đổi cơ sở.

1
Nhân tiện, 1000 phân tích của cùng một chuỗi sẽ gần như không mất thời gian, trong thời gian mà tiếng ồn tự nhiên làm cho kết quả không đáng kể. Tôi hy vọng sẽ phải phân tích cú pháp một triệu lần để có được thời gian hữu ích.
Jon Skeet

Downvoted vì điểm chuẩn là cách quá ngắn để có ích bạn không nhận ra rằng phương pháp của bạn là đưa ra các câu trả lời sai ngay cả đối với các mẫu mà bạn thử nghiệm đang. Chuỗi mẫu được chỉ gồm các chữ số, nhưng vì nó quá dài cho một int, TryParse được trả về false.
Jon Skeet

Nó gần hơn rất nhiều với 1m. Ah điểm tốt về chiều dài, tôi đã bỏ lỡ điều đó.
TheCodeKing

3
Ôi, với / o + khi biên dịch, giờ đây nhanh hơn 5 lần so với int.TryPude. Chỉ cần kiểm tra, bạn không chạy trong trình gỡ lỗi phải không?
Jon Skeet

59

Bạn chỉ có thể làm điều này bằng cách sử dụng LINQ

return str.All(char.IsDigit);

  1. .All trả về true cho chuỗi rỗng và ngoại lệ cho chuỗi null.
  2. char.IsDigit đúng với tất cả các ký tự Unicode.

3
char.IsDigit khớp với nhiều chữ số unicode từ nhiều địa phương khác nhau (xem fileformat.info/info/unicode/c Ab / Nd / list.htmlm ). Ngoài ra, câu trả lời của bạn sử dụng LINQ nên không chắc là cách nhanh nhất để làm điều đó. Nó có thể là đủ cho hầu hết các giai đoạn mặc dù.
Stephen Holt

1
@StephenHolt Có bạn nói đúng, tôi nhận ra rằng đó không hẳn là nhanh nhất, nhưng có lẽ nó dễ viết nhất.
Uday

Đúng, điểm công bằng. Tôi cũng đã viết một câu trả lời tương tự (xem bên dưới) một vài năm trước, mặc dù phiên bản của tôi chỉ thử nghiệm nếu char nằm trong khoảng '0' đến '9' để loại bỏ ký tự từ các địa phương khác. Điều đó sẽ phụ thuộc vào các yêu cầu chính xác.
Stephen Holt

34

Char đã có IsDigit (char c) thực hiện điều này:

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

Bạn chỉ có thể làm điều này:

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

Nếu bạn quan tâm để kiểm tra các chữ số Unicode, bạn không nên chuyển char thành int chỉ vì đó là mã xấu, ngay cả đối với mã nhanh hơn.
user823959

1
@ user823959: Tôi không chắc ý của bạn là gì. Char.IsDigit là một phần của mscorelib: msdn.microsoft.com/en-us/l
Library / 0t641e58.aspx

Gerhard xin lỗi, lỗi của tôi.
user823959

Điều này ngắn gọn hơn vòng lặp, nhưng trên máy của tôi, hơn một triệu lần lặp, vòng lặp luôn nhanh hơn ~ 1,5 lần
Sudhanshu Mishra

23

Có thể nhanh hơn khoảng 20% ​​bằng cách chỉ sử dụng một so sánh trên charforthay vì 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; 
}

Mã được sử dụng để kiểm tra (luôn cấu hình vì kết quả phụ thuộc vào phần cứng, phiên bản, đơn đặt hàng, v.v.):

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

Kết quả trên Intel i5-3470 @ 3.2GHz, VS 2015 .NET 4.6.1 Chế độ phát hành và tối ưu hóa được bật:

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 

Đối với bất kỳ ai muốn sử dụng các phương pháp ngắn hơn, lưu ý rằng


14

Nếu bạn lo lắng về hiệu suất, sử dụng không phải int.TryParsevà cũng không Regex- write (đơn giản) của chức năng của bạn ( DigitsOnlyhoặc DigitsOnly2dưới đây, nhưng không DigitsOnly3 - LINQ dường như phải chịu một chi phí đáng kể).

Ngoài ra, hãy lưu ý rằng int.TryParsesẽ thất bại nếu chuỗi quá dài để "khớp" vào int.

Điểm chuẩn đơn giản này ...

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

    }

}

... tạo ra kết quả như sau ...

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

Chức năng với xác nhận trống:

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

10

Tôi thích Linq và để làm cho nó thoát khỏi sự không phù hợp đầu tiên, bạn có thể làm điều này

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

8

Có lẽ cách nhanh nhất là:

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

Lưu ý: nó sẽ trả về True trong trường hợp chuỗi của bạn trống không chính xác (nếu bạn không coi trống là số / chữ số hợp lệ)


7

Điều này sẽ làm việc:

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

int.Parsehoặc int.TryParsesẽ không luôn hoạt động, bởi vì chuỗi có thể chứa nhiều chữ số hơn mà một int có thể giữ.

Nếu bạn định thực hiện kiểm tra này nhiều lần thì sẽ hữu ích khi sử dụng regex đã biên dịch - lần đầu tiên sẽ mất nhiều thời gian hơn, nhưng sau đó nhanh hơn nhiều.


3
Điều này là sai, nó trả về đúng nếu chỉ có một chữ số. mặc dù ý tưởng tuân thủ là tuyệt vời.
Nahum

1
Đây là phương pháp chậm nhất, nhưng là giải pháp tốt nhất dựa trên kích thước chuỗi không xác định. Như đã đề cập, regex cũng cần một tinh chỉnh.
TheCodeKing

6

Bạn có thể làm điều này trong một câu lệnh LINQ một dòng. OK, tôi nhận ra rằng đây không nhất thiết là nhanh nhất, vì vậy về mặt kỹ thuật không trả lời câu hỏi, nhưng có lẽ nó dễ viết nhất:

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

4
str.All(char.IsDigit)thậm chí còn dễ viết hơn, nhưng tất nhiên không tương đương với mã của bạn.
CodeInChaos

Tôi đã thử kiểm tra điều này: pastebin.com/PuWBp9n1 khi phát hành không có trình gỡ lỗi tất nhiên ... và có vẻ như WAYYYY nhanh hơn. @Jon Skeet bạn có thể cung cấp một số cái nhìn sâu sắc? str.All (c => c> = '0' && c <= '9') dường như CÁCH nhanh hơn IsDigit
Nahum

1
@NahumLitvin IsDigithỗ trợ unicode. Vì vậy, tùy thuộc vào sự đánh đổi bộ nhớ thời gian mà Microsoft đã chọn khi triển khai nó, việc kiểm tra có thể khá tốn kém. Tôi giả định rằng nó chuyển tiếp sang mã gốc, quá trình chuyển đổi đó cũng có thể khá tốn kém.
CodeInChaos

@CodesInChaos khi bạn nói nó "không tương đương với mã của tôi" Tôi đã đi kiểm tra xem cái gì khác có thể khớp và hóa ra các chữ số ở các địa phương khác (ví dụ tiếng Ả Rập) sẽ khớp với phiên bản của bạn. Tôi đoán đó là điều mà OP sẽ cần xem xét, cho dù những chữ số đó có hợp lệ hay không. Khi thực hiện int.TryPude, tôi nghĩ rằng sẽ không chấp nhận các chuỗi có chứa các ký tự như vậy.
Stephen Holt

LINQ là cách chậm nhất để hoàn thành bất cứ điều gì. Nếu bạn muốn áp dụng quy tắc mền cho mã hóa, giả sử mức độ cao hơn & chức năng mà một cái gì đó cung cấp, nó càng chậm.
TravisO

3

Điều này có thể đến siêu muộn!, Nhưng tôi chắc chắn rằng nó sẽ giúp được ai đó, vì nó đã giúp tôi.

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

1

Bạn có thể thử sử dụng Biểu thức chính quy bằng cách kiểm tra chuỗi đầu vào chỉ có các chữ số (0-9) bằng cách sử dụng .IsMatch(string input, string pattern)phương thức trong 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;
        }
    }
}

Trân trọng


3
Xin chào jason và chào mừng bạn đến với Stackoverflow. Cảm ơn bạn đã trả lời nhưng lưu ý rằng câu hỏi là về cách nhanh nhất. Biểu thức chính quy tương đối chậm, điều này đã được thảo luận trong các câu trả lời khác.
Nahum

1

Điều này sẽ hoạt động hoàn hảo, có nhiều cách khác nhưng điều này sẽ làm việc

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

Hãy thử mã này:

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

Bạn có thể giải thích tại sao giải pháp của bạn tốt hơn những giải pháp đã được cung cấp không?
Noel Widmer

Bởi vì thứ tự thời gian chạy mã này [o (1)] ít hơn các mã khác [o (n)]
H. Borsipour

Tôi sẽ rất ngạc nhiên nếu Convert.ToInt32chạy nhanh hơn o (n). Bạn có bất cứ bằng chứng nào ủng hộ giả định này không?
BDL

1
nó có thể nhanh hơn nếu str thực sự là một con số, nhưng nó có thể sẽ chậm hơn trong trường hợp Exception. Ngoài ra, nó không trả lời câu hỏi vì nó sẽ không hoạt động nếu str lớn hơn int.MaxValue.
Tomer Wolberg

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

Mặc dù mã này có thể giải quyết vấn đề, bạn nên thêm một lời giải thích tại sao / cách thức hoạt động của nó. Và hãy giải thích lý do tại sao bạn nghĩ rằng mã này tốt hơn mã đã được cung cấp.
BDL

1
Ngoài ra: Mã của bạn trả về True cho chuỗi rỗng.
BDL


-3

Cách rất thông minh và dễ dàng để phát hiện chuỗi của bạn chỉ chứa các chữ số hay không là cách này:

string s = "12fg";

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

Điều kiện if là không cần thiết, hai câu lệnh return cũng vậy, bạn chỉ có thể trả về s. Tất cả ... Nhưng có những vấn đề khác như với chuỗi rỗng.
alvarlagerlof
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.