Validar se uma sequência é um número inteiro positivo

200

Eu gostaria que o teste mais simples à prova de falhas verifique se uma string em JavaScript é um número inteiro positivo.

isNaN(str)retorna true para todos os tipos de valores não inteiros e parseInt(str)está retornando números inteiros para cadeias flutuantes, como "2,5". E não quero ter que usar algum plugin jQuery também.

Mick Byrne
fonte
7
Você permite "+2"? Que tal "0.1e1"? Que tal "1.0000"?
Phrogz 31/05
A isNaN()função se comporta da maneira que funciona, porque o conceito Not a Numbertem um significado muito específico na especificação de ponto flutuante IEEE 794. Não pretende responder à simples pergunta coloquial: "esse valor não é um número?"
Pointy
3
A questão é vaga ao extremo. Você não pode validar que "uma string é um número inteiro", porque não existe tal coisa - nenhum objeto pode ser uma string e um número ao mesmo tempo. Você provavelmente quis dizer "como testar se uma string é uma representação válida de um número inteiro", mas para responder a isso, precisamos saber sobre qual idioma ou "cultura" você está falando. Por exemplo, ٢‎٣٤ou MCMXIXsão duas representações inteiras válidas, mas não acho que você esteja procurando um código que possa analisá-las. Você pode especificar quais formatos numéricos serão suportados, pois as pessoas parecem confusas sobre isso.
Georg
5
Eu acho que "vago ao extremo" é um pouco extremo; mas de qualquer maneira ... O contexto é a validação de um campo de entrada de quantidade em um carrinho de compras usado em um país de língua inglesa, portanto, apenas números ocidentais. Por "positivo", eu quis dizer maior que zero. Eu aceitaria o seguinte: "+2" sim, "0.1e1" não, "1.000", por que não? No entanto, se você puder fornecer uma resposta que possa ser ajustada para incluir / excluir esses vários cenários especializados, prometo fornecer ups extras (e tenho certeza que outros também o farão).
Mick Byrne

Respostas:

296

Duas respostas para você:

  • Com base na análise

  • Expressão regular

Observe que nos dois casos, interpretei "número inteiro positivo" para incluir 0, mesmo que 0não seja positivo. Incluo notas se você deseja proibir 0.

Baseado na análise

Se você deseja que ela seja uma sequência inteira decimal normalizada em um intervalo razoável de valores, faça o seguinte:

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

ou se você deseja permitir espaços em branco e zeros à esquerda:

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

Cama de teste ao vivo (sem manipular zeros à esquerda ou espaço em branco):

Cama de teste ao vivo ( com manipulação para zeros à esquerda e espaços em branco):

Se você deseja proibir 0, mude >= 0para > 0. (Ou, na versão que permite zeros à esquerda, remova || "0"a replacelinha).

Como isso funciona:

  1. Na versão que permite espaços em branco e zeros à esquerda:

    • str = str.trim(); remove qualquer espaço em branco inicial e final.
    • if (!str) pega uma string em branco e retorna, não adianta fazer o resto do trabalho.
    • str = str.replace(/^0+/, "") || "0"; remove todos os 0s iniciais da sequência - mas se isso resultar em uma sequência em branco, restaura um único 0.
  2. Number(str): Converta strpara um número; o número pode muito bem ter uma porção fracionária ou pode ser NaN.

  3. Math.floor: Trunca o número (corta qualquer parte fracionária).

  4. String(...): Converte o resultado novamente em uma seqüência decimal normal. Para números realmente grandes, isso vai para notação científica, que pode quebrar essa abordagem. (Não sei exatamente onde está a divisão, os detalhes estão nas especificações , mas para números inteiros acredito que esteja no ponto em que você excedeu 21 dígitos [quando o número se tornou muito impreciso, como IEEE-754 números de precisão dupla têm apenas 15 dígitos de precisão ..)

  5. ... === str: Compara isso à string original.

  6. n >= 0: Verifique se é positivo.

Observe que isso falha na entrada "+1", qualquer entrada na notação científica que não volte à mesma notação científica no String(...)estágio e por qualquer valor que o tipo de número que o JavaScript use (ponto flutuante binário de precisão dupla IEEE-754) não é possível representar com precisão o que é mais próximo de um valor diferente do valor especificado (que inclui muitos números inteiros acima de 9.007.199.254.740.992; por exemplo, 1234567890123456789falhará). O primeiro é uma solução fácil, os dois últimos nem tanto.

Expressão regular

A outra abordagem é testar os caracteres da string por meio de uma expressão regular, se seu objetivo é permitir (digamos) apenas um opcional +seguido por uma 0ou uma string no formato decimal normal:

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

Testbed ao vivo:

Como isso funciona:

  1. ^: Corresponde ao início da sequência

  2. \+?: Permita um único, opcional +(remova-o se não desejar)

  3. (?:...|...): Permita uma dessas duas opções (sem criar um grupo de captura):

    1. (0|...): Permitir 0por conta própria ...

    2. (...|[1-9]\d*): ... ou um número começando com algo diferente de 0e seguido por qualquer número de dígitos decimais.

  4. $: Corresponde ao final da string.

Se você deseja proibir 0(porque não é positivo), a expressão regular se torna justa /^\+?[1-9]\d*$/(por exemplo, podemos perder a alternância que precisávamos permitir 0).

Se você deseja permitir zeros à esquerda (0123, 00524), substitua a alternação (?:0|[1-9]\d*)por\d+

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

Se você deseja permitir espaço em branco, adicione \s*logo após ^e \s*pouco antes $.

Observe quando você converte isso em um número: em mecanismos modernos, provavelmente seria bom usá-lo +strou Number(str)fazê-lo, mas os mais antigos podem estendê-los de uma maneira não-padrão (mas anteriormente permitida) que diz que um zero à esquerda significa octal (base 8), por exemplo, "010" => 8. Depois de validar o número, você pode usá-lo com segurança parseInt(str, 10)para garantir que ele seja analisado como decimal (base 10). parseIntignoraria o lixo no final da string, mas garantimos que não há nenhum com a regex.

TJ Crowder
fonte
ambos Number(' ')e Number('')return 0while devem retornar NaN.
Neurino
@TJCrowder ok, mas eu preciso de strings como '' 2a '' para ser rejeitado de alguma forma, parseIntretorna 2.
Neurino
@neurino: /\D/.test('2a')é verdade (porque não existe um dígito). Então, talvez if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}... Apenas fora do topo da minha cabeça ...
TJ Crowder
Em termos de desempenho, qual método seria mais rápido? Ou qual é aconselhável?
phkavitha
@phkavitha: A resposta para as questões de desempenho do JavaScript é quase sempre "depende" porque os diferentes mecanismos são muito diferentes um do outro. Você pode testar seus mecanismos / navegadores de destino usando jsperf.com . Eu não acho que esta operação seria susceptível de ser o gargalo em qualquer aplicativo dado ... :-)
TJ Crowder
76

Solução 1

Se considerarmos que um número inteiro JavaScript é o valor máximo 4294967295(ou seja Math.pow(2,32)-1), a seguinte solução curta funcionará perfeitamente:

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

DESCRIÇÃO:

  1. O operador de mudança à direita com enchimento zero faz três coisas importantes:
    • trunca a parte decimal
      • 123.45 >>> 0 === 123
    • faz a mudança para números negativos
      • -1 >>> 0 === 4294967295
    • "funciona" na faixa de MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloatcorrige a análise de números de sequência (configuração NaNpara sequências não numéricas)

TESTES:

"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/


Solução 2

Outra maneira é boa para todos os valores numéricos válidos até Number.MAX_VALUE, ou seja, aproximadamente 1.7976931348623157e+308:

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

DESCRIÇÃO:

  1. !isNaN(parseFloat(n))é usado para filtrar puros valores de cadeia, por exemplo "", " ", "string";
  2. 0 <= ~~nfiltra negativo e valores não inteiros grandes, por exemplo "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n)retorna truese o valor é numérico e positivo ;
  4. 0 === n % (...)verifica se o valor não é flutuante - aqui (...)(veja 3) é avaliado como 0no caso de falsee como 1no caso de true.

TESTES:

"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/


A versão anterior:

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

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

Visão
fonte
Tente uma string vazia ou um número grande, como 1290000981231123.1 - jsfiddle.net/5UCy4/1
Niko
@gdoron O mesmo que ~~valcortar a parte fracionária. É um pouco mais rápido, pois faz uma operação bit a bit em vez de duas.
VisioN
Ótima resposta. Pessoalmente, eu seria a favor de ser um pouco mais detalhado para facilitar a leitura(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)
Ramy Nasr
true, false, 0xFFe outros valores passam isPositiveIntegersem serem detectados.
Xeoncross
1
Este até lida com zeros acolchoados (bom!). Você pode adicionar isso aos seus testes.
Rashack 25/09/19
21

Parece que uma expressão regular é o caminho a seguir:

var isInt = /^\+?\d+$/.test('the string');
Niko
fonte
Feche, a menos que "1.0" ou ".1e1" sejam permitidos.
Phrogz 31/05
Bem, 1290000192379182379123782900192981231 é um número inteiro, mas não é representável exatamente nos números nativos do JavaScript; portanto, com um regex, ainda é necessário fazer uma conversão numérica e verificar se funcionou.
Pointy
Esta é uma solução muito imprecisa.
usr
@usr: Suponho que isso permita liderar 0onde você normalmente não esperaria, mas na maioria das vezes parece bom.
TJ Crowder
Ele não verifica o intervalo para no máximo 2 ^ 31-1 e não permite dígitos arábicos. Este é um hack, não é uma boa solução. A melhor solução tem mais votos positivos nesta questão. Por que não usar esse? É melhor em todos os aspectos.
usr
17

A solução moderna que funciona no nó e em mais de 90% de todos os navegadores (exceto o IE e o Opera Mini) é usar o Number.isInteger, seguido de uma verificação positiva simples.

Number.isInteger(x) && x > 0

Isso foi finalizado no ECMAScript 2015 .

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

O Polyfil é:

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

Se você precisar dar suporte à entrada que possa estar na forma de sequência ou número , poderá usar esta função contra a qual escrevi uma grande suíte de testes depois que todas as respostas existentes (01/02/2018) falharam em alguma forma de entrada.

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

Esta é quase uma pergunta duplicada para esta:

Validar números decimais em JavaScript - IsNumeric ()

Sua resposta é:

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

portanto, um número inteiro positivo seria:

function isPositiveInteger(n) {
  var floatN = parseFloat(n);
  return !isNaN(floatN) && isFinite(n) && floatN > 0
      && floatN % 1 == 0;
}
Chango
fonte
Eu sei que é quase uma duplicata para a pergunta 'Validar números em JavaScript', eu li a coisa toda, mas pensei que uma pergunta especificamente sobre representações de strings de números inteiros merecia sua própria página.
Mick Byrne
2
return ((parseInt(str, 10).toString() == str) && str.indexOf('-') === -1);

não funcionará se você der uma string como '0001'

Sebas
fonte
2

Minha função verifica se o número é + ve e também pode ter 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
fonte
2

Apenas para aproveitar a resposta do VisioN acima, se você estiver usando o plugin de validação jQuery, poderá usar o seguinte:

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

Em seguida, você pode usar em seu conjunto de regras normal ou adicioná-lo dinamicamente da seguinte maneira:

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

Simples

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

console.log(isInteger(1));

Você também pode estender o número e atribuir a função a ele via protótipo.

manish kumar
fonte
1

Se você estiver usando formulários HTML5, poderá usar o atributo min="0"para o elemento do formulário <input type="number" />. Isso é suportado por todos os principais navegadores. Não envolve Javascript para tarefas simples, mas está integrado no novo padrão html. Está documentado em https://www.w3schools.com/tags/att_input_min.asp

mggluscevic
fonte
0

(~~a == a)onde aestá a string

Gus
fonte
1
Isso falhará para números negativos: ~~"-1" == "-1"retornará true. E o OP pediu apenas números inteiros positivos.
Igor Milla
1
Também falha para '' e ''.
neverfox
0

ES6:

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