วิธีที่เร็วที่สุดในการตรวจสอบว่าสตริงมีตัวเลขเท่านั้นหรือไม่


178

ฉันรู้วิธีตรวจสอบสิ่งนี้ regex, int.parse, tryparse, บ่วง

ใครสามารถบอกฉันได้ว่าเป็นวิธีที่เร็วที่สุดในการตรวจสอบคืออะไร

ความต้องการคือการตรวจสอบเท่านั้นไม่จำเป็นต้องแยกวิเคราะห์จริง

นี่ไม่ใช่คำถามเดียวกันกับ: ฉันจะระบุได้อย่างไรว่าสตริงเป็นตัวเลข?

คำถามไม่เพียงเกี่ยวกับวิธีการระบุ แต่เกี่ยวกับวิธีที่เร็วที่สุดคืออะไร


2
โดยไม่ต้องวัดฉันจะเดา int.tryparse
kenny

อาจเป็นลูปที่เขียนในชุดประกอบที่อ่านข้อมูลขนาดเนทีฟจากสตริงลงในรีจิสเตอร์จากนั้นทำการตรวจสอบช่วงบนแต่ละไบต์ในรีจิสเตอร์
aroth

35
เรียบง่ายreturn str.All(Char.IsDigit);
Mohsen

2
int.TryParse ไม่ตรวจสอบว่าสตริงมีตัวเลขเท่านั้น! สตริงเช่น "-13" (ที่มีเครื่องหมายลบและช่องว่าง) จะถูกวิเคราะห์คำสำเร็จ
aleyush

คำตอบ:


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

    return true;
}

อาจเป็นวิธีที่เร็วที่สุดที่จะทำ


16
นอกจากนี้ยังมีchar.IsDigit()
Keith

30
@Keith IsDigitส่งคืนtrueอักขระประมาณสามร้อยตัว รวมถึงตัวเลขทศนิยมแบบเต็มความกว้าง0123... (ทั่วไปในประเทศจีนและญี่ปุ่น) และตัวเลขจากวัฒนธรรมอื่น ๆ เช่น০১২௧௨௩௪꘤꘥꘦꘧꘨และอีกมากมาย
CodesInChaos

62
หากใครสนใจก็สามารถลดได้หนึ่งreturn str.All(c => c >= '0' && c <= '9');
บรรทัด

18
คุณสามารถทำสิ่งนี้ได้เช่นกัน: return str.All(char.IsDigit);. ไชโยสำหรับกลุ่มวิธี!
Icemanind

11
โปรดทราบว่าสตริงที่ว่างเปล่าไม่ใช่หมายเลข vaild
Danon

64

ต่อไปนี้เป็นเกณฑ์มาตรฐานบางส่วนจาก 1000000 parses ของสตริงเดียวกัน:

อัปเดตสำหรับreleaseสถิติ:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

นี่คือรหัสดูเหมือน IsDigitsOnly เร็วกว่า:

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

แน่นอนว่ามันเป็นเรื่องที่น่าสังเกตว่า TryParse นั้นอนุญาตให้มีช่องว่างนำหน้า / ต่อท้ายรวมถึงสัญลักษณ์เฉพาะวัฒนธรรม นอกจากนี้ยังมีข้อ จำกัด เกี่ยวกับความยาวของสตริง


การแยกตัวเลขต้องใช้เวลามากกว่าการตรวจสอบตัวเลขแต่ละหลักแน่นอนว่าคุณกำลังทำการแปลงฐาน

1
1000 แยกวิเคราะห์ของสตริงเดียวกันควรใช้เวลาเกือบไม่มีเวลาที่ทุกคนโดยวิธีการที่ดีภายใต้เวลาที่เสียงธรรมชาติทำให้ผลที่ไม่มีนัยสำคัญ ฉันคาดว่าจะต้องแยกวิเคราะห์มันเป็นล้านครั้งเพื่อให้ได้เวลาที่มีประโยชน์
Jon Skeet

Downvoted เพราะมาตรฐานเป็นวิธีที่สั้นเกินไปที่จะเป็นประโยชน์และคุณไม่ได้มองเห็นว่าวิธีการของคุณจะให้คำตอบที่ผิดแม้สำหรับตัวอย่างที่คุณกำลังทดสอบ สตริงตัวอย่างจะประกอบด้วยเฉพาะของตัวเลข แต่เป็นเพราะมันยาวเกินไปสำหรับint, TryParse คือกลับเท็จ
Jon Skeet

มันใกล้มากกับ 1m จุดที่ดีเกี่ยวกับความยาวฉันคิดถึงมัน
TheCodeKing

3
Ooh ด้วยการรวบรวม / o + ตอนนี้มันเร็วกว่า int.TryParse มากกว่า 5 เท่า เพื่อตรวจสอบว่าคุณไม่ได้ทำงานในโปรแกรมดีบั๊กหรือไม่
Jon Skeet

59

คุณสามารถทำได้โดยใช้ LINQ

return str.All(char.IsDigit);

  1. .All ส่งคืน true สำหรับสตริงว่างและข้อยกเว้นสำหรับสตริง null
  2. char.IsDigit เป็นจริงสำหรับอักขระ Unicode ทั้งหมด

3
char.IsDigit จับคู่ตัวเลข Unicode จำนวนมากจากโลแคลต่างๆ (ดูfileformat.info/info/unicode/category/Nd/list.htm ) นอกจากนี้คำตอบของคุณใช้ LINQ ดังนั้นจึงไม่น่าจะเป็นวิธีที่เร็วที่สุดที่จะทำ มันอาจจะเพียงพอสำหรับการ usecases ส่วนใหญ่
สตีเฟ่นโฮลต์

1
@StephenHolt ใช่คุณพูดถูกฉันรู้ว่าไม่จำเป็นต้องเร็วที่สุด แต่อาจเป็นวิธีที่ง่ายที่สุดในการเขียน
Uday

ใช่จุดยุติธรรม ฉันยังเขียนคำตอบที่คล้ายกัน (ดูด้านล่าง) ไม่กี่ปีที่ผ่านมาถึงแม้ว่ารุ่นของฉันเพิ่งทดสอบว่าถ่านอยู่ระหว่าง '0' และ '9' เพื่อกำจัดตัวอักษรจากที่อื่น ๆ ที่จะขึ้นอยู่กับข้อกำหนดที่แน่นอน
สตีเฟ่นโฮลท์

34

ถ่านมี IsDigit (ถ่าน c) อยู่แล้วซึ่งทำสิ่งนี้:

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

คุณสามารถทำได้:

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

หากคุณสนใจที่จะตรวจสอบตัวเลข Unicode คุณไม่ควรส่งถ่านไปยัง int เพราะมันเป็นรหัสที่ไม่ดีแม้แต่รหัสที่เร็วขึ้น
user823959

1
@ user823959: ฉันไม่แน่ใจว่าคุณหมายถึงอะไร Char.IsDigit เป็นส่วนหนึ่งของ mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

แกร์ฮาร์ดขอโทษที่ฉันทำผิด
user823959

นี่จะกระชับกว่าการวนซ้ำ แต่ในเครื่องของฉันมากกว่าหนึ่งล้านการวนซ้ำสำหรับการวนซ้ำจะเร็วกว่าเสมอ ~ 1.5 เท่า
Sudhanshu Mishra

23

สามารถเร็วขึ้นประมาณ 20% โดยใช้การเปรียบเทียบเพียงครั้งเดียวต่อครั้งcharและforแทน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; 
}

รหัสที่ใช้สำหรับการทดสอบ (โปรไฟล์เสมอเนื่องจากผลลัพธ์ขึ้นอยู่กับฮาร์ดแวร์รุ่นคำสั่ง ฯลฯ ):

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 โหมด Release และเปิดใช้งานการปรับให้เหมาะสม:

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 

สำหรับผู้ที่ต้องการใช้วิธีที่สั้นกว่านั้นโปรดทราบว่า


14

หากคุณกังวลเกี่ยวกับประสิทธิภาพการทำงานอย่าใช้int.TryParseหรือRegex- เขียนฟังก์ชั่น (ง่าย) ของคุณเอง ( DigitsOnlyหรือ DigitsOnly2ด้านล่าง) แต่ไม่ใช่ DigitsOnly3 - LINQ ดูเหมือนจะมีค่าใช้จ่ายที่สำคัญ)

และโปรดระวังด้วยว่าint.TryParseจะล้มเหลวหากสตริงยาวเกินไปที่จะ "พอดี"intลงใน

มาตรฐานที่เรียบง่ายนี้ ...

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

    }

}

... สร้างผลลัพธ์ต่อไปนี้ ...

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

ฟังก์ชั่นที่มีการตรวจสอบที่ว่างเปล่า:

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

10

ฉันชอบ Linq และเพื่อให้มันออกจากการจับคู่ครั้งแรกคุณสามารถทำได้

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

8

อาจเป็นวิธีที่เร็วที่สุดคือ:

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

หมายเหตุ:มันจะกลับมาจริงในกรณีที่สตริงของคุณว่างเปล่าซึ่งไม่ถูกต้อง (หากคุณไม่ได้พิจารณาว่าว่างเปล่าเป็นตัวเลข / หลักที่ถูกต้อง)


7

สิ่งนี้น่าจะใช้ได้:

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

int.Parseหรือint.TryParseไม่สามารถใช้งานได้เสมอเพราะสตริงอาจมีตัวเลขมากกว่าที่ int สามารถถือ

หากคุณจะทำการตรวจสอบนี้มากกว่าหนึ่งครั้งจะเป็นประโยชน์ในการใช้ regex ที่รวบรวม - มันต้องใช้เวลามากขึ้นในครั้งแรก แต่จะเร็วกว่ามากหลังจากนั้น


3
นี่เป็นสิ่งที่ผิดมันจะกลับมาจริงถ้ามีตัวเลขหนึ่งหลัก แม้ว่าความคิดที่ปฏิบัติตามจะยอดเยี่ยม
Nahum

1
นี่เป็นวิธีที่ช้าที่สุด แต่เป็นวิธีที่ดีที่สุดโดยพิจารณาจากขนาดของสตริงที่ไม่รู้จัก ดังที่ได้กล่าวมาแล้ว regex ยังต้องการการปรับแต่ง
TheCodeKing

6

คุณสามารถทำได้ในหนึ่งบรรทัดคำสั่ง LINQ ตกลงฉันรู้ว่านี่ไม่จำเป็นต้องเร็วที่สุดดังนั้นจึงไม่ตอบคำถามทางเทคนิค แต่อาจเป็นวิธีที่ง่ายที่สุดในการเขียน:

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

4
str.All(char.IsDigit)ง่ายกว่าในการเขียน แต่แน่นอนไม่เทียบเท่ากับรหัสของคุณ
CodesInChaos

ฉันพยายามทดสอบนี้: pastebin.com/PuWBp9n1 ในการเปิดตัวไม่มีการดีบักแน่นอน ... และดูเหมือนว่า WAYYYY เร็วขึ้น @ Jon Skeet คุณสามารถให้ข้อมูลเชิงลึกบางอย่างได้หรือไม่ str.All (c => c> = '0' && c <= '9') ดูเหมือนว่าจะเร็วกว่า IsDigit
Nahum

1
@NahumLitvin IsDigitรองรับ Unicode ดังนั้นขึ้นอยู่กับการแลกเปลี่ยนหน่วยความจำเวลาที่ Microsoft เลือกเมื่อใช้งานการตรวจสอบอาจมีราคาแพง ฉันคิดว่ามันจะส่งต่อไปยังโค้ดเนทีฟการเปลี่ยนแปลงนั้นอาจมีราคาแพงเช่นกัน
CodesInChaos

@CodesInChaos เมื่อคุณบอกว่ามัน "ไม่เทียบเท่ากับรหัสของฉัน" ฉันไปตรวจสอบสิ่งที่อาจจะตรงกันและปรากฎว่าตัวเลขในสถานที่อื่น (เช่นภาษาอาหรับ) จะตรงกับรุ่นของคุณ ฉันเดาว่ามันเป็นสิ่งที่ OP จะต้องพิจารณาไม่ว่าตัวเลขนั้นจะถูกต้องหรือไม่ก็ตาม เมื่อทำ int.TryParse ฉันคิดว่าจะไม่ยอมรับสตริงที่มีอักขระดังกล่าว
Stephen Holt

LINQ เป็นวิธีที่ช้าที่สุดที่จะทำสิ่งใดให้สำเร็จ หากคุณต้องการใช้กฎแบบครอบคลุมกับการเข้ารหัสสมมติว่ามีสิ่งที่เสนอในระดับสูง & ฟังก์ชันการทำงานที่มากขึ้นก็จะช้าลง
TravisO

3

นี่อาจจะช้าไป! แต่ฉันแน่ใจว่ามันจะช่วยใครซักคนเพราะมันช่วยฉันได้

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

1

คุณสามารถลองใช้นิพจน์ปกติได้โดยทดสอบสตริงอินพุตให้มีเฉพาะตัวเลข (0-9) โดยใช้.IsMatch(string input, string pattern)วิธีการใน 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;
        }
    }
}

ความนับถือ


3
สวัสดีเจสันและยินดีต้อนรับสู่ Stackoverflow ขอบคุณสำหรับการตอบกลับ แต่โปรดสังเกตว่าคำถามนั้นเกี่ยวกับวิธีที่เร็วที่สุด การแสดงออกปกติค่อนข้างช้านี้ถูกกล่าวถึงในคำตอบอื่น ๆ
Numum

1

นี้จะทำงานได้อย่างสมบูรณ์แบบมีหลายวิธี แต่จะทำงานได้

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

ลองรหัสนี้:

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

คุณช่วยอธิบายได้หรือไม่ว่าทำไมทางออกของคุณจึงดีกว่าโซลูชันที่ให้มา?
Noel Widmer

เนื่องจากลำดับเวลาของการเรียกใช้รหัสนี้ [o (1)] นั้นน้อยกว่ารหัสอื่น ๆ [o (n)]
H. Borsipour

ฉันจะประหลาดใจมากถ้าConvert.ToInt32วิ่งเร็วกว่า o (n) คุณมีหลักฐานที่จะสนับสนุนสมมติฐานนี้หรือไม่
BDL

1
มันอาจจะเร็วกว่าถ้า str เป็นจำนวนจริง แต่มันอาจจะช้ากว่าในกรณีของ Exeption นอกจากนี้ยังไม่ตอบคำถามเพราะจะไม่ทำงานหาก STR มีจำนวนมากกว่า int.MaxValue
Tomer Wolberg

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

แม้ว่ารหัสนี้อาจแก้ปัญหาได้ แต่คุณควรเพิ่มคำอธิบายว่าทำไม / ทำงานอย่างไร และโปรดอธิบายว่าทำไมคุณคิดว่ารหัสนี้ดีกว่าที่ระบุไว้แล้ว
BDL

1
นอกจากนี้: รหัสของคุณจะคืนค่า True สำหรับสตริงว่าง
BDL


-3

วิธีที่ชาญฉลาดและง่ายต่อการตรวจสอบสตริงของคุณมีตัวเลขเท่านั้น

string s = "12fg";

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

เงื่อนไข if ไม่จำเป็นดังนั้นเป็นสอง statement return คุณสามารถส่งคืน s.All ... แต่มีปัญหาอื่น ๆ เช่นสตริงว่าง
alvarlagerlof
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.