Як перевірити, що рядок є цілим, але не подвійним тощо?


155

PHP має intval()функцію, яка перетворить рядок у ціле число. Однак я хочу заздалегідь перевірити, що рядок є цілим числом, щоб я міг дати корисне повідомлення про помилку, якщо воно неправильне. PHP має is_int(), але це повертає значення false для рядка "2".

PHP має is_numeric()функцію, але це поверне справжнє значення, якщо число подвійне. Я хочу чогось, що поверне помилковим для подвійного, але істинного для int.

наприклад:

my_is_int("2") == TRUE
my_is_int("2.1") == FALSE

6
як слід ставитися до "2.0"?
нікфф

Відповіді:


183

Як щодо використання ctype_digit?

З посібника:

<?php
$strings = array('1820.20', '10002', 'wsl!12');
foreach ($strings as $testcase) {
    if (ctype_digit($testcase)) {
        echo "The string $testcase consists of all digits.\n";
    } else {
        echo "The string $testcase does not consist of all digits.\n";
    }
}
?>

Наведений вище приклад виведе:

Рядок 1820.20 складається не з усіх цифр.
Рядок 10002 складається з усіх цифр.
Рядок wsl! 12 складається не з усіх цифр.

Це буде працювати лише в тому випадку, якщо ваш вхід завжди є рядком:

$numeric_string = '42';
$integer        = 42;

ctype_digit($numeric_string);  // true
ctype_digit($integer);         // false

Якщо ваше введення може бути типу int, комбінуйте ctype_digitз is_int.

Якщо ви дбаєте про негативні числа, то вам потрібно буде перевірити вхід на попередній -, і якщо так, зателефонуйте ctype_digitна substrрядок введення. Щось подібне це зробить:

function my_is_int($input) {
  if ($input[0] == '-') {
    return ctype_digit(substr($input, 1));
  }
  return ctype_digit($input);
}

7
від’ємні числа?
Анураг

1
@Anurag, відредагований у (хоча, якщо ви дійсно дбаєте про цей матеріал, регулярний вираз може бути простішим).
Домінік Роджер

4
Якщо ви хочете перевірити чи немає від'ємних цілих чисел, додайте використання abs (), як, ctype_digit(abs($str))а не просто ctype_digit.
чарувати

3
@enchance abs('definitelynotanint') === 0. до того ж, ctype_digitбере лише рядки
Клесун

Це неправильно. Велике ціле число в PHP - це подвійне!
jgmjgm

91

filter_var повинен це зробити:

var_dump(filter_var('2', FILTER_VALIDATE_INT));   // 2
var_dump(filter_var('2.0', FILTER_VALIDATE_INT)); // false
var_dump(filter_var('2.1', FILTER_VALIDATE_INT)); // false

але

var_dump(filter_var(2, FILTER_VALIDATE_INT));     // 2
var_dump(filter_var(2.0, FILTER_VALIDATE_INT));   // 2
var_dump(filter_var(2.1, FILTER_VALIDATE_INT));   // false

Якщо ви просто хочете, щоб булеві поверталися як значення, поверніть їх у функцію, наприклад

function validatesAsInt($number)
{
    $number = filter_var($number, FILTER_VALIDATE_INT);
    return ($number !== FALSE);
}

2
Мені подобається більшість відповідей, але я вважаю, що ця є найелегантнішою.
Ian Dunn

6
@grenoult 3v4l.org/qVRRS дає int 0 для рядків та цілих чисел, тож не знаю, чому ви вважаєте, що це не працює. Ви порівнюєте результат з ==можливо?
Гордон

1
@Gordon моя помилка, я справді порівнював лише if 3v4l.org/IAvCF
Гійом

Це дозволить зробити деякі дивні речі, такі як return true для "+123". Це пом'якшується тим, що воно має на меті повернути справжній int, але все одно це може бути не те, чого хочуть люди, а це сувора перевірка.
jgmjgm

@ MohammedRédaOUASSINI ОП попросив перевірити рядки , а не floats. Він буде працювати, якщо ви перейдете в '-1.0' (рядок). Він не буде працювати, якщо ви перейдете в -1.0 (поплавок), але це не було необхідністю для початку. Дивіться 3v4l.org/bOX6X
Гордон

20

+1 до відповіді Домініка (використовуючи ctype_digit). Ще один спосіб, як ви могли це зробити, - це примусовий тип:

$inty = "2";
$inty2 = " 2";
$floaty = "2.1";
$floaty2 = "2.0";

is_int($inty + 0); // true
is_int($floaty + 0); // false
is_int($floaty2 + 0); // false

// here's difference between this and the ctype functions.
is_int($inty2 + 0);  // true
ctype_digit($inty2); // false

2
+1 теж для вас (хоча мені цікаво, чи вирішувати цей конкретний випадок чіткіше, обрізаючи рядок введення).
Домінік Роджер

6
Цей метод не працює для нечислових рядків: is_int ("abc" +0) вірно
Kai Pommerenke

1
Правда, це не працює для нечислових рядків ... про що is_int($inty + 0) && is_numeric($inty)(я знаю, це ліниве рішення, але працює для більшості намірів і цілей ...
Алекс Мантаут,

Використання жонггінгу типу призведе до досить слабкої перевірки, де вкладки можуть бути представлені різними способами рядків, це не строкова перевірка. Вам також не потрібно +0. Ви можете просто + $ var.
jgmjgm

13

Відкиньте його до int. якщо воно все ще має однакове значення, його int;

function my_is_int($var) {
  $tmp = (int) $var;
  if($tmp == $var)
       return true;
  else
       return false;
}

2
Або ще коротше:return $var == (int) $var;
Al.G.

1
@ AI.G. Що станеться, якщо акторський склад не вдасться
DR.

Це дуже близько, слід уникати типу жонглювання, хоча якщо не потрібно. Це спрацює, якщо $ var "123xxxx".
jgmjgm

8
/**
 * Check if a number is a counting number by checking if it
 * is an integer primitive type, or if the string represents
 * an integer as a string
 */
function is_int_val($data) {
    if (is_int($data) === true) return true;
    if (is_string($data) === true && is_numeric($data) === true) {
        return (strpos($data, '.') === false);
    }
}

Джерело .


замість того, щоб розсипати мій код за допомогою невеликих корисних функцій, я вважаю за краще щось вбудоване в php.
Рорі

Мені також не подобаються ці хаки. Але використовуючи такий підхід або підказку від типу Домініка, ви все одно будете інкапсулювати всю реалізацію методу. Під час використання php у мене завжди є клас "Util" для вирішення цих проблем.
GmonC

elseifможе бути лише ifтому, що ви вже повернулися в заяві над ним.
rybo111

Також не потрібно повертати помилкові наприкінці.
rybo111

1
Якщо $dataоб’єкт, це повернеться null. Краще мати return false;кінець.
Теодор Р. Сміт

6

is_intНещодавно виникла потреба в міцній . Я вважав, що intval () занадто непередбачуваний:

intval(array('foo', 'bar')) //returns 1 ?!?
intval("2dog") //returns 2 even though the value is definitely not an integer
intval("dog2") //also returns 2


Цей фрагмент потрапив у коментарі документації PHP, і після його тестування він охоплює майже все, що ви кидаєте на нього:

function my_is_int($s) {
    return (is_numeric($s) ? intval($s) == $s : false);
}


my_is_int(2); //true
my_is_int("2"); //true
my_is_int(2.1); //false
my_is_int("2.1"); //false
my_is_int("dog"); //false
my_is_int("2dog"); //false
my_is_int("dog2"); //false
my_is_int(array('foo', 'bar')); //false
my_is_int(array(1)); //false


Але обережно:

my_is_int(2.0); //true
my_is_int("2.0"); //true

6

Один із дійсно чистих способів, який я люблю використовувати, - це той. Ви кидаєте це двічі, по-перше int, по-друге string, потім ви суворо порівнюєте ===. Дивіться приклад нижче:

$value === (string)(int)$value;

Тепер, стосовно функції my_is_int, ви можете зробити щось подібне:

function my_is_int($value){ return $value === (string)(int)$value; }
my_is_int('2');  // true
my_is_int('2.1') // false

4
function my_is_int($var) {
    return preg_match('/^\d+$/', $var);
}

1
Я використав цю варіацію, яка дозволила сигналу числа:'/^[-+]?[0-9]+$/'
Denilson Sá Maia

Це те саме, що і ctype_digit, він також дозволяє провести новий рядок.
jgmjgm

3

Я використовую цей:

function isInt($val){

    return (filter_var($val, FILTER_VALIDATE_INT) !== false && strpos($val, '-') === false);

}

var_dump (isInt("1"));

3

Ви можете просто перевірити наявність номера, якщо він перевіряється, ніж при передачі дається подвійний чи ні:

((is_numeric($var) && !is_double(1*$var)));

Тільки для позитивних цифр:

(is_numeric($var) && !is_double(1*$var)) && ($var >= 0)

Перевірка:

$numbersToCheck = array("a", "-1", "1", "1.0", "1.2");

foreach ($numbersToCheck as $var) {
    echo $var . " is integer? ";var_dump((is_numeric($var) && !is_double(1*$var)));

    echo $var . " is a positive integer? ";var_dump((is_numeric($var) && !is_double(1*$var)) && ($var >= 0));
}

Вихід:

a is integer? bool(false)
a is a positive integer? bool(false)
-1 is integer? bool(true)
-1 is a positive integer? bool(false)
1 is integer? bool(true)
1 is a positive integer? bool(true)
1.0 is integer? bool(false)
1.0 is a positive integer? bool(false)
1.2 is integer? bool(false)
1.2 is a positive integer? bool(false)

is_numeric () було те, що я шукав.
ccjjmartin

1
Щоб переконатися, що це те, що ви хочете, вам потрібно буде прочитати джерело PHP, оскільки воно недостатньо добре зафіксовано. Наприклад, is_numeric приймає провідний пробіл, що не є документованим у керівництві PHP. Також слід пам’ятати, що рядкові цілі числа, які є занадто великими, щоб бути представленими нативним Int, перетворюються на плаваючі.
jgmjgm

2

Спробуйте це:

$string='12abc';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: Invalid!

$string='789';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: int 789

$string='345.00';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: 345

$string='123.01';
if ((int)$string==$string) var_dump((int)$string); else echo 'Invalid!';
// Outputs: Invalid!

Також працює, якщо у вашому рядку $ є десяткові знаки


Це буде вважати 123xxxx як дійсний рядок int.
jgmjgm

2

Це також подбає про негативну кількість

function myIsInt()
{
   return (is_numeric($var) AND (is_int($var) OR ctype_digit(trim($var, '-'))))
}
//'234-' => false
//'-234' => true
//'--234' => false
//'234' => true

Я не розумію, чому це повернеться помилковим для 234- або -344, ви це тестували? Це повернеться правдою для ----- 1234 ----
jgmjgm


2

Я придумав спосіб, якого я не міг ніде знайти, тому вкладаю його сюди:

Без зайвої приналежності це: ctype_digit((string) abs($input))

Приклад:

function means_int($input) {
    return ctype_digit((string) abs($input));
}

$list = array(
    0,
    '0',
    1,
    '1',
    1.1,
    '1.1',
    2.0,
    '2.0',  
    2.6,
    '2.6',
    -4,
    '-4',   
    -3.2,
    '-3.2',
    -30.02,
    '-30.02',   
    100.00,
    '100.00',   
);

foreach ($list as $x) {
    var_dump($x);
    var_dump(means_int($x));
    echo PHP_EOL;
}

Результати: (як передбачається, я думаю)

int(0)
bool(true)

string(1) "0"
bool(true)

int(1)
bool(true)

string(1) "1"
bool(true)

float(1.1)
bool(false)

string(3) "1.1"
bool(false)

float(2)
bool(true)

string(3) "2.0"
bool(true)

float(2.6)
bool(false)

string(3) "2.6"
bool(false)

int(-4)
bool(true)

string(2) "-4"
bool(true)

float(-3.2)
bool(false)

string(4) "-3.2"
bool(false)

float(-30.02)
bool(false)

string(6) "-30.02"
bool(false)

float(100)
bool(true)

string(6) "100.00"
bool(true)

abs - це введення даних від рядка до числового. Це те саме, що is_int (+ $ var).
jgmjgm

2

Можливо, не найефективніший спосіб це зробити. Але ви можете написати це одним рядком.

function my_is_int($input) {
    return intval($input).'' === $input.'';
}

Як і очікувалося:

    my_is_int(1);     // TRUE
    my_is_int(-1);    // TRUE
    my_is_int(1.2);   // FALSE
    my_is_int("1");   // TRUE
    my_is_int("-1");  // TRUE
    my_is_int("1.2"); // FALSE
    my_is_int(0);     // TRUE
    my_is_int(null);  // FALSE

Отримав:

    my_is_int(1.0);   // TRUE
    my_is_int("1.0"); // FALSE

Цей правильний, якщо ви впевнені, що $ input є рядковим типом.
jgmjgm

2

На кілька років пізніше, але на основі наведених тут відповідей я придумав рішення, яке є дещо точнішим (зокрема, щодо булів, зокрема) та більш ефективним (на мою думку), ніж більшість інших відповідей:

function my_is_int($s) {
    return ctype_digit($s) || is_int($s);
}

Працюючи, як очікувалося, для цього:

my_is_int(2);                   // true
my_is_int("2");                 // true
my_is_int(-2);                  // true
my_is_int(2.0);                 // false
my_is_int("2.0");               // false
my_is_int(2.1);                 // false
my_is_int("2.1");               // false
my_is_int("dog");               // false
my_is_int("2dog");              // false
my_is_int("dog2");              // false
my_is_int(array('foo', 'bar')); // false
my_is_int(array(1));            // false
my_is_int(true);                // false
my_is_int(false);               // false
my_is_int("true");              // false
my_is_int("false");             // false
my_is_int("0x101010");          // false

За винятком цих 2:

my_is_int(0x101010);            // true
my_is_int("-2");                // false

дуже хороша. якщо ви хочете обробити числовий рядок з від'ємним знаком, ви можете змінити на ctype_digits (preg_replace ('/ ^ - /', '', $ s)), хоча це починає бути трохи затятим. також, 0x101010 - це int, тому це не помилка, але, скоріше, "0x101010" може вважатися невірним, і я впевнений, що двійкові рядки, що відповідають дворядним буквальним позначенням ("0b11111"), також не зможуть
fbas

1

Як щодо:

function isIntStr($str) {
   return preg_match('/^(-?\d+)(?:\.0+)?$/', trim($str), $ms)
       && bcComp($ms[1], PHP_INT_MAX) <= 0
       && bcComp($ms[1], -PHP_INT_MAX - 1) >= 0;
}

Ця функція повинна повертати true лише для будь-якого номера рядка, який можна передати в int з (int)абоintval() без втрати нічого математичного значення (наприклад, не нулі після десяткових знаків або числа за межами цілого діапазону PHP), приймаючи речі, які не мають математичного значення (наприклад, пробіли; провідні нулі; або, після десяткової крапки, виключно нулі).

Він поверне помилкове, '10.'але не для '10.0'. Якщо ви хочете '10.'бути правдою, ви можете змінити +після 0в регулярному виразі після на *.


1

Ось якийсь код, який я використав, він, здається, працює добре і не має жодних проблем, які мають багато хто з інших.

if (0 != strlen(str_replace(range(0, 9), '', $TestInt))) { print 'Not an integer!';}

Він не перевіряє порядок тощо, тому не призначений для негативних цілих чисел, але з деяким кодом додавання, який можна зробити також, використовуючи деякі інші ідеї зверху. Він також може бути адаптований для роботи з двійковими array('0', '1')або шістнадцятковими зображеннями тощо.


Це просто дуже дорогий ctype_digit.
jgmjgm

1

Дивіться це. Перетворює $ val у ціле число, а потім перевіряє, чи оригінальний $ val, перетворений у рядок, є ІДЕНТИЧНИМ (===) - просто == не буде працювати, як очікувалося - у цілий val, перетворений у рядок.

function validInt($val, $min=null, $max=null) {
    $ival = intval($val);
    //echo "'$ival' '$val'<br>\n"; // Uncomment to see the comparisons done in below if block
    if(''.$ival !== ''.$val) {
        return false;
    }
    if($min !== null && $ival < $min)
        return false;
    if($max !== null && $ival > $max)
        return false;
    return true;
}

Якщо ви не перевірите значення рядків, це може не працювати, як ви очікували:

$nums = array(
    '1',
    '+1',
    '-1',
    '01',
    '1.0',
    '.0',
    '1.123',
    'a123',
    '0x101010',
    1,
    -1,
    01,
    1.0,
    .0,
    1.123,
    0x101010,
);
foreach($nums as $num) {
    if(validInt2($num))
        echo $num." - Valid integer.<br>\n";
    else
        echo $num." - Not a valid integer.<br>\n";
}

Вихід:

1 - Valid integer.
+1 - Not a valid integer.
-1 - Valid integer.
01 - Not a valid integer.
1.0 - Not a valid integer.
.0 - Not a valid integer.
1.123 - Not a valid integer.
a123 - Not a valid integer.
0x101010 - Not a valid integer.
1 - Valid integer.
-1 - Valid integer.
1 - Valid integer.
1 - Valid integer.
0 - Valid integer.
1.123 - Not a valid integer.
1052688 - Valid integer.

Причина навіть у тому випадку, якщо ви використовуєте шістнадцятковий (0x101010), восьмеричний (01) або ціле число, що зберігається як float (1.0, 0.0), все внутрішньо зберігаються як float. Однак якщо ви використовуєте функцію для перевірки наявності int, що зберігається як рядок, вона буде працювати.


1
public static function isNumeric($value, $negativ = false) {
    return is_int($value) || is_string($value) && (
        ctype_digit($value) || (
            $negativ && $value{0} == '-' && ctype_digit(substr($value, 1))
        )
    );

    //alternativ:
    //return $value == (int) $value;
}

У ньому відсутня перевірка цього рядка! == ''. Якщо ви введете в це дійсно довге ціле число, воно також поверне істину для того, що PHP буде кинутий плавати!
jgmjgm

1

Можна скористатися наступною умовою. Зауважте, що не слід використовувати! ==

$value = 12; // true
$value = '12'; // true
$value = 'abc'; // false
$value = 12.1; // false
$value = '12.1'; // false

if (!is_numeric($value) || (int) $value != (float) $value) {
    echo "false";
} else {
    echo "true";
}

1

Це прекрасно працює! Сподіваюся, це буде корисно вам =)

$value = 1; // true
$value = '1'; // true
$value = '1.0'; // true
$value = ' 1'; // true
$value = '01'; // true

$value = -1; // true
$value = '-1'; // true
$value = '-1.0'; // true
$value = ' -1'; // true
$value = '-01'; // true

$value = PHP_INT_MIN; // true
$value = PHP_INT_MAX; // true
$value = 0x000001; // true

$value = 'a'; // false
$value = '1a'; // false
$value = 'a1'; // false
$value = 1.1; // false
$value = '1.1'; // false
$value = '--1'; // false
$value = []; // false
$value = [1,2]; // false
$value = true; // false
$value = false; // false
$value = null; // false
$value = 'NaN'; // false
$value = 'undefined'; // false

function isInt($value) {
    return is_numeric($value) && floatval(intval($value)) === floatval($value);
}

1
Відповідаючи на старе запитання, ваша відповідь була б набагато кориснішою для інших користувачів StackOverflow, якщо ви включили якийсь контекст, щоб пояснити, як допомагає ваша відповідь, особливо на питання, на яке вже є прийнята відповідь. Див.: Як написати гарну відповідь .
Девід Бак

0

Якщо ви хочете по-справжньому знати, чи рядок є дійсним поданням справжнього цілого типу PHP ...

in_array($string, array_map('strval', range(PHP_INT_MIN, PHP_INT_MAX)), true)

Однак це неможливо запустити, оскільки набір занадто великий (в цьому випадку не впишеться в пам'ять, якщо замість нього циклічно буде потрібно занадто багато циклів процесора).

Можливо, ви можете виконати двійковий пошук із порівнянням рядків, проте є кращі способи.

Найпростіша істота:

strlen($string) <= max(strlen((string)PHP_INT_MIN), strlen((string)PHP_INT_MAX)) && $string === (string)(int)$string

Є й інші незвичайні способи наблизитись до нього, такі як:

is_int(array_keys([$string => null])[0])

Ви також можете робити порівняння рядків, але вам все одно доведеться робити такі речі, як ctype_digit, перевірити, чи довжина є розумною (не витрачайте процесор, перш ніж робити такі речі, як ctype_digit), і мати якісь незручні можливості обробки негативних цифр.

Зауважте, що filter_var цього не робить правильно стверджувати , що рядок дійсно уявлення РНР ціле. Це дозволить провідному + та навколишньому пробілу.

Внутрішньо PHP використовує функцію "_zend_handle_numeric_str" для суворого порівняння, але він не піддає це прямо деінде, отже, і трюк, використовуючи клавіші масиву (який використовує його для перетворення будь-якої рядки, що представляє ціле число PHP, до цілого числа PHP).

Якщо ви хочете, щоб бінарне безпечне перетворення в PHP і з нього, це такий підхід.

Не всі можуть цього хотіти, і це може бути випадком обробки даних користувача. filter_var не надто поганий для цього і в більшості випадків буде досить безпечним для людей, які не знайомі з PHP.

Перевірка довжини, ctype_digit, а потім перевірка перетвореного значення, що знаходиться в діапазоні, також є досить надійним для введення користувачем. Більш складні схеми можуть вимагати обрізки або регулярного вираження.

Проблема з великою кількістю відповідей у ​​цьому відношенні полягає в тому, що хоча питання неясне, відповідей не повинно бути. Якщо ви збираєтесь запропонувати рішення, ви повинні мати можливість пояснити, що саме воно буде, і чого ви не очікуєте. Без цього не можна сказати, чи відповідає відповідь на питання чи безпечний. Посібник з PHP не завжди допомагає, оскільки не пояснює всі застереження для кожного з відповідних методів, які він надає. Такі речі, як ctype_digit та is_int, дуже надійні та легко передбачувані, але специфіка is_numeric, filter_var та жонглювання (+ $ var) чи кастинг (intval / floatval) погано зафіксована.

Це грі PHP для вас. Він має безліч схем для інтерпретації рядків як цілих чисел з невідповідностями. Найсуворіший метод перевірки цілого рядка користувачеві не піддається безпосередньому впливу.


0

Якщо ви обробляєте числові ідентифікатори з mysql і намагаєтесь встановити валідацію, щоб вона була дійсною нульовою int або intin, але у рядковому форматі ( оскільки mysql завжди повертає все у рядковому форматі ), а не NULL. Можна скористатися наступним. Це самий спосіб підтвердження помилок / попереджень / сповіщень, щоб дозволити лише те, int/string-intsщо я знайшов.

...
if(empty($id) || !is_scalar($id) || !ctype_digit($id)){
  throw("Invalid ID");
}
...

0

Ось просте рішення, яке використовує is_numeric , floatval та intval :

function is_string_an_int($string)
{
    if (is_string($string) === false)
    {
        //throw some kind of error, if needed
    }

    if (is_numeric($string) === false || floatval(intval($string)) !== floatval($string))
    {
        return false;
    }

    else
    {
        return true;
    }
}

Результати:

is_string_an_int('-1'); //true
is_string_an_int('-1.0'); //true
is_string_an_int('-1.1'); //false
is_string_an_int('0'); //true
is_string_an_int('0.0'); //true
is_string_an_int('0.1'); //false
is_string_an_int('1'); //true
is_string_an_int('1.0'); //true
is_string_an_int('1.1'); //false
is_string_an_int('' . PHP_INT_MAX); //true
is_string_an_int('foobar'); //false
is_string_an_int('NaN'); //false
is_string_an_int('null'); //false
is_string_an_int('undefined'); //false

Зауважте, що значення, що перевищують PHP_INT_MAX, можуть повертати помилкові.


-1

Чи не можна використовувати is_numeric (), а потім перевірити наявність "". в рядку (хоча не особливо чутливий до культури).

Крім того, використовуйте is_numeric (), потім перекиньте на подвійний і подивіться, чи $ var == floor ($ var) (повинен повернути true, якщо це ціле число).


Це єдина відповідь, яка розпізнає зміни місцевості, а також змінює те, як PHP інтерпретує та виводить числа. Будьте дуже обережні з набором значень та рішеннями, які роблять припущення щодо роздільників.
jgmjgm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.