文字列に数字のみが含まれているかどうかを確認する最速の方法


178

これを確認する方法をいくつか知っています。正規表現、int.parsetryparse、ループ。

誰が最も速いチェック方法を教えてもらえますか?

必要なのは チェック実際に解析する必要がないです。

これは同じ質問ではありません: 。文字列が数値であるかどうかをどのように特定しますか?

問題は、どのように特定するかだけではありません。しかし、最速の方法は何ですか


2
単に測定しているだけで、int.tryparseを推測します
ケニー

おそらく、アセンブリで記述されたループで、ネイティブワードサイズのデータ​​のチャンクを文字列からレジスタに読み込み、レジスタの各バイトで範囲チェックを実行します。
2011

35
単にreturn str.All(Char.IsDigit);
Mohsen 2013年

2
int.TryParseは、文字列に数字のみが含まれているかどうかをチェックしません!"-13"(マイナスとスペースを含む)のような文字列は正常に解析されます。
aleyush 2014年

回答:


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

    return true;
}

おそらくそれを行う最も速い方法でしょう。


16
ありますchar.IsDigit()
キース

30
@Keith IsDigittrueさらに約300文字を返します。全角10進数字0123...(中国と日本で一般的)および他の文化からの数字などを含み০১২௧௨௩௪꘤꘥꘦꘧꘨ます。
CodesInChaos 2013

62
誰かが気にすれば、これは確かにワンライナーに減らすことができます->return str.All(c => c >= '0' && c <= '9');
ジョネソポリス

18
これも簡単に実行できますreturn str.All(char.IsDigit);。メソッドグループの皆さん、こんにちは!
Icemanind 2014

11
空の文字列は有効な番号ではないことに注意してください。
Danon 2015年

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回解析しても、自然のノイズによって結果が意味のないものとなる時間帯では、ほとんど時間がかかりません。有用なタイミングを得るためには、100万回解析する必要があると思います。
Jon Skeet、2011

ベンチマークであるためDownvoted 方法に有用であるには余りにも短い、あなたの方法でも、あなたしているテストサンプルのために間違った答えを与えていることを発見しませんでした。サンプル文字列数字のみで構成されていますが、には長すぎるためint、TryParseはfalseを返します。
Jon Skeet、2011

1mとかなり近いです。ああ、長さがいいので、見逃してしまいました。
TheCodeKing、2011

3
ああ、/ 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を使用しているので、それを行うための最速の方法とは思えません。ただし、ほとんどのユースケースでは十分な場合があります。
スティーブンホルト

1
@StephenHoltはい、そうです。これが必ずしも最速であるとは限りませんが、おそらく最も簡単に書くことができます。
2018

うん、フェアポイント。私のバージョンでは、他のロケールから文字を削除するために、charが「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);

Unicodeの数字をチェックすることに注意を払っていれば、コードが悪いからといって、charをintにキャストするべきではありません。
user823959 2013

1
@ user823959:どういう意味かわかりません。Char.IsDigitはmscorelibの一部です:msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

ゲルハルトごめんなさい、私の間違い。
user823959 2013

これはループよりも簡潔ですが、私のマシンでは、100万回を超える反復処理のため、forループは常に約1.5倍高速です
Sudhanshu Mishra

23

ただ1つの比較あたりの使用してより速く、約20%とすることができるcharforの代わりに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
これは間違っています。1桁でもtrueを返します。従ったアイデアは素晴らしいですが。
ナフム

1
これははるかに遅い方法ですが、未知のサイズの文字列に基づく最良の解決策です。述べたように、正規表現も微調整が必​​要です。
TheCodeKing、2011

6

これは、1行のLINQステートメントで実行できます。わかりました。これが必ずしも最速であるとは限らないので、技術的には質問に答えませんが、おそらく最も簡単に記述できます。

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

4
str.All(char.IsDigit)書くのはさらに簡単ですが、もちろんあなたのコードと同等ではありません。
CodesInChaos 2013

私はこれをテストしようとしました: もちろんペーストなしのリリースではpastebin.com/PuWBp9n1 ...そしてそれはWAYYYYより速いようです。@ジョンスキートは洞察を提供できますか?str.All(c => c> = '0' && c <= '9')はIsDigitよりもはるかに高速に見える
Nahum

1
@NahumLitvin IsDigitはユニコードをサポートしています。したがって、Microsoftが実装したときに選択した時間とメモリのトレードオフによっては、チェックにかなりの費用がかかる場合があります。私はそれがネイティブコードに転送すると思います、その移行も非常に高価になる可能性があります。
CodesInChaos 2013

@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
こんにちは、ジェイソン。Stackoverflowへようこそ。回答いただきありがとうございます。質問は最速の方法に関するものでした。正規表現は比較的遅く、これは他の回答で議論されました。
Nahum 14

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 2017年

このコードを実行する時間順序[o(1)]は他のコードよりも短い[o(n)]
H. Borsipour

Convert.ToInt32o(n)よりも高速で実行されるとしたら、私は非常に驚きます。この仮定を裏付ける証拠はありますか?
BDL 2017年

1
strが実際に数値の場合はより高速になる可能性がありますが、Exeptionの場合はおそらく低速になります。また、strがint.MaxValueより大きい数の場合は機能しないため、質問には答えていません。
Tomer Wolberg、2018

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

このコードは問題を解決する可能性がありますが、その理由と動作の説明を追加する必要があります。そして、このコードがすでに提供されているものよりも優れていると思う理由を説明してください。
BDL 2017年

1
さらに、コードは空の文字列に対してTrueを返します。
BDL 2017年


-3

文字列を検出する非常に賢明で簡単な方法は、数字しか含まれていないか、この方法です:

string s = "12fg";

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

if条件は不要なので、2つのreturnステートメントも必要なので、s.Allを返すだけで済みます...しかし、空の文字列などの他の問題があります。
alvarlagerlof
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.