Самый быстрый способ проверить, содержит ли строка только цифры


178

Я знаю несколько способов, как это проверить. регулярное выражение, int.parse, tryparse, зацикливание.

Кто-нибудь может сказать мне, какой самый быстрый способ проверить?

нужно только ПРОВЕРИТЬ, нет необходимости фактически разбирать.

это не тот же вопрос, что и: Как я могу определить, является ли строка числом?

вопрос не только в том, как определить. но о том, какой метод самый быстрый .


2
ж / о только измерения я думаю int.tryparse
Kenny

Вероятно, цикл, написанный на ассемблере, который считывает фрагменты данных размером с собственное слово из строки в регистр, а затем выполняет проверку диапазона для каждого байта в регистре.
aroth

35
простоreturn str.All(Char.IsDigit);
Мохсен

2
int.TryParse не проверяет, содержит ли строка только цифры! Строки типа "-13" (с минусом и пробелами) будут успешно проанализированы.
Алеюш

Ответы:


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

    return true;
}

Вероятно, это будет самый быстрый способ сделать это.


16
Там такжеchar.IsDigit()
Кит

30
@Keith IsDigitвозвращает еще trueоколо трехсот символов. Включая десятичные цифры полной ширины 0123... (распространенные в Китае и Японии) и цифры из других культур, например, ০১২௧௨௩௪꘤꘥꘦꘧꘨и многое другое.
CodesInChaos

62
если кому-то все равно, это, безусловно, может быть сведено к одной return str.All(c => c >= '0' && c <= '9');
строчке

18
Вы можете просто сделать это тоже return str.All(char.IsDigit);. Ура для метода группы!
Icemanind

11
Обратите внимание, что пустая строка не является пустым числом.
Данон

64

Вот несколько тестов на основе 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 возвращается ложь.
Джон Скит

Это намного ближе с 1м. Ах, хорошая точка зрения о длине, я пропустил это.
TheCodeKing

3
Ох, с / o + при компиляции, теперь это в 5 раз быстрее, чем int.TryParse. Просто чтобы проверить, вы не работаете в отладчике, не так ли?
Джон Скит

59

Вы можете просто сделать это с помощью LINQ

return str.All(char.IsDigit);

  1. .All возвращает true для пустых строк и исключение для пустых строк.
  2. char.IsDigit верно для всех символов Юникода.

3
char.IsDigit соответствует множеству цифр Юникода из разных локалей (см. fileformat.info/info/unicode/category/Nd/list.htm ). Кроме того, ваш ответ использует LINQ, поэтому вряд ли это будет самый быстрый способ сделать это. Это может быть достаточно для большинства случаев использования, хотя.
Стивен Холт,

1
@ StephhenHolt Да, вы правы, я понимаю, что это не обязательно самый быстрый, но это, вероятно, легче всего писать.
Uday

Да, справедливо. Я также написал аналогичный ответ (см. Ниже) несколько лет назад, хотя моя версия только что проверила, было ли значение char между '0' и '9', чтобы исключить символы из других локалей. Это будет зависеть от точных требований.
Стивен Холт

34

У char уже есть 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-цифр, вы не должны приводить символ к int только потому, что это плохой код, даже для более быстрого кода.
user823959

1
@ user823959: Я не уверен, что вы имеете в виду. Char.IsDigit является частью mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Герхард, извини, моя ошибка.
user823959

Это более кратко, чем зацикливание, но на моей машине более миллиона итераций, поскольку зацикливание всегда быстрее в ~ 1.5 раза
Судханшу Мишра

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,2 ГГц, 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пишите свою собственную (простую) функцию ( 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))

Примечание: он вернет True в случае, если ваша строка пуста, что неверно (если вы не считаете пустым допустимое число / цифру)


7

Это должно работать:

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

int.Parseили int.TryParseне всегда будет работать, потому что строка может содержать больше цифр, которые может содержать int.

Если вы собираетесь выполнять эту проверку более одного раза, полезно использовать скомпилированное регулярное выражение - в первый раз это займет больше времени, но после этого будет намного быстрее.


3
это неправильно, он возвращает истину, если есть хотя бы одна цифра. хотя выполненная идея потрясающая.
Наум

1
Это, безусловно, самый медленный метод, но это лучшее решение, основанное на неизвестном размере строки. Как уже упоминалось, регулярное выражение также нуждается в настройке.
TheCodeKing

6

Вы можете сделать это в одной строке LINQ оператора. Хорошо, я понимаю, что это не обязательно самый быстрый, поэтому технически не отвечает на вопрос, но это, вероятно, проще всего написать:

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

4
str.All(char.IsDigit)даже легче написать, но, конечно, не эквивалентно вашему коду.
CodesInChaos

Я попытался проверить это: pastebin.com/PuWBp9n1 на выпуске, конечно, нет отладчика ... и кажется, WAYYYY быстрее. @ Джон Скит, можешь ли ты дать некоторое представление? str.All (c => c> = '0' && c <= '9') кажется ПУТЬ быстрее, чем IsDigit
Наум

1
@NahumLitvin IsDigitподдерживает юникод. Таким образом, в зависимости от того, какой компромисс между временем и памятью Microsoft выбрал при реализации, проверка может быть довольно дорогой. Я предполагаю, что это переходит к нативному коду, этот переход также может быть довольно дорогим.
CodesInChaos

@CodesInChaos, когда вы сказали, что это «не эквивалентно моему коду», я пошел проверить, что еще может совпадать, и выясняется, что цифры в других локалях (например, арабском) будут совпадать в вашей версии. Я полагаю, что OP должен учитывать, действительны ли такие цифры или нет. При выполнении int.TryParse, я думаю, что не будет принимать строки, содержащие такие символы.
Стивен Холт

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. Спасибо за ответ, но обратите внимание, что вопрос был о самом быстром способе. Регулярные выражения относительно медленные, это обсуждалось в других ответах.
Наум

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

Можете ли вы объяснить, почему ваше решение лучше, чем уже предоставленное?
Ноэль Видмер

Поскольку порядок выполнения этого кода [o (1)] меньше, чем у других [o (n)]
Х. Борсипур

Я был бы очень удивлен, если Convert.ToInt32бы бежал быстрее, чем o (n). У вас есть доказательства, подтверждающие это предположение?
BDL

1
это может быть быстрее, если str на самом деле число, но, вероятно, будет медленнее в случае исключения. Также он не отвечает на вопрос, потому что он не будет работать, если str на число больше, чем int.MaxValue.
Томер Вольберг

-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 не является обязательным, так что это два оператора return, вы можете просто вернуть s.All ... Но есть и другие проблемы, например, с пустыми строками.
alvarlagerlof
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.