Найшвидший спосіб перевірити, чи рядок містить лише цифри


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 для порожніх рядків і виняток для null рядків.
  2. char.IsDigit вірно для всіх символів Unicode.

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

1
@StephenHolt Так, ви праві, я розумію, що це не обов'язково найшвидше, але це, мабуть, найпростіше написати.
Удай

Так, справедлива точка. Я також написав подібну відповідь (див. Нижче) кілька років тому, хоча моя версія просто перевіряла, чи не було знаків між "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 лише тому, що це поганий код, навіть для швидшого коду.
користувач823959

1
@ user823959: Я не впевнений, що ти маєш на увазі. Char.IsDigit є частиною програми mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Герхард вибачте, моя помилка.
користувач823959

Це більш коротко, ніж циклічно, але на моїй машині понад мільйон ітерацій, оскільки цикл завжди швидший приблизно в 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,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 швидше. @Jon Skeet чи можете ви ознайомитись? str.All (c => c> = '0' && c <= '9') здається БУДЕ швидше, ніж IsDigit
Nahum

1
@NahumLitvin IsDigitпідтримує unicode. Отже, залежно від того, які компроміси з тимчасовою пам’яттю обрали Microsoft, впроваджуючи її, чек може бути досить дорогим. Я припускаю, що він пересилає на власний код, і цей перехід може бути досить дорогим.
CodesInChaos

@CodesInChaos, коли ви сказали, що це "не еквівалентно моєму коду", я пішов перевірити, що ще може відповідати, і виявляється, що цифри в інших регіонах (наприклад, арабською мовою) відповідатимуть вашій версії. Я думаю, що це те, що ОП потрібно було б врахувати, чи є такі цифри дійсними чи ні. Роблячи 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 насправді число, але, можливо, це буде повільніше у випадку Exeption. Крім того, він не відповідає на питання, тому що він не працюватиме, якщо 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
}

Якщо умова не потрібна, так це два заяви про повернення, ви можете просто повернути s.All ... Але є й інші проблеми, наприклад, із порожніми рядками.
alvarlagerlof
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.