Maneira mais rápida de verificar se a string contém apenas dígitos


178

Eu sei algumas maneiras de verificar isso. regex, int.parse, tryparse, looping.

alguém pode me dizer qual é a maneira mais rápida de verificar?

a necessidade é apenas VERIFICAR, não é necessário analisar de fato.

esta não é a mesma pergunta que: Como identifico se uma sequência é um número?

a questão não é apenas sobre como se identificar. mas sobre qual é o método mais rápido .


2
w / o apenas medir eu acho int.tryparse
Kenny

Provavelmente, um loop escrito em assembly que lê pedaços de dados do tamanho de palavras nativas da cadeia de caracteres em um registro e, em seguida, executa uma verificação de intervalo em cada byte no registro.
aroth 18/09/11

35
simplesmentereturn str.All(Char.IsDigit);
Mohsen

2
int.TryParse não verifica se a string contém apenas dígitos! Seqüências de caracteres como "-13" (com menos e espaços) serão analisadas com êxito.
Aleyush

Respostas:


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

    return true;
}

Provavelmente será a maneira mais rápida de fazer isso.


16
Há tambémchar.IsDigit()
Keith

30
@Keith IsDigitretorna truepara cerca de trezentos caracteres. Incluindo dígitos decimais de largura total 0123... (comuns na China e no Japão) e dígitos de outras culturas, por exemplo, ০১২௧௨௩௪꘤꘥꘦꘧꘨e muito mais.
CodesInChaos

62
se alguém se importa, isso certamente pode ser reduzido a uma linha ->return str.All(c => c >= '0' && c <= '9');
Jonesopolis 6/14

18
Você poderia simplesmente fazer isso também: return str.All(char.IsDigit);. Viva para grupos de métodos!
Icemanind 27/08/14

11
Observe que a string vazia não é um número válido.
Danon

64

Aqui estão alguns benchmarks baseados em 1000000 análises da mesma string:

Atualizado para releaseestatísticas:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

Aqui está o código, parece que IsDigitsOnly é mais rápido:

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

É claro que vale a pena notar que o TryParse permite espaços em branco iniciais / finais, bem como símbolos específicos da cultura. Também é limitado no comprimento da corda.


A análise de um número definitivamente leva mais tempo do que apenas a verificação de cada dígito, enquanto você realiza a conversão básica.

1
A propósito, 1000 análises da mesma string não devem levar quase nenhum tempo, bem abaixo do tempo em que o ruído natural torna os resultados insignificantes. Eu esperaria ter que analisá-lo um milhão de vezes para obter horários úteis.
Jon Skeet

Downvoted porque a referência é maneira muito curta para ser útil e você não detectar que seu método está dando a resposta errada mesmo para a amostra que você está testando. A sequência de amostra é composta apenas por dígitos, mas como é muito longo para um int, o TryParse está retornando false.
Jon Skeet

É muito mais perto com 1m. Ah, bom ponto sobre o comprimento, eu senti falta disso.
TheCodeKing 18/09/11

3
Ooh, com / o + na compilação, agora é mais de 5 vezes mais rápido que o int.TryParse. Só para verificar, você não está rodando no depurador, está?
Jon tiro ao prato

59

Você poderia simplesmente fazer isso usando o LINQ

return str.All(char.IsDigit);

  1. .All retorna true para cadeias vazias e exceção para cadeias nulas.
  2. char.IsDigit é verdadeiro para todos os caracteres Unicode.

3
char.IsDigit corresponde a vários dígitos unicode de vários códigos de idioma (consulte fileformat.info/info/unicode/category/Nd/list.htm ). Além disso, sua resposta usa o LINQ, portanto, é improvável que seja a maneira mais rápida de fazer isso. Pode ser suficiente para a maioria dos casos de uso.
Stephen Holt

1
@StephenHolt Sim, você está certo, percebo que não é necessariamente o mais rápido, mas é provavelmente o mais fácil de escrever.
Uday

Sim, ponto justo. Também escrevi uma resposta semelhante (veja abaixo) há alguns anos, embora minha versão apenas testasse se o caractere estava entre '0' e '9' para eliminar caracteres de outros locais. Isso vai depender dos requisitos exatos.
Stephen Holt

34

O char já possui um IsDigit (char c) que faz isso:

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

Você pode simplesmente fazer isso:

var theString = "839278";
bool digitsOnly = theString.All(char.IsDigit);

Se você procurou por dígitos Unicode, não deveria ter convertido um char para um int apenas porque é um código incorreto, mesmo para códigos mais rápidos.
user823959

1
@ user823959: Não sei ao certo o que você quer dizer. Char.IsDigit faz parte do mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Gerhard desculpe, meu erro.
user823959

Esta é mais conciso do que looping, mas na minha máquina, mais de um milhão de iterações, para o laço é sempre mais rápido por ~ 1,5 vezes
Sudhanshu Mishra

23

Pode ser cerca de 20% mais rápido usando apenas uma comparação por chare em forvez de 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; 
}

Código usado para teste (sempre perfil porque os resultados dependem de hardware, versões, ordem, etc.):

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

Resultados no modo de liberação Intel i5-3470 @ 3.2GHz, VS 2015 .NET 4.6.1 e otimizações ativadas:

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 

Para alguém tentado a usar métodos mais curtos, observe que


14

Se você está preocupado com desempenho, não use int.TryParsenem Regexescreva sua própria função (simples) ( DigitsOnlyou DigitsOnly2abaixo, mas não DigitsOnly3 - o LINQ parece incorrer em uma sobrecarga significativa).

Além disso, esteja ciente de que int.TryParsefalhará se a string for muito longa para "encaixar" int.

Esta referência simples ...

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

    }

}

... produz o seguinte resultado ...

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

Função com validação vazia:

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

10

Eu gosto do Linq e, para fazê-lo sair na primeira incompatibilidade, você pode fazer isso

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

8

Provavelmente, o caminho mais rápido é:

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

Nota: retornará True caso sua string esteja vazia, o que está incorreto (se você não considerar vazio como número / dígito válido)


7

Isso deve funcionar:

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

int.Parseou int.TryParsenem sempre funcionará, porque a sequência pode conter mais dígitos que um int pode conter.

Se você fizer essa verificação mais de uma vez, é útil usar um regex compilado - leva mais tempo na primeira vez, mas é muito mais rápido depois disso.


3
isso estiver errado, ele retornará verdadeiro se houver pelo menos um dígito. embora a ideia cumprida seja incrível.
Nahum

1
Esse é de longe o método mais lento, mas é a melhor solução com base no tamanho desconhecido da string. Como mencionado, o regex também precisa de um ajuste.
TheCodeKing 18/09/11

6

Você pode fazer isso em uma instrução LINQ de uma linha. OK, eu sei que isso não é necessariamente o mais rápido, por isso não responde tecnicamente à pergunta, mas é provavelmente o mais fácil de escrever:

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

4
str.All(char.IsDigit)é ainda mais fácil de escrever, mas é claro que não é equivalente ao seu código.
CodesInChaos

Eu tentei testar isso: pastebin.com/PuWBp9n1 no lançamento, sem depurador, é claro ... e parece MUITO MAIS rápido. @ Jon Skeet, você pode fornecer algumas dicas? str.All (c => c> = '0' && c <= '9') parece muito mais rápido do IsDigit
Nahum

1
@NahumLitvin IsDigitsuporta unicode. Portanto, dependendo de quais compensações de memória de tempo a Microsoft escolheu ao implementá-la, a verificação pode ser bastante cara. Suponho que ele encaminha para o código nativo, que a transição também pode ser bastante cara.
CodesInChaos

@CodesInChaos quando você disse que "não era equivalente ao meu código", fui verificar o que mais poderia corresponder, e acontece que dígitos em outros locais (por exemplo, árabe) corresponderiam à sua versão. Eu acho que é algo que o OP precisaria considerar, se esses dígitos são válidos ou não. Ao fazer int.TryParse, acho que não aceitaria seqüências contendo esses caracteres.
Stephen Holt

O LINQ é a maneira mais lenta de realizar qualquer coisa. Se você deseja aplicar uma regra geral à codificação, assuma o nível e a funcionalidade mais altos que algo oferece, mais lento é.
TravisO

3

Isso pode estar chegando tarde demais !, mas tenho certeza de que ajudará alguém, assim como me ajudou.

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

1

Você pode tentar usar expressões regulares testando a sequência de entrada para ter apenas dígitos (0-9) usando o .IsMatch(string input, string pattern)método em 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;
        }
    }
}

Saudações


3
Oi Jason e bem-vindo ao Stackoverflow. Obrigado por responder, mas observe que a pergunta era a maneira mais rápida. Expressões regulares são relativamente lentas, isso foi discutido em outras respostas.
Naum

1

isso funcionará perfeitamente, existem muitas outras maneiras, mas isso funcionaria

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

Tente este código:

bool isDigitsOnly(string str)
{
   try
   {
      int number = Convert.ToInt32(str);
      return true;
   }
   catch (Exception)
   {
      return false;
   }
}

Você pode explicar por que sua solução é melhor do que as já fornecidas?
precisa

Como a ordem do tempo para executar esse código [o (1)] é menor que os outros [o (n)]
H. Borsipour 6/17

Eu ficaria muito surpreso se Convert.ToInt32rodasse mais rápido que o (n). Você tem alguma evidência para apoiar essa suposição?
BDL

1
pode ser mais rápido se str for realmente um número, mas provavelmente seria mais lento em caso de exceção. Também não está respondendo à pergunta porque não funcionará se str for um número maior que int.MaxValue.
Tomer Wolberg

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

Embora esse código possa resolver o problema, você deve adicionar uma explicação do porquê / como ele funciona. E, por favor, explique por que você acha que esse código é melhor do que o já fornecido.
BDL 30/06

1
Além disso: seu código retorna True para cadeias vazias.
BDL


-3

Uma maneira muito inteligente e fácil de detectar sua string contém apenas dígitos ou não, é desta maneira:

string s = "12fg";

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

A condição if é desnecessária, assim como duas instruções de retorno, você pode simplesmente retornar o s.All ... Mas há outros problemas, como com cadeias vazias.
alvarlagerlof
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.