문자열에 숫자 만 포함되어 있는지 확인하는 가장 빠른 방법


178

이것을 확인하는 몇 가지 방법을 알고 있습니다. 정규식, 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()
— 키이스

30
@Keith IsDigit는 true약 3 백 자 이상의 문자를 반환 합니다. 전각 10 진수 0123(중국과 일본에서 일반적)와 다른 문화권의 숫자 ০১২௧௨௩௪꘤꘥꘦꘧꘨등을 포함합니다.
— 코드 InChaos

62
누군가 관심이 있다면, 이것은 확실히 한 라이너로 줄어들 수 있습니다->return str.All(c => c >= '0' && c <= '9');
— Jonesopolis

18
당신은 단순히 이것을 할 수도 있습니다 : return str.All(char.IsDigit);. 분석법 그룹을위한 만세!
— Icemanind

11
빈 문자열은 유효한 숫자가 아닙니다.
— Danon

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 개 파싱 거의해야 더 잘 자연 잡음이 결과 미미를 렌더링하는 시간에 따라, 그런데, 전혀 시간을. 유용한 타이밍을 얻으려면 백만 번 구문 분석해야합니다 .
— Jon Skeet

때문에을 downvoted 벤치 마크는 방법이 유용 할 너무 짧은 하고 당신은 당신의 방법도 샘플 당신에게있는 거 테스트를 위해 잘못된 대답을주고 있음을 발견하지 않았다. 샘플 문자열 은 자릿수로만 구성되어 있지만에는 너무 길기 때문에 intTryParse가 false를 반환합니다.
— Jon Skeet

1m에 훨씬 가깝습니다. 아 길이에 대한 좋은 지적, 나는 그것을 그리워했다.
— TheCodeKing

3
/ o +를 컴파일하면 int.TryParse보다 5 배 이상 빠릅니다. 확인하기 위해 디버거에서 실행되고 있지 않습니까?
— Jon Skeet

59

LINQ를 사용하여 간단히 할 수 있습니다.

return str.All(char.IsDigit);

  1. .All 빈 문자열에 대해서는 true를 반환하고 null 문자열에 대해서는 예외를 반환합니다.
  2. char.IsDigit 모든 유니 코드 문자에 적용됩니다.

3
char.IsDigit는 다양한 로케일의 수많은 유니 코드 숫자와 일치합니다 ( fileformat.info/info/unicode/category/Nd/list.htm 참조 ). 또한 귀하의 답변은 LINQ를 사용하므로 가장 빠른 방법은 아닙니다. 그래도 대부분의 유스 케이스에 충분할 수 있습니다.
— Stephen Holt

1
@StephenHolt 네, 그렇습니다. 필자가 이것이 가장 빠른 것은 아니라는 것을 알고 있지만 아마도 가장 쓰는 것이 가장 쉽습니다.
— Uday

그렇습니다. 몇 년 전에 비슷한 대답 (아래 참조)을 작성했지만 내 버전은 다른 로케일에서 문자를 제거하기 위해 문자가 '0'과 '9'사이인지 테스트했습니다. 정확한 요구 사항에 따라 다릅니다.
— Stephen Holt

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

유니 코드 숫자를 확인하려는 경우 코드가 빠르더라도 나쁜 코드이기 때문에 문자를 int로 캐스팅해서는 안됩니다.
— user823959

1
@ user823959 : 무슨 말인지 잘 모르겠습니다. Char.IsDigit는 mscorelib의 일부입니다. msdn.microsoft.com/en-us/library/0t641e58.aspx
— flayn

게르하르트 미안, 내 실수.
— user823959

이것은 루핑보다 간결하지만, 내 컴퓨터에서 백만 회 이상 반복하면 for 루프는 항상 ~ 1.5 배 빠릅니다.
— Sudhanshu Mishra

23

다음 char과 같이 for대신 하나의 비교 만 사용하여 약 20 % 더 빠를 수 있습니다 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 릴리스 모드 및 최적화 사용 결과 :

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
이것은 틀 렸으며, 한자리수라도 있다면 참을 반환합니다. 칭찬 된 아이디어는 굉장하지만.
— Nahum

1
이것은 지금까지 가장 느린 방법이지만 알 수없는 크기의 문자열을 기반으로하는 최상의 솔루션입니다. 언급했듯이 정규 표현식도 조정해야합니다.
— TheCodeKing

6

한 줄의 LINQ 문에서이 작업을 수행 할 수 있습니다. OK, 이것이 이것이 가장 빠른 것은 아니라는 것을 알고 기술적으로 질문에 대답하지는 않지만 아마도 가장 작성하기 쉽습니다.

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

4
str.All(char.IsDigit)작성하기는 쉽지만 물론 코드와 동일하지는 않습니다.
— 코드 InChaos

나는 이것을 테스트하려고 시도했다 : 물론 릴리즈 디버거가없는 pastebin.com/PuWBp9n1 ... WAYYYY가 더 빠르다. @ Jon Skeet 통찰력을 제공 할 수 있습니까? str.All (c => c> = '0'&& c <= '9')는 IsDigit
— Nahum

1
@NahumLitvin IsDigit은 유니 코드를 지원합니다. 따라서 Microsoft가 구현할 때 선택한 시간 메모리 상충 관계에 따라 수표가 상당히 비쌀 수 있습니다. 네이티브 코드로 전달한다고 가정하면 전환 비용도 상당히 많이들 수 있습니다.
— 코드 InChaos

@CodesInChaos "내 코드와 동일하지 않다"고 말했을 때 다른 것이 일치하는지 확인하고 다른 로케일 (예 : 아랍어)의 숫자가 버전과 일치하는 것으로 나타났습니다. 그러한 숫자가 유효한지 여부에 관계없이 OP가 고려해야 할 사항이라고 생각합니다. int.TryParse를 수행 할 때 그러한 문자가 포함 된 문자열을 허용하지 않을 것이라고 생각합니다.
— Stephen Holt

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
안녕하세요 Jason과 Stackoverflow에 오신 것을 환영합니다. 답변 해 주셔서 감사하지만 질문은 가장 빠른 방법에 관한 것입니다. 정규식은 상대적으로 느립니다. 이것은 다른 답변에서 논의되었습니다.
— Nahum

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

솔루션이 이미 제공된 솔루션보다 나은 이유를 설명 할 수 있습니까?
— 노엘 Widmer

이 코드를 실행하는 시간 순서가 [o (1)] 다른 것보다 적기 때문에 [o (n)]
— H. Borsipour

Convert.ToInt32o (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 조건이 필요하지 않으므로 두 개의 return 문이 있으므로 s.All을 반환 할 수 있습니다 ...하지만 빈 문자열과 같은 다른 문제가 있습니다.
— alvarlagerlof
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.