La forma más rápida de verificar si la cadena contiene solo dígitos


178

Sé algunas maneras de verificar esto. expresiones regulares, int.parse, tryparse, de enlace.

¿Alguien puede decirme cuál es la forma más rápida de verificar?

la necesidad es solo COMPROBAR sin necesidad de analizar realmente.

Esta no es la misma pregunta que: ¿Cómo identifico si una cadena es un número?

la pregunta no es solo acerca de cómo identificarse. pero sobre cuál es el método más rápido .


2
w / o que determinar un Conjeturaría int.TryParse
Kenny

Probablemente un bucle escrito en conjunto que lee fragmentos de datos del tamaño de una palabra nativa de la cadena en un registro y luego realiza una verificación de rango en cada byte en el registro.
Aroth

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

2
int. TryParse no comprueba si la cadena contiene solo dígitos. Las cadenas como "-13" (con menos y espacios) se analizarán correctamente.
aleyush

Respuestas:


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

    return true;
}

Probablemente será la forma más rápida de hacerlo.


16
También haychar.IsDigit()
Keith

30
@Keith IsDigit regresa truepor unos trescientos caracteres más. Incluyendo dígitos decimales de ancho completo 0123... (común en China y Japón) y dígitos de otras culturas, por ejemplo, ০১২௧௨௩௪꘤꘥꘦꘧꘨y mucho más.
CodesInChaos

62
si a alguien le importa, esto ciertamente puede reducirse a una sola línea ->return str.All(c => c >= '0' && c <= '9');
Jonesopolis

18
Usted simplemente puede hacer esto también: return str.All(char.IsDigit);. ¡Hurra por los grupos de métodos!
Icemanind

11
Tenga en cuenta que la cadena vacía no es un número vaild.
Danon

64

Aquí hay algunos puntos de referencia basados ​​en 1000000 análisis de la misma cadena:

Actualizado para releaseestadísticas:

IsDigitsOnly: 384588
TryParse:     639583
Regex:        1329571

Aquí está el código, parece que IsDigitsOnly es más 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;
    }
}

Por supuesto, vale la pena señalar que TryParse sí permite espacios en blanco al inicio / final así como símbolos específicos de la cultura. También está limitado en la longitud de la cuerda.


Analizar un número definitivamente lleva más tiempo que simplemente verificar cada dígito, ya que está realizando una conversión base.

1
1000 análisis de la misma cadena no deberían tomar casi nada de tiempo, por cierto, muy por debajo del tiempo en que el ruido natural hace que los resultados sean insignificantes. Esperaría tener que analizarlo un millón de veces para obtener tiempos útiles.
Jon Skeet

Downvoted debido a que el punto de referencia es manera demasiado corta para ser útil y que no lugar que su método está dando la respuesta equivocada, incluso para la muestra que está probando. La cadena de muestra está compuesta solo por dígitos, pero debido a que es demasiado larga para un int, TryParse devuelve falso.
Jon Skeet

Está mucho más cerca con 1m. Ah buen punto sobre la longitud, me perdí eso.
TheCodeKing

3
Ooh, con / o + en la compilación, ahora es más de 5 veces más rápido que int. TryParse. Solo para verificar, no estás ejecutando en el depurador, ¿verdad?
Jon Skeet

59

Simplemente puede hacer esto usando LINQ

return str.All(char.IsDigit);

  1. .All devuelve verdadero para cadenas vacías y excepción para cadenas nulas.
  2. char.IsDigit es cierto para todos los caracteres Unicode.

3
char.IsDigit coincide con numerosos dígitos Unicode de varias configuraciones regionales (consulte fileformat.info/info/unicode/category/Nd/list.htm ). Además, su respuesta usa LINQ, por lo que es poco probable que sea la forma más rápida de hacerlo. Sin embargo, podría ser suficiente para la mayoría de los casos de uso.
Stephen Holt el

1
@StephenHolt Sí, tienes razón, me doy cuenta de que no es necesariamente el más rápido, pero probablemente sea el más fácil de escribir.
Uday

Sí, punto justo. También escribí una respuesta similar (ver más abajo) hace unos años, aunque mi versión solo probó si el carácter estaba entre '0' y '9' para eliminar caracteres de otras configuraciones regionales. Eso dependerá de los requisitos exactos.
Stephen Holt

34

El char ya tiene un IsDigit (char c) que hace esto:

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

Simplemente puedes hacer esto:

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

Si deseaba verificar los dígitos Unicode, no debería haber emitido un carácter a un int solo porque es un código incorrecto, incluso para un código más rápido.
user823959

1
@ user823959: No estoy seguro de lo que quieres decir. Char.IsDigit es parte de mscorelib: msdn.microsoft.com/en-us/library/0t641e58.aspx
flayn

Gerhard lo siento, mi error.
user823959

Esto es más conciso que el bucle, pero en mi máquina, más de un millón de iteraciones, porque el bucle siempre es más rápido ~ 1.5 veces
Sudhanshu Mishra

23

Puede ser aproximadamente un 20% más rápido usando solo una comparación por chary en forlugar 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 utilizado para las pruebas (siempre perfil porque los resultados dependen del hardware, las versiones, el orden, 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 en Intel i5-3470 @ 3.2GHz, VS 2015 .NET 4.6.1 Modo de lanzamiento y optimizaciones habilitadas:

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 cualquier persona tentada a usar los métodos más cortos, tenga en cuenta que


14

Si le preocupa el rendimiento, no use int.TryParseni Regex- escriba su propia función (simple) ( DigitsOnlyo DigitsOnly2debajo, pero no DigitsOnly3 - LINQ parece incurrir en una sobrecarga significativa).

Además, tenga en cuenta que int.TryParsefallará si la cadena es demasiado larga para "encajar" int.

Este simple punto de referencia ...

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

    }

}

... produce el siguiente 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

Función con validación vacía:

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

10

Me gusta Linq y para que salga en el primer desajuste puedes hacer esto

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

8

Probablemente la forma más rápida es:

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

Nota: devolverá True en caso de que su cadena esté vacía, lo cual es incorrecto (si no considera vacío como número / dígito válido)


7

Esto debería funcionar:

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

int.Parse o int.TryParse no siempre funcionará, porque la cadena puede contener más dígitos que un int puede contener.

Si va a hacer esta comprobación más de una vez, es útil usar una expresión regular compilada: lleva más tiempo la primera vez, pero es mucho más rápido después de eso.


3
esto está mal, devuelve verdadero si hay incluso un dígito. aunque la idea cumplida es asombrosa.
Nahum

1
Este es, con mucho, el método más lento, pero es la mejor solución basada en un tamaño desconocido de cadena. Como se mencionó, la expresión regular también necesita un ajuste.
TheCodeKing

6

Puede hacer esto en una declaración LINQ de una línea. OK, me doy cuenta de que esto no es necesariamente el más rápido, por lo que técnicamente no responde la pregunta, pero probablemente sea la más fácil de escribir:

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

44
str.All(char.IsDigit)es aún más fácil de escribir, pero por supuesto no es equivalente a su código.
CodesInChaos

Traté de probar esto: pastebin.com/PuWBp9n1 en el lanzamiento sin depurador, por supuesto ... y parece MUCHO más rápido. @ Jon Skeet, ¿puedes darnos alguna idea? str.All (c => c> = '0' && c <= '9') parece MUCHO más rápido que IsDigit
Nahum

1
@NahumLitvin IsDigitadmite unicode. Entonces, dependiendo de las compensaciones de memoria de tiempo que Microsoft eligió al implementarlo, la verificación podría ser bastante costosa. Supongo que reenvía al código nativo, esa transición también puede ser bastante costosa.
CodesInChaos

@CodesInChaos cuando dijiste que "no era equivalente a mi código", fui a verificar qué más podría coincidir, y resulta que los dígitos en otras configuraciones regionales (por ejemplo, árabe) coincidirían en tu versión. Supongo que es algo que OP necesitaría considerar, ya sea que tales dígitos sean válidos o no. Al hacer int. TryParse, creo que eso no aceptaría cadenas que contengan dichos caracteres.
Stephen Holt

LINQ es la forma más lenta de lograr cualquier cosa. Si desea aplicar una regla general a la codificación, suponga que cuanto más alto nivel y funcionalidad ofrece algo, más lento es.
TravisO

3

¡Esto podría llegar muy tarde !, pero estoy seguro de que ayudará a alguien, ya que me ayudó.

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

1

Puede intentar usar Expresiones regulares probando que la cadena de entrada solo tenga dígitos (0-9) usando el .IsMatch(string input, string pattern)método en 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;
        }
    }
}

Saludos


3
Hola Jason y bienvenido a Stackoverflow. Gracias por responder, pero tenga en cuenta que la pregunta era sobre la forma más rápida. Las expresiones regulares son relativamente lentas, esto se discutió en otras respuestas.
Nahum

1

esto funcionará perfectamente, hay muchas otras formas pero esto funcionaría

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

Prueba este código:

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

¿Puede explicar por qué su solución es mejor que las ya proporcionadas?
Noel Widmer

Debido a que el orden de tiempo para ejecutar este código [o (1)] es menor que otros [o (n)]
H. Borsipour

Me sorprendería mucho si Convert.ToInt32corriera más rápido que o (n). ¿Tiene alguna evidencia para apoyar esta suposición?
BDL

1
podría ser más rápido si str es realmente un número, pero probablemente sería más lento en caso de Exeption. Además, no responde la pregunta porque no funcionará si str es un número mayor que int.MaxValue.
Tomer Wolberg

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

Aunque este código podría resolver el problema, debe agregar una explicación de por qué / cómo funciona. Y explique por qué cree que este código es mejor que los ya proporcionados.
BDL

1
Además: su código devuelve True para cadenas vacías.
BDL


-3

Una manera muy inteligente y fácil de detectar su cadena contiene solo dígitos o no de esta manera:

string s = "12fg";

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

La condición if es innecesaria, también lo son dos declaraciones de retorno, puede devolver el s.Todos ... Pero hay otros problemas, como las cadenas vacías.
alvarlagerlof
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.