Sprawdź, czy łańcuch jest dodatnią liczbą całkowitą

200

Chciałbym, aby najprostszy niezawodny test sprawdził, czy ciąg w JavaScript jest dodatnią liczbą całkowitą.

isNaN(str)zwraca true dla wszystkich rodzajów wartości niecałkowitych i parseInt(str)zwraca liczby całkowite dla łańcuchów zmiennoprzecinkowych, takich jak „2.5”. Nie chcę też używać wtyczki jQuery.

Mick Byrne
źródło
7
Czy zezwalasz na „+2”? Co powiesz na „0.1e1”? Co powiesz na „1.0000”?
Phrogz
isNaN()Funkcja zachowuje się tak robi, bo to pojęcie Not a Numberma bardzo konkretne znaczenie w pływającym punktem specyfikacji IEEE 794. Nie ma na celu udzielenia odpowiedzi na proste, potoczne pytanie: „czy ta wartość nie jest liczbą?”
Pointy
3
Pytanie jest do końca niejasne. Nie można sprawdzić, czy „ciąg jest liczbą całkowitą”, ponieważ nie ma czegoś takiego - żaden obiekt nie może być jednocześnie ciągiem i liczbą. Prawdopodobnie miałeś na myśli „jak sprawdzić, czy łańcuch jest prawidłową reprezentacją liczby całkowitej”, ale aby odpowiedzieć na to pytanie, musimy wiedzieć, o którym języku lub „kulturze” mówisz. Na przykład, ٢‎٣٤czy MCMXIXsą zarówno ważne reprezentacjami całkowitymi, ale nie sądzę, szukasz kodu, który byłby zdolny do analizowania tych. Czy możesz określić, które formaty liczb będą obsługiwane, ponieważ ludzie wydają się być zdezorientowani.
Georg
5
Myślę, że „niejasne do skrajności” samo w sobie jest nieco ekstremalne; ale w każdym razie ... Kontekstem jest sprawdzenie poprawności pola wprowadzania ilości w koszyku używanym w kraju anglojęzycznym, więc tylko cyfry zachodnie. Przez „pozytywne” miałem na myśli więcej niż zero. Czy zaakceptowałbym następujące słowa: „+2” tak, „0.1e1” nie, „1.000” jasne, dlaczego nie. Jeśli jednak możesz udzielić odpowiedzi, którą można dostosować w celu uwzględnienia / wyłączenia tych różnych specjalistycznych scenariuszy, obiecuję dać ci dodatkowe ulepszenia (i jestem pewien, że inni też to zrobią).
Mick Byrne,

Odpowiedzi:

296

Dwie odpowiedzi dla ciebie:

  • Na podstawie analizy

  • Wyrażenie regularne

Zauważ, że w obu przypadkach zinterpretowałem „dodatnią liczbę całkowitą”, aby uwzględnić 0, chociaż 0nie jest dodatnia. Załączam notatki, jeśli chcesz się nie zgadzać 0.

Na podstawie analizy

Jeśli chcesz, aby był to znormalizowany dziesiętny ciąg liczb całkowitych w rozsądnym zakresie wartości, możesz to zrobić:

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

lub jeśli chcesz zezwolić na białe spacje i zera wiodące:

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

Aktywna platforma testowa (bez obsługi wiodących zer i białych znaków):

Aktywna platforma testowa ( z obsługą wiodących zer i białych znaków):

Jeśli chcesz zabronić 0, po prostu zmień >= 0na > 0. (Lub w wersji, która pozwala na zera na początku, usuń znak || "0"w replacewierszu).

Jak to działa:

  1. W wersji ze spacjami i wiodącymi zerami:

    • str = str.trim(); usuwa wszelkie wiodące i końcowe białe znaki.
    • if (!str) łapie pusty ciąg i wraca, nie ma sensu wykonywać reszty pracy.
    • str = str.replace(/^0+/, "") || "0"; usuwa wszystkie wiodące zera z ciągu - ale jeśli wynikiem jest pusty ciąg, przywraca pojedyncze 0.
  2. Number(str): Konwertuj strna liczbę; liczba może również zawierać ułamek lub może być NaN.

  3. Math.floor: Obetnij liczbę (odcina każdą część ułamkową).

  4. String(...): Konwertuje wynik z powrotem na normalny ciąg dziesiętny. W przypadku naprawdę dużych liczb przejdzie to do notacji naukowej, co może złamać to podejście. (Nie do końca wiem, gdzie jest podział, szczegóły podano w specyfikacji , ale wydaje mi się, że w przypadku liczb całkowitych przekroczyłeś 21 cyfr [do tego czasu liczba stała się bardzo nieprecyzyjna, ponieważ IEEE-754 Liczby o podwójnej precyzji mają tylko 15 cyfr dokładności ..)

  5. ... === str: Porównuje to do oryginalnego ciągu.

  6. n >= 0: Sprawdź, czy to jest pozytywne.

Zauważ, że nie powiedzie się to dla danych wejściowych "+1", dowolnych danych w notacji naukowej, które nie zamieniają się z powrotem w tę samą notację naukową na String(...)etapie, i dla każdej wartości, której używa rodzaj kodu JavaScript (zmiennoprzecinkowa podwójna precyzja IEEE-754) nie może dokładnie przedstawić, która parsuje jako bliżej innej wartości niż podana (która zawiera wiele liczb całkowitych powyżej 9 007,199,254,740,992; na przykład 1234567890123456789zawiedzie). Pierwsza z nich jest łatwa do rozwiązania, a druga nie tyle.

Wyrażenie regularne

Drugim podejściem jest przetestowanie znaków ciągu za pomocą wyrażenia regularnego, jeśli Twoim celem jest po prostu (powiedzmy) opcjonalny ciąg, +po którym następuje jeden 0lub ciąg w normalnym formacie dziesiętnym:

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

Live testbed:

Jak to działa:

  1. ^: Dopasuj początek łańcucha

  2. \+?: Zezwól na jeden, opcjonalny +(usuń to, jeśli nie chcesz)

  3. (?:...|...): Zezwól na jedną z tych dwóch opcji (bez tworzenia grupy przechwytywania):

    1. (0|...): Pozwól 0sam ...

    2. (...|[1-9]\d*): ... lub liczbę rozpoczynającą się od czegoś innego niż, 0po której następuje dowolna liczba cyfr dziesiętnych.

  4. $: Dopasuj koniec łańcucha.

Jeśli chcesz zabronić 0(ponieważ nie jest to pozytywne), wyrażenie regularne staje się po prostu /^\+?[1-9]\d*$/(np. Możemy stracić alternatywę, na którą musieliśmy pozwolić 0).

Jeśli chcesz, aby umożliwić zer (0123, 00524), a następnie po prostu zastąpić przemienności (?:0|[1-9]\d*)z\d+

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

Jeśli chcesz zezwolić na białe znaki, dodaj \s*tuż po ^i \s*tuż przed $.

Uwaga: kiedy konwertujesz to na liczbę: w nowoczesnych silnikach prawdopodobnie dobrze byłoby użyć +strlub Number(str)zrobić to, ale starsze mogą rozszerzyć je w niestandardowy (ale wcześniej dozwolony) sposób, który mówi, że wiodące zero oznacza liczbę ósemkową (podstawa 8), np. „010” => 8. Po sprawdzeniu poprawności liczby można bezpiecznie użyć, parseInt(str, 10)aby upewnić się, że jest ona przetwarzana jako dziesiętna (podstawa 10). parseIntzignorowałby śmieci na końcu łańcucha, ale upewniliśmy się, że nie ma żadnego z wyrażeniem regularnym.

TJ Crowder
źródło
oba Number(' ')i Number('')wróć, 0podczas gdy powinny wrócić NaN.
neurino
@ TJCrowder ok, ale potrzebuję ciągów takich jak „2a”, aby jakoś zostać odrzucone, parseIntzwraca 2.
neurino
@neurino: /\D/.test('2a')jest prawdziwe (ponieważ nie ma cyfr). Więc może if (!/\D/.test(str) && !isNan((num = parseInt(str, 10))) { /* Valid number in num */}... Tuż po mojej głowie ...
TJ Crowder
Pod względem wydajności, która metoda byłaby szybsza? Lub który jest wskazany?
phkavitha
@phkavitha: Odpowiedź na pytania dotyczące wydajności JavaScript jest prawie zawsze „Zależy”, ponieważ różne silniki tak bardzo się od siebie różnią. Możesz przetestować swoje docelowe silniki / przeglądarki za pomocą jsperf.com . Nie sądzę, że ta operacja może być wąskim gardłem w dowolnej aplikacji ... :-)
TJ Crowder
76

Rozwiązanie 1

Jeśli będziemy rozważać na całkowitą JavaScript jest wartość maksymalnej 4294967295(tj Math.pow(2,32)-1), a następnie po krótkim rozwiązanie doskonale pracy:

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

OPIS:

  1. Prawy operator zmiany zerowego napełniania wykonuje trzy ważne rzeczy:
    • obcina część dziesiętną
      • 123.45 >>> 0 === 123
    • robi przesunięcie dla liczb ujemnych
      • -1 >>> 0 === 4294967295
    • „działa” w zakresie MAX_INT
      • 1e10 >>> 0 === 1410065408
      • 1e7 >>> 0 === 10000000
  2. parseFloatpoprawia parsowanie liczb ciągów (ustawienie NaNdla ciągów nienumerycznych)

TESTY:

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


Rozwiązanie 2

Inny sposób jest dobry dla wszystkich wartości liczbowych, które są ważne do Number.MAX_VALUE, tj. Do około 1.7976931348623157e+308:

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

OPIS:

  1. !isNaN(parseFloat(n))stosuje się do filtrowania czystych ciągów znaków, na przykład "", " ", "string";
  2. 0 <= ~~nFiltry ujemnych i dużymi liczbami całkowitymi, na przykład "-40.1", "129000098131766699";
  3. (!isNaN(parseFloat(n)) && 0 <= ~~n)zwraca, truejeśli wartość jest zarówno liczbowa, jak i dodatnia ;
  4. 0 === n % (...)sprawdza, czy wartość nie jest zmiennoprzecinkowa - tutaj (...)(patrz 3) jest obliczana jak 0w przypadku falsei jak 1w przypadku true.

TESTY:

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


Poprzednia wersja:

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

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

Wizja
źródło
Wypróbuj pusty ciąg lub dużą liczbę, na przykład 1290000981231123.1 - jsfiddle.net/5UCy4/1
Niko
@gdoron To samo, co ~~valodcinanie części ułamkowej. Jest to nieco szybsze, ponieważ wykonuje jedną operację bitową zamiast dwóch.
VisioN
Świetna odpowiedź. Osobiście wolałbym być bardziej gadatliwy dla czytelności(!isNaN(parseFloat(n)) && n % 1 === 0 && 0 <= ~~n)
Ramy Nasr
true, false, 0xFF, a inne wartości przechodzą isPositiveIntegerbez wykrycia.
Xeoncross
1
Ten obsługuje nawet wypełnione zera (dobrze!). Możesz dodać to do swoich testów.
Rashack,
21

Wygląda na to, że należy zastosować wyrażenie regularne:

var isInt = /^\+?\d+$/.test('the string');
Niko
źródło
Zamknij, chyba że dozwolone są „1.0” lub „.1e1”.
Phrogz
Cóż, 1290000192379182379123782900192981231 jest liczbą całkowitą, ale nie jest reprezentowalna w liczbach rodzimych JavaScript, więc przy wyrażeniu regularnym nadal konieczne jest wykonanie konwersji numerycznej i sprawdzenie, czy zadziałało.
Pointy
To bardzo nieprecyzyjne rozwiązanie.
usr
@usr: Przypuszczam, że pozwala on prowadzić 0tam, gdzie normalnie byś się nie spodziewał, ale w większości wydaje się w porządku.
TJ Crowder
Nie sprawdza, aby zakres wynosił co najwyżej 2 ^ 31-1 i nie dopuszcza cyfr arabskich. To jest hack, a nie dobre rozwiązanie. Najlepsze rozwiązanie ma najwięcej głosów pozytywnych w tym pytaniu. Dlaczego tego nie użyć? Jest lepszy pod każdym względem.
usr
17

Nowoczesne rozwiązanie, które działa w węźle i w ponad 90% wszystkich przeglądarek (oprócz IE i Opera Mini), polega na użyciu Number.isInteger, a następnie prostej kontroli pozytywnej.

Number.isInteger(x) && x > 0

Zostało to sfinalizowane w ECMAScript 2015 .

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

Polyfil to:

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

Jeśli potrzebujesz obsługi danych wejściowych w postaci ciągu lub liczby , możesz skorzystać z tej funkcji, dla której napisałem duży zestaw testów po tym, jak wszystkie istniejące odpowiedzi (2/1/2018) zawiodły w przypadku niektórych form wprowadzania.

function isPositiveInteger(v) {
  var i;
  return v && (i = parseInt(v)) && i > 0 && (i === v || ''+i === v);
}
Xeoncross
źródło
4

To jest prawie duplikat tego pytania:

Sprawdź poprawność liczb dziesiętnych w JavaScript - IsNumeric ()

Odpowiedź brzmi:

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

więc dodatnią liczbą całkowitą byłoby:

function isPositiveInteger(n) {
  var floatN = parseFloat(n);
  return !isNaN(floatN) && isFinite(n) && floatN > 0
      && floatN % 1 == 0;
}
Chango
źródło
Wiem, że to prawie duplikat pytania „Sprawdź poprawność liczb w JavaScript”. Przeczytałem to wszystko, ale pomyślałem, że pytanie dotyczące reprezentacji ciągu liczb całkowitych zasługuje na jego własną stronę.
Mick Byrne,
2
return ((parseInt(str, 10).toString() == str) && str.indexOf('-') === -1);

nie zadziała, jeśli podasz ciąg taki jak „0001”

Sebas
źródło
2

Moja funkcja sprawdza, czy liczba jest + ve i może mieć również wartość dziesiętną.

       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
źródło
2

Aby skorzystać z powyższej odpowiedzi VisioN, jeśli używasz wtyczki sprawdzania poprawności jQuery, możesz użyć tego:

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

Następnie możesz użyć w swoim normalnym zestawie reguł lub dodać go dynamicznie w ten sposób:

$("#positiveIntegerField").rules("add", {required:true, integer:true});
usr-bin-drinking
źródło
2

Prosty

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

console.log(isInteger(1));

Możesz także rozszerzyć liczbę i przypisać do niej funkcję za pomocą prototypu.

manish kumar
źródło
1

Jeśli używasz formularzy HTML5, możesz użyć atrybutu min="0"elementu formularza <input type="number" />. Jest to obsługiwane przez wszystkie główne przeglądarki. Nie wymaga Javascript dla tak prostych zadań, ale jest zintegrowany z nowym standardem HTML. Jest to udokumentowane na https://www.w3schools.com/tags/att_input_min.asp

mggluscevic
źródło
0

(~~a == a)gdzie ajest ciąg.

Gus
źródło
1
Nie powiedzie się to dla liczb ujemnych: ~~"-1" == "-1"powróci true. I OP poprosił tylko o dodatnie liczby całkowite.
Igor Milla
1
Również nie powiedzie się dla „” i „”.
neverfox
0

ES6:

Number.isInteger(Number(theNumberString)) > 0
oma
źródło