¿Cómo verificar que una cadena es un int, pero no un doble, etc.?

155

PHP tiene una intval()función que convertirá una cadena en un número entero. Sin embargo, quiero comprobar que la cadena es un número entero de antemano, para poder dar un mensaje de error útil al usuario si está equivocado. PHP tiene is_int(), pero eso devuelve falso para una cadena como "2".

PHP tiene la is_numeric()función, pero eso devolverá verdadero si el número es un doble. Quiero algo que devuelva falso para un doble, pero cierto para un int.

p.ej:

my_is_int("2") == TRUE
my_is_int("2.1") == FALSE
Rory
fuente
66
¿Cómo se debe tratar "2.0"?
nickf

Respuestas:

183

¿Qué tal el uso ctype_digit?

Del manual:

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

El ejemplo anterior generará:

La cadena 1820.20 no consta de todos los dígitos.
La cadena 10002 consta de todos los dígitos.
La cadena wsl! 12 no consta de todos los dígitos.

Esto solo funcionará si su entrada es siempre una cadena:

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

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

Si su entrada puede ser de tipo int, combínela ctype_digitcon is_int.

Si le importan los números negativos, deberá verificar la entrada de un precedente -y, de ser así, invocar ctype_digituna substrde las cadenas de entrada. Algo como esto lo haría:

function my_is_int($input) {
  if ($input[0] == '-') {
    return ctype_digit(substr($input, 1));
  }
  return ctype_digit($input);
}
Dominic Rodger
fuente
77
¿números negativos?
Anurag
1
@Anurag, editado en (aunque si realmente te importan estas cosas, una expresión regular probablemente sea más simple).
Dominic Rodger
44
Si desea verificar enteros negativos, agregue el uso de abs () como tal en ctype_digit(abs($str))lugar de solo ctype_digit.
mejora
3
@enchance abs('definitelynotanint') === 0. por otra parte, ctype_digitsolo toma hilos
Klesun
Esto no es correcto. ¡Un entero grande en PHP es un doble!
jgmjgm
91

filter_var Deberías hacerlo:

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

pero

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

Si solo desea booleanos como valores de retorno, ajústelo en una función, por ejemplo

function validatesAsInt($number)
{
    $number = filter_var($number, FILTER_VALIDATE_INT);
    return ($number !== FALSE);
}
Gordon
fuente
2
Me gustan la mayoría de las respuestas, pero creo que esta es la más elegante.
Ian Dunn
66
@grenoult 3v4l.org/qVRRS proporciona int 0 para cadenas y enteros, por lo que no estoy seguro de por qué cree que no funciona. ¿Estás comparando el resultado con ==quizás?
Gordon
1
@Gordon, mi error, de hecho me estaba comparando con solo un if 3v4l.org/IAvCF
Guillaume Renoult
Esto hará algunas cosas extrañas, como return true para "+123". Está mitigado por el hecho de que está destinado a devolver el verdadero int, pero aún así podría no ser lo que la gente quiere, lo que es un estricto chequeo int.
jgmjgm
@ MohammedRédaOUASSINI, el OP solicitó validar cadenas , no flotantes. Se va a funcionar si se pasa en '-1,0' (una cadena) que lo hará no trabajo si se pasa en -1.0 (un flotador), pero eso no era un requisito para empezar. Ver 3v4l.org/bOX6X
Gordon
20

+1 a la respuesta de Dominic (usando ctype_digit). Otra forma de hacerlo es con la coerción de tipo:

$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
nickf
fuente
2
+1 a usted también (aunque me pregunto si ese caso particular se manejaría más claramente recortando la cadena de entrada).
Dominic Rodger
66
Este método no funciona para cadenas no numéricas: is_int ("abc" +0) es verdadero
Kai Pommerenke
1
Es cierto, no funciona para cadenas no numéricas ... qué pasa is_int($inty + 0) && is_numeric($inty)(sé que es una solución perezosa, pero funciona para la mayoría de las intenciones y propósitos ...
Alex Mantaut
El uso de malabarismo de tipos dará como resultado una comprobación bastante flexible de dónde se pueden representar las entradas en todo tipo de formas de cadena, no es una verificación de cadena. Tampoco necesitas hacer +0. Puedes simplemente + $ var.
jgmjgm
13

Envíalo a int. si todavía tiene el mismo valor, int;

function my_is_int($var) {
  $tmp = (int) $var;
  if($tmp == $var)
       return true;
  else
       return false;
}
Greg
fuente
2
O incluso más corto:return $var == (int) $var;
Al.G.
1
@ AI.G. ¿Qué sucede si falla el yeso
DR.
Esto está muy cerca, aunque debe evitar el malabarismo de tipo si no es necesario. Esto funcionará si $ var es "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);
    }
}

Fuente .

GmonC
fuente
en lugar de dispersar mi código con pequeñas funciones de utilidad, prefiero tener algo integrado en php.
Rory
Tampoco me gustan estos hacks. Pero al usar este enfoque o sugerencia de tipo de Dominic, de todas formas va a encapsular toda la implementación en un método. Cuando uso php, siempre tengo una clase "Util" para abordar estos problemas.
GmonC
elseifpuede ser simplemente ifporque ya has regresado en la declaración anterior.
rybo111
Además, no es necesario devolver falso al final.
rybo111
1
Si $dataes un objeto, esto regresará null. Mejor tener el return false;al final.
Theodore R. Smith
6

Tenía la necesidad de un robusto is_intrecientemente. Encontré intval () demasiado impredecible:

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


Encontré este fragmento en los comentarios de la documentación de PHP, y después de probarlo, cubre casi todo lo que le arrojas:

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


Pero cuidado:

my_is_int(2.0); //true
my_is_int("2.0"); //true
Costa
fuente
6

Una forma realmente limpia que me gusta usar es esa. Lo lanzas dos veces, primero adentro int, segundo adentro string, luego comparas estrictamente ===. Vea el siguiente ejemplo:

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

Ahora, sobre su función my_is_int, puede hacer algo como esto:

function my_is_int($value){ return $value === (string)(int)$value; }
my_is_int('2');  // true
my_is_int('2.1') // false
Jonathan Gagne
fuente
4
function my_is_int($var) {
    return preg_match('/^\d+$/', $var);
}
Michael Connor
fuente
1
Usé una variación de esto, permitiendo la señal numérica:'/^[-+]?[0-9]+$/'
Denilson Sá Maia
Esto es lo mismo que ctype_digit, también permite una nueva línea final.
jgmjgm
3

Estoy usando este:

function isInt($val){

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

}

var_dump (isInt("1"));
julesdude
fuente
3

Puede verificar si hay un número, si es así, verifique que al casting se le dé un doble o no:

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

Solo para números positivos:

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

Comprobándolo:

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

Salida:

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)
Jordi Martínez
fuente
is_numeric () era lo que estaba buscando.
ccjjmartin
1
Para asegurarse de que esto es lo que desea, necesitará leer la fuente PHP ya que no está bien documentada. Por ejemplo, is_numeric acepta espacios en blanco iniciales, lo cual no es algo documentado en el manual de PHP. También debe tener en cuenta que los enteros de cadena que son demasiado grandes para representarse con un int nativo se convierten en flotantes.
jgmjgm
2

Prueba esto:

$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!

También funciona si su cadena de $ tiene decimales

Jack M.
fuente
Esto tratará 123xxxx como una cadena int válida.
jgmjgm
2

Esto también se encargará del número negativo

function myIsInt()
{
   return (is_numeric($var) AND (is_int($var) OR ctype_digit(trim($var, '-'))))
}
//'234-' => false
//'-234' => true
//'--234' => false
//'234' => true
wolakpaul
fuente
No entiendo por qué esto sería falso para 234 o --234, ¿lo probaste? Esto volverá verdadero para ----- 1234 ----
jgmjgm
2
function my_is_int($var){
    return is_numeric($var) && gettype($var+0)=='integer';
}
conceder sol
fuente
2

Ideé una forma que no pude encontrar en ningún lado, así que lo estoy poniendo aquí:

Sin más preámbulos es esto: ctype_digit((string) abs($input))

Ejemplo:

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

Resultados: (son como se esperaba, supongo)

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)
Smuuf
fuente
abs está enviando la entrada de la cadena a numérico. Es lo mismo que is_int (+ $ var).
jgmjgm
2

Quizás no sea la forma más eficaz de hacerlo. Pero puedes escribirlo en una línea.

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

Como se esperaba:

    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

Gotcha:

    my_is_int(1.0);   // TRUE
    my_is_int("1.0"); // FALSE
mwallisch
fuente
Este es correcto, siempre y cuando se asegure de que $ input sea del tipo string.
jgmjgm
2

Algunos años tarde, pero en base a las respuestas dadas aquí, se me ocurrió una solución que es un poco más precisa (en booleanos, en particular) y más eficiente (creo) que la mayoría de las otras respuestas:

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

Trabajando como se esperaba para estos:

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

Excepto tal vez por estos 2:

my_is_int(0x101010);            // true
my_is_int("-2");                // false
SharkWipf
fuente
muy agradable. si desea manejar la cadena numérica con signo negativo, puede cambiar a ctype_digits (preg_replace ('/ ^ - /', '', $ s)), aunque eso comienza a ser un poco largo. Además, 0x101010 es un int, por lo que no es un error, sino que "0x101010" podría considerarse incorrecto, y estoy seguro de que las cadenas binarias que coinciden con la notación literal binaria ("0b11111") también fallarían
fbas
1

Qué tal si:

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

Esta función solo debe devolver verdadero para cualquier número de cadena que se pueda convertir a int con (int)ointval() sin perder nada de importancia matemática (como no ceros después del punto decimal o números fuera del rango entero de PHP) mientras se aceptan cosas que no son matemáticamente significativas (como el espacio en blanco; ceros a la izquierda; o, después del punto decimal, ceros exclusivamente).

Volverá falso para '10.'pero no para '10.0'. Si desea '10.'ser sincero, puede cambiar el +después 0de la expresión regular a *.


fuente
1

Aquí hay un código que he usado que parece funcionar bien y no tiene ninguno de los problemas que tienen muchos de los otros.

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

No verifica el orden, etc., por lo que no está destinado a enteros negativos, sino con algún código de adición que también se puede hacer usando algunas de las otras ideas anteriores. También se puede adaptar para trabajar con binarios array('0', '1')o hexadecimales, etc.

Dragonaire
fuente
Esto es solo un ctype_digit muy costoso.
jgmjgm
1

Mira esto. Convierte $ val en entero y luego comprueba si el $ val original convertido en cadena es IDÉNTICO (===), solo == no funcionará como se esperaba, en el entero val convertido en cadena.

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

Si no verifica los valores de cadena, puede que no funcione como lo espera:

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

Salida:

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.

La razón es que incluso si usa hexadecimal (0x101010), octal (01) o un número entero almacenado como flotante (1.0, 0.0), internamente todos se almacenan como flotante. Sin embargo, si usa la función para verificar int almacenado como una cadena, funcionará.

Joe
fuente
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;
}
Volker
fuente
¡Falta una marca en esa cadena! == ''. ¡Si pones un número entero realmente largo en esto, también será cierto para algo que PHP lanzaría a un flotante!
jgmjgm
1

Puede usar la siguiente condición. ¡Tenga en cuenta que no debe usar! ==

$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";
}
Saman Shafigh
fuente
1

Funciona perfectamente! Espero que te sea útil =)

$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);
}
vietbq
fuente
1
Al responder una pregunta anterior, su respuesta sería mucho más útil para otros usuarios de StackOverflow si incluyera algún contexto para explicar cómo ayuda su respuesta, particularmente para una pregunta que ya tiene una respuesta aceptada. Ver: ¿Cómo escribo una buena respuesta ?
David Buck
0

Si desea saber realmente si una cadena es una representación válida de un verdadero tipo entero de PHP ...

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

Sin embargo, esto es imposible de ejecutar ya que el conjunto es demasiado grande (no cabe en la memoria en este caso, si realiza un bucle en su lugar, tomará demasiados ciclos de CPU).

Quizás pueda hacer una búsqueda binaria con comparación de cadenas, sin embargo, hay mejores formas.

El ser más simple:

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

Hay otras formas inusuales de abordarlo, como:

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

También puede hacer una comparación de cadenas, pero aún tendrá que hacer cosas como ctype_digit, verificar que la longitud sea razonable (no desperdicie la CPU antes de hacer cosas como ctype_digit) y tener un manejo incómodo para los números negativos.

Tenga en cuenta que filter_var no afirma correctamente que una cadena es realmente la representación de un entero PHP. Permitirá un espacio en blanco inicial + y circundante.

Internamente, PHP usa la función "_zend_handle_numeric_str" para una comparación estricta, pero no expone directamente esto en ningún lado, de ahí el truco que usa las teclas de matriz (que lo usa para convertir cualquier cadena que sea una representación de un entero PHP en un entero PHP).

Si desea una conversión segura binaria hacia y desde PHP, este es el enfoque a seguir.

No todos pueden querer eso y podría ser un caso de manejo de la entrada del usuario. filter_var no es tan malo para eso y será bastante seguro en la mayoría de los casos para las personas nuevas en PHP.

Una verificación de longitud, ctype_digit y luego una verificación del valor convertido de que está en un rango también es bastante sólida para la entrada del usuario. Los esquemas más complejos pueden querer recortar o regex.

El problema con muchas de las respuestas aquí a ese respecto es que, si bien la pregunta es vaga, las respuestas no deberían serlo. Si va a proponer una solución, debería poder explicar exactamente qué es lo que esperará y qué no. Sin eso, no se sabe si una respuesta coincide con una pregunta o si es segura. El manual de PHP no siempre ayuda porque no explica todas las advertencias para cada uno de los métodos relevantes que proporciona. Cosas como ctype_digit e is_int son muy confiables y fáciles de predecir, pero los detalles de is_numeric, filter_var y malabares (+ $ var) o casting (intval / floatval) están poco documentados.

Este es PHP fudge para ti. Tiene una gran cantidad de esquemas para interpretar cadenas como enteros, con inconsistencias. El método más estricto para validar una cadena entera no está expuesto directamente al usuario.

jgmjgm
fuente
0

Si está manejando ID numéricos de mysql y está tratando de imponer una validación para que sea un int no cero válido o un intpero en formato de cadena ( ya que mysql siempre devuelve todo en formato de cadena ) y no NULL. Puedes usar lo siguiente. Esta es la forma más a prueba de error / advertencia / aviso para permitir solo int/string-intslo que encontré.

...
if(empty($id) || !is_scalar($id) || !ctype_digit($id)){
  throw("Invalid ID");
}
...
Mohd Abdul Mujib
fuente
0

Aquí hay una solución simple que usa is_numeric , floatval e 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;
    }
}

Resultados:

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

Tenga en cuenta que los valores mayores que PHP_INT_MAX pueden devolver falso.

Pikamander2
fuente
-1

Podría usar is_numeric () y luego verificar la presencia de "." en la cadena (aunque no particularmente sensible a la cultura).

Alternativamente, use is_numeric (), luego convierta a un doble y vea si $ var == floor ($ var) (debería devolver verdadero si es un entero).

Paolo
fuente
Esta es la única respuesta que reconoce los cambios locales y también cambia la forma en que PHP interpreta y genera números. Tenga mucho cuidado con setlocale y las soluciones que hacen suposiciones sobre los separadores.
jgmjgm