Validar que una cadena es un entero positivo

200

Me gustaría que la prueba más simple a prueba de fallas verifique que una cadena en JavaScript sea un entero positivo.

isNaN(str)devuelve verdadero para todo tipo de valores no enteros y parseInt(str)devuelve enteros para cadenas flotantes, como "2.5". Y tampoco quiero tener que usar algún complemento jQuery.

Mick Byrne
fuente
77
¿Permiten "+2"? ¿Qué tal "0.1e1"? ¿Qué tal "1.0000"?
Phrogz
La isNaN()función se comporta como lo hace porque el concepto Not a Numbertiene un significado muy específico en la especificación de coma flotante IEEE 794. No tiene la intención de proporcionar una respuesta a la simple pregunta coloquial, "¿este valor no es un número?"
Puntiagudo
3
La pregunta es vaga hasta el extremo. No puede validar que "una cadena es un número entero", porque no existe tal cosa, ningún objeto puede ser una cadena y un número al mismo tiempo. Probablemente quisiste decir "cómo probar si una cadena es una representación válida de un entero", pero para responder a esto necesitamos saber de qué idioma o "cultura" estás hablando. Por ejemplo, ٢‎٣٤o MCMXIXambas son representaciones enteras válidas, pero no creo que esté buscando un código que pueda analizarlas. ¿Podría especificar qué formatos de números admitirá, ya que la gente parece confundida al respecto?
georg
55
Creo que 'vago al extremo' es en sí mismo un poco extremo; pero de todos modos ... El contexto es la validación de un campo de entrada de cantidad en un carrito de compras utilizado en un país de habla inglesa, por lo que solo los números occidentales. Por "positivo" quise decir mayor que cero. ¿Aceptaría lo siguiente: "+2" sí, "0.1e1" no, "1.000" seguro por qué no. Sin embargo, si puede proporcionar una respuesta que pueda ajustarse para incluir / excluir estos diversos escenarios especializados, prometo brindarle mejoras adicionales (y estoy seguro de que otros también lo harán).
Mick Byrne

Respuestas:

296

Dos respuestas para ti:

  • Basado en análisis

  • Expresión regular

Tenga en cuenta que en ambos casos, he interpretado "entero positivo" para incluir 0, aunque 0no es positivo. Incluyo notas si desea no permitir 0.

Basado en análisis

Si desea que sea una cadena entera decimal normalizada en un rango razonable de valores, puede hacer esto:

function isNormalInteger(str) {
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

o si desea permitir espacios en blanco y ceros a la izquierda:

function isNormalInteger(str) {
    str = str.trim();
    if (!str) {
        return false;
    }
    str = str.replace(/^0+/, "") || "0";
    var n = Math.floor(Number(str));
    return n !== Infinity && String(n) === str && n >= 0;
}

Banco de pruebas en vivo (sin manejar ceros a la izquierda o espacios en blanco):

Banco de pruebas en vivo ( con manejo de ceros y espacios en blanco):

Si desea rechazar 0, simplemente cambie >= 0a > 0. (O, en la versión que permite los ceros a la izquierda, elimine el || "0"en la replacelínea).

Cómo funciona eso:

  1. En la versión que permite espacios en blanco y ceros a la izquierda:

    • str = str.trim(); elimina cualquier espacio en blanco inicial y final.
    • if (!str) atrapa una cadena en blanco y regresa, no tiene sentido hacer el resto del trabajo.
    • str = str.replace(/^0+/, "") || "0"; elimina todos los 0 iniciales de la cadena, pero si eso resulta en una cadena en blanco, restaura un solo 0.
  2. Number(str): Convertir stra un número; el número bien puede tener una porción fraccional, o puede ser NaN.

  3. Math.floor: Trunca el número (corta cualquier porción fraccional).

  4. String(...): Convierte el resultado nuevamente en una cadena decimal normal. Para números realmente grandes, esto irá a notación científica, lo que puede romper este enfoque. (No sé exactamente dónde está la división, los detalles están en la especificación , pero para los números enteros creo que es en el momento en que has excedido los 21 dígitos [en ese momento el número se ha vuelto muy impreciso, como IEEE-754 los números de doble precisión solo tienen 15 dígitos precisos de precisión ...)

  5. ... === str: Compara eso con la cadena original.

  6. n >= 0: Comprueba que sea positivo.

Tenga en cuenta que esto falla para la entrada "+1", cualquier entrada en notación científica que no se convierta en la misma notación científica en la String(...)etapa, y para cualquier valor que utilice el tipo de número que JavaScript utiliza (punto flotante binario de doble precisión IEEE-754) no puede representar con precisión qué análisis está más cerca de un valor diferente que el dado (que incluye muchos enteros de más de 9,007,199,254,740,992; por ejemplo, 1234567890123456789fallará). El primero es una solución fácil, los dos últimos no tanto.

Expresión regular

El otro enfoque es probar los caracteres de la cadena a través de una expresión regular, si su objetivo es permitir (digamos) una opción +seguida por una 0o una cadena en formato decimal normal:

function isNormalInteger(str) {
    return /^\+?(0|[1-9]\d*)$/.test(str);
}

Banco de pruebas en vivo:

Cómo funciona eso:

  1. ^: Inicio de la secuencia del partido

  2. \+?: Permitir un solo, opcional +(elimine esto si no lo desea)

  3. (?:...|...): Permita una de estas dos opciones (sin crear un grupo de captura):

    1. (0|...): Permitir 0por sí solo ...

    2. (...|[1-9]\d*): ... o un número que comienza con algo diferente 0y seguido de cualquier número de dígitos decimales.

  4. $: Coincide con el final de la cadena.

Si desea rechazar 0(porque no es positivo), la expresión regular se vuelve justa /^\+?[1-9]\d*$/(por ejemplo, podemos perder la alternancia que necesitábamos permitir 0).

Si desea permitir ceros a la izquierda (0123, 00524), simplemente reemplace la alternancia (?:0|[1-9]\d*)con\d+

function isNormalInteger(str) {
    return /^\+?\d+$/.test(str);
}

Si desea permitir espacios en blanco, agregue \s*justo después ^y \s*justo antes $.

Nota para cuando convierta eso en un número: en los motores modernos probablemente estaría bien usarlo +stro Number(str)hacerlo, pero los más antiguos podrían extenderlos de una manera no estándar (pero permitida anteriormente) que dice que un cero inicial significa octal (base 8), por ejemplo, "010" => 8. Una vez que haya validado el número, puede usarlo parseInt(str, 10)con seguridad para asegurarse de que se analiza como decimal (base 10). parseIntignoraría la basura al final de la cadena, pero nos hemos asegurado de que no haya ninguna con la expresión regular.

TJ Crowder
fuente
ambos Number(' ')y Number('')regresar 0mientras debería regresar NaNen su lugar
neurino
@TJCrowder está bien, pero necesito cadenas como '' 2a '' para ser rechazadas de alguna manera, parseIntregresa 2.
neurino
@neurino: /\D/.test('2a')es cierto (porque hay un no dígito). Entonces, tal vez if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}... justo en la parte superior de mi cabeza ...
TJ Crowder
En cuanto al rendimiento, ¿qué método sería más rápido? ¿O cuál es aconsejable?
phkavitha
@phkavitha: La respuesta a las preguntas de rendimiento de JavaScript es casi siempre "depende" porque los diferentes motores son muy diferentes entre sí. Puede probar sus motores / navegadores de destino utilizando jsperf.com . No creo que esta operación sea el cuello de botella en ninguna aplicación ... :-)
TJ Crowder
76

Solución 1

Si consideramos que un entero JavaScript es un valor máximo 4294967295(es decir Math.pow(2,32)-1), entonces la siguiente solución breve funcionará perfectamente:

function isPositiveInteger(n) {
    return n >>> 0 === parseFloat(n);
}

DESCRIPCIÓN:

  1. El operador de desplazamiento a la derecha de llenado a cero hace tres cosas importantes:
    • trunca la parte decimal
      • 123.45 >>> 0 === 123
    • hace el cambio para números negativos
      • -1 >>> 0 === 4294967295
    • "funciona" en el rango de MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloatrealiza un análisis correcto de los números de cadena (configuración NaNpara cadenas no numéricas)

PRUEBAS

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e7"                  : false
"1e7"                   : true
"1e10"                  : false
"1edf"                  : false
" "                     : false
""                      : false

DEMO: http://jsfiddle.net/5UCy4/37/


Solución 2

Otra forma es buena para todos los valores numéricos que son válidos hasta Number.MAX_VALUE, es decir, aproximadamente 1.7976931348623157e+308:

function isPositiveInteger(n) {
    return 0 === n % (!isNaN(parseFloat(n)) && 0 <= ~~n);
}

DESCRIPCIÓN:

  1. !isNaN(parseFloat(n))se utiliza para filtrar puros valores de cadena, por ejemplo "", " ", "string";
  2. 0 <= ~~nfiltra negativo y valores no enteros grandes, por ejemplo "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n)devuelve truesi el valor es tanto numérico como positivo ;
  4. 0 === n % (...)comprueba si el valor no es flotante - aquí (...)(ver 3) se evalúa como 0en el caso de false, y como 1en el caso de true.

PRUEBAS

"0"                     : true
"23"                    : true
"-10"                   : false
"10.30"                 : false
"-40.1"                 : false
"string"                : false
"1234567890"            : true
"129000098131766699.1"  : false
"-1e10"                 : false
"1e10"                  : true
"1edf"                  : false
" "                     : false
""                      : false

DEMO: http://jsfiddle.net/5UCy4/14/


La versión anterior:

function isPositiveInteger(n) {
    return n == "0" || ((n | 0) > 0 && n % 1 == 0);
}

DEMO: http://jsfiddle.net/5UCy4/2/

Visión
fuente
Pruebe con una cadena vacía o un número grande como 1290000981231123.1 - jsfiddle.net/5UCy4/1
Niko
@gdoron Lo mismo que ~~valcortar la parte fraccionaria. Es un poco más rápido ya que realiza una operación bit a bit en lugar de dos.
VisioN
Gran respuesta. Personalmente, preferiría ser un poco más detallado para la legibilidad(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)
Ramy Nasr
true, false, 0xFF, y otros valores logran pasar isPositiveIntegersin ser detectados.
Xeoncross
1
Este incluso maneja ceros acolchados (¡bien!). Podrías agregar eso a tus pruebas.
Rashack
21

Parece que una expresión regular es el camino a seguir:

var isInt = /^\+?\d+$/.test('the string');
Niko
fuente
Cerrar, a menos que "1.0" o ".1e1" estén permitidos.
Phrogz
Bueno, 1290000192379182379123782900192981231 es un número entero pero no es representable exactamente en números nativos de JavaScript, por lo que con una expresión regular todavía es necesario hacer una conversión numérica y verificar que funcionó.
Puntiagudo
Esta es una solución muy imprecisa.
usr
@ usr: Supongo que permite dirigir hacia 0donde normalmente no esperarías, pero en su mayor parte, parece estar bien.
TJ Crowder
No comprueba que el rango sea como máximo 2 ^ 31-1 y no permite dígitos arábigos. Este es un truco, no es una buena solución. La mejor solución tiene más votos a favor en esta pregunta. ¿Por qué no usar ese? Es mejor en todos los aspectos.
usr
17

La solución moderna que funciona en el nodo y en más del 90% de todos los navegadores (excepto IE y Opera Mini) es usar Number.isInteger seguido de una simple verificación positiva.

Number.isInteger(x) && x > 0

Esto se finalizó en ECMAScript 2015 .

function isPositiveInteger(x) {
    return Number.isInteger(x) && x > 0
}

El Polyfil es:

Number.isInteger = Number.isInteger || function(value) {
  return typeof value === 'number' && 
    isFinite(value) && 
    Math.floor(value) === value;
};

Si necesita admitir entradas que pueden estar en forma de cadena o número, entonces puede usar esta función contra la que escribí un gran conjunto de pruebas después de que todas las respuestas existentes (2/1/2018) fallaron en alguna forma de entrada.

function isPositiveInteger(v) {
  var i;
  return v && (i = parseInt(v)) && i > 0 && (i === v || ''+i === v);
}
Xeoncross
fuente
4

Esta es casi una pregunta duplicada para esta:

Validar números decimales en JavaScript - IsNumeric ()

Su respuesta es:

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

entonces, un entero positivo sería:

function isPositiveInteger(n) {
  var floatN = parseFloat(n);
  return !isNaN(floatN) && isFinite(n) && floatN > 0
      && floatN % 1 == 0;
}
Chango
fuente
Sé que es casi un duplicado de la pregunta 'Validar números en JavaScript', leí todo eso, pero pensé que una pregunta específicamente sobre representaciones de cadenas de enteros merecía su propia página.
Mick Byrne
2
return ((parseInt(str, 10).toString() == str) && str.indexOf('-') === -1);

aunque no funcionará si le das una cadena como '0001'

Sebas
fuente
2

Mi función verifica si el número es + ve y también podría tener un valor decimal.

       function validateNumeric(numValue){
            var value = parseFloat(numValue);
            if (!numValue.toString().match(/^[-]?\d*\.?\d*$/)) 
                    return false;
            else if (numValue < 0) {
                return false;
            }
            return true;        
        }
Deepak Nirala
fuente
2

Solo para construir sobre la respuesta de VisioN anterior, si está utilizando el complemento de validación jQuery, podría usar esto:

$(document).ready(function() {
    $.validator.addMethod('integer', function(value, element, param) {
        return (value >>> 0 === parseFloat(value) && value > 0);
    }, 'Please enter a non zero integer value!');
}

Luego, podría usar en su conjunto de reglas normales o agregarlo dinámicamente de esta manera:

$("#positiveIntegerField").rules("add", {required:true, integer:true});
usr-bin-drinking
fuente
2

Sencillo

function isInteger(num) {
  return (num ^ 0) === num;
}

console.log(isInteger(1));

También puede extender Número y asignarle la función a través de un prototipo.

kumar viril
fuente
1

Si está utilizando formularios HTML5, puede usar el atributo min="0"para el elemento del formulario <input type="number" />. Esto es compatible con todos los principales navegadores. No implica Javascript para tareas tan simples, pero está integrado en el nuevo estándar html. Está documentado en https://www.w3schools.com/tags/att_input_min.asp

mggluscevic
fuente
0

(~~a == a)donde aesta la cuerda

Gus
fuente
1
Esto fallará para los números negativos: ~~"-1" == "-1"volverá true. Y OP solo pidió enteros positivos.
Igor Milla
1
También falla para '' y ''.
neverfox
0

ES6:

Number.isInteger(Number(theNumberString)) > 0
oma
fuente