La forma más rápida de convertir cadenas a enteros en PHP

251

Usando PHP, ¿cuál es la forma más rápida de convertir una cadena como esta: "123"a un entero?

¿Por qué ese método en particular es el más rápido? ¿Qué sucede si recibe una entrada inesperada, como "hello"o una matriz?

nickf
fuente
12
bueno, si no duele (legibilidad), ¿por qué no hacer las cosas de la manera más eficiente posible?
nickf
19
Si no afecta la velocidad, ¿por qué no hacer las cosas de la manera más legible posible?
Andy Lester
44
@ Andy, mira las pruebas de referencia a continuación. ¡La diferencia entre (int)y intval()puede ser superior al 400%!
nickf
66
lo más rápido importa porque la velocidad es importante para la experiencia del usuario. ¡Cuando tienes muchas operaciones en marcha, quieres tenerlas RÁPIDAS!
philipp
13
sin patear un caballo muerto, también diría que la cuestión de la velocidad frente a la legibilidad es irrelevante en este caso, porque la pregunta fue etiquetada bajo optimización. La razón para querer velocidad en una pregunta etiquetada bajo optimización se explica por sí misma.
totallyNotLizards

Respuestas:

371

Acabo de configurar un ejercicio rápido de evaluación comparativa:

Function             time to run 1 million iterations
--------------------------------------------
(int) "123":                0.55029
intval("123"):              1.0115  (183%)

(int) "0":                  0.42461
intval("0"):                0.95683 (225%)

(int) int:                  0.1502
intval(int):                0.65716 (438%)

(int) array("a", "b"):      0.91264
intval(array("a", "b")):    1.47681 (162%)

(int) "hello":              0.42208
intval("hello"):            0.93678 (222%)

En promedio, llamar a intval () es dos veces y media más lento, y la diferencia es mayor si su entrada ya es un número entero.

Sin embargo, me interesaría saber por qué .


Actualización: he vuelto a ejecutar las pruebas, esta vez con coerción (0 + $var)

| INPUT ($x)      |  (int) $x  |intval($x) |  0 + $x   |
|-----------------|------------|-----------|-----------|
| "123"           |   0.51541  |  0.96924  |  0.33828  |
| "0"             |   0.42723  |  0.97418  |  0.31353  |
| 123             |   0.15011  |  0.61690  |  0.15452  |
| array("a", "b") |   0.8893   |  1.45109  |  err!     |
| "hello"         |   0.42618  |  0.88803  |  0.1691   |
|-----------------|------------|-----------|-----------|

Anexo: Acabo de encontrar un comportamiento ligeramente inesperado que debe tener en cuenta al elegir uno de estos métodos:

$x = "11";
(int) $x;      // int(11)
intval($x);    // int(11)
$x + 0;        // int(11)

$x = "0x11";
(int) $x;      // int(0)
intval($x);    // int(0)
$x + 0;        // int(17) !

$x = "011";
(int) $x;      // int(11)
intval($x);    // int(11)
$x + 0;        // int(11) (not 9)

Probado con PHP 5.3.1

nickf
fuente
99
Probablemente tenga algo que ver con el hecho de que intval () invoca una llamada de función, mientras que el reparto se maneja directamente en la calculadora de expresiones del intérprete. Esta también puede ser la razón por la cual una coerción es aún más rápida.
staticsan
10
Su ejemplo de coerción puede simplificarse aún más utilizando el poco conocido operador unario plus de php. $ x + 0 -> + $ x
Ozzy
@Ozzy Eso es genial. ¡Gracias por el consejo! +"15" == 15
caiosm1005
1
@John ya que prueba solo dos casos en ese primer código (int)y intval, y en cada par, da un% en intval, el caso base debe ser (int). Pero tiene un buen punto de que habría sido más claro si lo hubiera dicho explícitamente, ¡especialmente porque más tarde agregó un tercer caso!
ToolmakerSteve
2
¿Hay algún cambio en estos resultados en las nuevas versiones de PHP?
Artyom
34

Personalmente siento que el casting es el más bonito.

$iSomeVar = (int) $sSomeOtherVar;

Si se envía una cadena como 'Hola', se convertirá al entero 0. Para una cadena como '22 años ', se convertirá al entero 22. Cualquier cosa que no se pueda analizar a un número se convierte en 0.

Si realmente NECESITA la velocidad, supongo que las otras sugerencias aquí son correctas al suponer que la coerción es la más rápida.

Rexxars
fuente
77
Curiosamente, las matrices se lanzan a 1. ir figura.
nickf
2
@nickf No es así: también se puede convertir a 0. Convierte su valor booleano (verdadero | falso) en un entero: 'falso' = 0, 'verdadero' = 1. Una matriz es falsa si está 100% vacía, y es verdadera si contiene CUALQUIER información, incluso si solo está vacía cadenas o valores NULL. Si tuviera que lanzar una matriz vacía a un número entero, se convertiría en 0. (¡Sí, estoy al tanto de esto!)
Super Cat
15

Haz una prueba.

   string coerce:          7.42296099663
   string cast:            8.05654597282
   string fail coerce:     7.14159703255
   string fail cast:       7.87444186211

Esta fue una prueba que ejecutó cada escenario 10,000,000 de veces. :-)

Co-ercion es 0 + "123"

El casting es (integer)"123"

Creo que Coerción es un poco más rápido. Ah, e intentarlo 0 + array('123')es un error fatal en PHP. Es posible que desee que su código verifique el tipo del valor proporcionado.

Mi código de prueba está abajo.


function test_string_coerce($s) {
    return 0 + $s;
}

function test_string_cast($s) {
    return (integer)$s;
}

$iter = 10000000;

print "-- running each text $iter times.\n";

// string co-erce
$string_coerce = new Timer;
$string_coerce->Start();

print "String Coerce test\n";
for( $i = 0; $i < $iter ; $i++ ) {
    test_string_coerce('123');
}

$string_coerce->Stop();

// string cast
$string_cast = new Timer;
$string_cast->Start();

print "String Cast test\n";
for( $i = 0; $i < $iter ; $i++ ) {
    test_string_cast('123');
}

$string_cast->Stop();

// string co-erce fail.
$string_coerce_fail = new Timer;
$string_coerce_fail->Start();

print "String Coerce fail test\n";
for( $i = 0; $i < $iter ; $i++ ) {
    test_string_coerce('hello');
}

$string_coerce_fail->Stop();

// string cast fail
$string_cast_fail = new Timer;
$string_cast_fail->Start();

print "String Cast fail test\n";
for( $i = 0; $i < $iter ; $i++ ) {
    test_string_cast('hello');
}

$string_cast_fail->Stop();

// -----------------
print "\n";
print "string coerce:          ".$string_coerce->Elapsed()."\n";
print "string cast:            ".$string_cast->Elapsed()."\n";
print "string fail coerce:     ".$string_coerce_fail->Elapsed()."\n";
print "string fail cast:       ".$string_cast_fail->Elapsed()."\n";


class Timer {
    var $ticking = null;
    var $started_at = false;
    var $elapsed = 0;

    function Timer() {
        $this->ticking = null;
    }

    function Start() {
        $this->ticking = true;
        $this->started_at = microtime(TRUE);
    }

    function Stop() {
        if( $this->ticking )
            $this->elapsed = microtime(TRUE) - $this->started_at;
        $this->ticking = false;
    }

    function Elapsed() {
        switch( $this->ticking ) {
            case true: return "Still Running";
            case false: return $this->elapsed;
            case null: return "Not Started";
        }
    }
}
staticsan
fuente
Agregué settypeesta prueba y la ejecuté usando PHP 7. Cast salió un poco adelante y una gran mejora en el rendimiento en general: string coerce: 1.9255340099335 string cast: 1.5142338275909 string settype: 4.149735212326 string fail coerce: 1.2346560955048 string fail cast: 1.3967711925507 string fail settype: 4.149735212326
bstoney
9

Simplemente puede convertir una cadena larga en un entero utilizando FLOAT

$float = (float)$num;

O si quieres un entero no flotante, entonces ve con

$float = (int)$num;

Por ej.

(int)   "1212.3"   = 1212 
(float) "1212.3"   = 1212.3
Nishchit Dhanani
fuente
1
¿Eh? Si quieres un int, ¿por qué no lo usarías (int)? Puede ser cierto que si la cadena contiene un número entero, (float)devolverá un valor que se parece mucho a un número entero (aunque su tipo interno sea probablemente float), pero ¿por qué haría esto si la especificación es devolver un valor entero? ? Supongamos que la cadena entrante es "1.3"? No obtendrás un número entero. Además, por el bien de cualquiera que lea el código en el futuro, debe decir lo que quiere decir. Si quiere decir "debería ser un número entero", entonces diga que (int)no (float).
ToolmakerSteve
7
$int = settype("100", "integer"); //convert the numeric string to int
Elric Wamugu
fuente
3
¡Creo que alguna referencia o prueba está en orden!
Mehran
$ int en realidad sería un booleano aquí, si la declaración funcionara, pero no lo haría ya que el primer parámetro de settype () se pasa por referencia y, por lo tanto, debe ser una var.
Queue
7

entero excract de cualquier cadena

$ in = 'tel.123-12-33';

preg_match_all('!\d+!', $in, $matches);
$out =  (int)implode('', $matches[0]);

// $ out = '1231233';

Desarrollador
fuente
¡Eres lo mejor de lo mejor de lo mejor! He pasado horas para convertir algunas var de cadena de datos json a entero, ¡solo su método ayudó! ¡gracias!
Kamnibula
4

Más resultados de referencia ad-hoc:

$ time php -r 'for ($x = 0;$x < 999999999; $x++){$i = (integer) "-11";}'     

real    2m10.397s
user    2m10.220s
sys     0m0.025s

$ time php -r 'for ($x = 0;$x < 999999999; $x++){$i += "-11";}'              

real    2m1.724s
user    2m1.635s
sys     0m0.009s

$ time php -r 'for ($x = 0;$x < 999999999; $x++){$i = + "-11";}'             

real    1m21.000s
user    1m20.964s
sys     0m0.007s
Daniel
fuente
Sería mejor escribir la declaración que se está probando 10 veces en cada ciclo, para que el tiempo no esté dominado por la sobrecarga del ciclo. Por ej { $i = +"-11"; $i = +"-11"; $i= +"-11"; $i= +"-11"; ... }. También es arriesgado usar el valor literal "-11"directamente, a menos que esté seguro de que el lenguaje no hará parte del trabajo en el momento de la compilación. Probablemente esté bien para un lenguaje dinámico como PHP, pero menciono para referencia futura si prueba fórmulas en otros idiomas. Es más seguro establecer una variable $x = "-11"antes del bucle de prueba y luego usarla. De modo que el código interno es $i =+$x.
ToolmakerSteve
4

Ejecutó un punto de referencia, y resulta que la forma más rápida de obtener un número entero real (usando todos los métodos disponibles) es

$foo = (int)+"12.345";

Solo usando

$foo = +"12.345";

devuelve un flotador

Andrew Plank
fuente
2
Esto es más rápido que simplemente (int)"12.345"? ¿Más rápido en qué porcentaje?
ToolmakerSteve