检查字符串是否仅包含数字的最快方法


177

我知道几种检查方法。正则表达式,int.parsetryparse,循环。

谁能告诉我最快的检查方法是什么?

需要的只是检查,不需要实际解析。

这与以下问题不同:如何确定字符串是否为数字?

问题不仅在于如何识别。但是关于什么是最快的方法。


2
W / O只是衡量我猜int.tryparse
肯尼

可能是一个用汇编语言编写的循环,该循环将字符串中本机字大小的数据块读取到寄存器中,然后对寄存器中的每个字节执行范围检查。
aroth

35
只是return str.All(Char.IsDigit);
Mohsen

2
int.TryParse不检查字符串是否仅包含数字!诸如“ -13”(带减号和空格)的字符串将被成功解析。
aleyush

Answers:


258
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大约300个字符。包括全角十进制数字0123...(在中国和日本很常见)以及其他文化的数字,例如০১২௧௨௩௪꘤꘥꘦꘧꘨等等。
CodesInChaos

62
如果有人在乎,那肯定可以简化为return str.All(c => c >= '0' && c <= '9');
单线

18
您也可以这样做:return str.All(char.IsDigit);。方法组万岁!
Icemanind

11
请注意,空字符串不是有效数字。

63

以下是基于同一字符串的1000000个解析的一些基准:

更新了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个解析几乎根本不需要时间。我期望有分析它一百万次,以获得有用的时机。
乔恩·斯基特

Downvoted因为基准测试方法太短是有用的你没有发现,你的方法是给错误的答案,即使是样品,你正在测试。样本串仅由数字,而是因为它是太长了int,是的TryParse返回false。
乔恩·斯基特

1m更近了。关于长度好点啊,我错过了。
TheCodeKing 2011年

3
哦,编译时带有/ o +,它现在比int.TryParse快5倍以上。只是检查一下,您没有在调试器中运行?
乔恩·斯基特

58

您可以简单地使用LINQ做到这一点

return str.All(char.IsDigit);

  1. .All 对于空字符串返回true,对于空字符串返回异常。
  2. char.IsDigit 对于所有Unicode字符均为true。

3
char.IsDigit匹配来自各种语言环境的众多unicode数字(请参阅fileformat.info/info/unicode/category/Nd/list.htm)。另外,您的答案使用LINQ,因此不可能是最快的方法。不过,对于大多数用例而言,这可能就足够了。
斯蒂芬·霍尔特

1
@StephenHolt是的,您是对的,我知道这不一定是最快的,但它可能是最容易编写的。
Uday

是的,很公平。几年前,我也写了一个类似的答案(见下文),尽管我的版本只是测试了char是否在'0'和'9'之间,以消除其他语言环境中的char。那将取决于确切的要求。
斯蒂芬·霍尔特

34

字符已经具有一个IsDigit(char 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数字,则不应该仅仅因为它是不好的代码而将char强制转换为int,甚至对于更快的代码也是如此。
user823959 2013年

1
@ user823959:我不确定您的意思。Char.IsDigit是mscorelib的一部分:msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn 2013年

Gerhard对不起,我的错。
user823959 2013年

这比循环更简洁,但是在我的机器上,经过一百万次迭代,for循环总是快约1.5倍
Sudhanshu Mishra 2014年

22

只需使用一个比较charfor而不是foreach:可以将速度提高约20%

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发行模式下启用的结果和优化功能:

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编写自己的(简单)函数(DigitsOnlyDigitsOnly2以下函数,但不要 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

10

我喜欢Linq,并使其在第一次失配时退出,您可以执行此操作

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

9

具有空验证功能:

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

8

最快的方法可能是:

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

注意:如果您的字符串为空(不正确)(如果您不认为空为有效数字),它将返回True


7

这应该工作:

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

int.Parseint.TryParse将无法始终有效,因为该字符串可能包含int可以容纳的更多数字。

如果您不止要进行一次此检查,则使用编译后的正则表达式会很有用-第一次会花费更多时间,但之后要快得多。


3
这是错误的,如果只有一位,则返回true。尽管这个主意很棒。
Nahum

1
到目前为止,这是最慢的方法,但是基于未知的字符串大小,这是最好的解决方案。如前所述,正则表达式也需要进行调整。
TheCodeKing 2011年

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时,我认为这将不接受包含此类字符的字符串。
斯蒂芬·霍尔特

LINQ是完成任何事情的最慢方法。如果您想对编码应用总括规则,请假设某项功能提供的更高级别和功能越慢。
TravisO

3

这可能来的太晚了!但是我相信它会帮助某人,因为它对我有帮助。

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

1

您可以尝试使用正则表达式,.IsMatch(string input, string pattern)方法是使用C#中的方法将输入字符串测试为仅包含数字(0-9)。

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。感谢您的回答,但请注意,问题在于最快的方法。正则表达式相对较慢,这在其他答案中已有讨论。
Nahum 2014年

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
}

如果条件是不必要的,那么两个return语句也是不必要的,您只需返回s.All ...,但是还有其他问题,例如空字符串。
alvarlagerlof
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.