Le moyen le plus rapide de convertir une chaîne en entier en PHP

251

En utilisant PHP, quel est le moyen le plus rapide pour convertir une chaîne comme celle-ci: "123"en entier?

Pourquoi cette méthode particulière est-elle la plus rapide? Que se passe-t-il s'il reçoit une entrée inattendue, comme "hello"ou un tableau?

nickf
la source
12
eh bien si ça ne fait pas mal (lisibilité), pourquoi ne pas faire les choses de la manière la plus efficace possible?
nickf
19
Si cela ne nuit pas à la vitesse, pourquoi ne pas faire les choses de la manière la plus lisible possible?
Andy Lester,
4
@Andy, regardez les tests de référence ci-dessous. La différence entre (int)et intval()peut être supérieure à 400%!
nickf
6
la rapidité compte car la vitesse est importante pour l'expérience utilisateur Lorsque vous avez beaucoup d'opérations en cours, vous voulez les avoir RAPIDEMENT!
philipp
13
sans donner un coup de pied à un cheval mort, je dirais également que la question de la vitesse par rapport à la lisibilité n'est pas pertinente dans ce cas, car la question a été étiquetée sous optimisation. La raison de vouloir de la vitesse dans une question étiquetée sous optimisation est explicite.
totallyNotLizards

Réponses:

371

Je viens de mettre en place un exercice de benchmarking rapide:

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 moyenne, appeler intval () est deux fois et demi plus lent, et la différence est la plus grande si votre entrée est déjà un entier.

J'aimerais savoir pourquoi cependant.


Mise à jour: j'ai relancé les tests, cette fois avec coercition (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   |
|-----------------|------------|-----------|-----------|

Addendum: Je viens de rencontrer un comportement légèrement inattendu dont vous devez être conscient lorsque vous choisissez l'une de ces méthodes:

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

Testé avec PHP 5.3.1

nickf
la source
9
Cela a probablement quelque chose à voir avec le fait que intval () appelle un appel de fonction, tandis que le transtypage est géré directement dans le calculateur d'expression de l'interpréteur. C'est peut-être aussi la raison pour laquelle une coercition est encore plus rapide.
staticsan
10
Votre exemple de contrainte peut être encore simplifié en utilisant l'opérateur unary plus peu connu de php. $ x + 0 -> + $ x
Ozzy
@Ozzy C'est génial. Merci pour le conseil! +"15" == 15
caiosm1005
1
@John puisqu'il teste seulement deux cas dans ce premier code, (int)et intval, et dans chaque paire, donne un% sur intval, le cas de base doit être (int). Mais vous avez raison de dire qu'il aurait été plus clair s'il l'avait dit explicitement, surtout depuis qu'il a ajouté plus tard un troisième cas!
ToolmakerSteve
2
Y a-t-il des changements dans ces résultats dans les nouvelles versions de PHP?
Artyom
34

Personnellement, je pense que le casting est le plus joli.

$iSomeVar = (int) $sSomeOtherVar;

Si une chaîne comme 'Hello' est envoyée, elle sera convertie en entier 0. Pour une chaîne telle que '22 ans ', elle sera convertie en entier 22. Tout ce qu'elle ne peut pas analyser en nombre devient 0.

Si vous avez vraiment BESOIN de la vitesse, je suppose que les autres suggestions ici sont correctes en supposant que la contrainte est la plus rapide.

Rexxars
la source
7
Fait intéressant, les tableaux sont convertis en 1. Allez comprendre.
nickf
2
@nickf Not so - Il peut également être converti en 0. Il convertit sa valeur booléenne (true | false) en un entier - 'false' = 0, 'true' = 1. Un tableau est faux s'il est à 100% vide, et il est vrai s'il contient des données, même s'il est juste vide des chaînes ou des valeurs NULL. Si vous deviez convertir un tableau vide en un entier, il deviendrait 0. (Oui, je suis conscient de ce vieux!)
Super Cat
15

Exécutez un test.

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

Il s'agissait d'un test qui a exécuté chaque scénario 10 000 000 fois. :-)

La coercition est 0 + "123"

Le casting est (integer)"123"

Je pense que Co-ercion est un tout petit peu plus rapide. Oh, et essayer 0 + array('123')est une erreur fatale en PHP. Vous voudrez peut-être que votre code vérifie le type de la valeur fournie.

Mon code de test est ci-dessous.


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";
        }
    }
}
staticien
la source
J'ai ajouté settypeà ce test et l' ai exécuté en utilisant PHP 7. Le cast est ressorti légèrement devant et une amélioration des performances par-dessus tout: contrainte de chaîne: 1.9255340099335 conversion de chaîne: 1.5142338275909 type de chaîne: 4.149735212326 échec de la chaîne contrainte: 1.2346560955048 échec de la chaîne cast: 1.3967711925507 échec de la chaîne settype: 4.149735212326
bstoney
9

Vous pouvez simplement convertir une chaîne longue en entier en utilisant FLOAT

$float = (float)$num;

Ou si vous voulez un entier non flottant, alors allez avec

$float = (int)$num;

Par exemple.

(int)   "1212.3"   = 1212 
(float) "1212.3"   = 1212.3
Nishchit Dhanani
la source
1
Hein? Si vous voulez un int, pourquoi ne l'utiliseriez-vous pas (int)? Il peut être vrai que si la chaîne contient un entier, cela (float)renverra une valeur qui agit beaucoup comme un entier (même si son type interne est probablement float), mais pourquoi feriez-vous cela, si la spécification doit renvoyer une valeur entière ? Supposons que la chaîne entrante soit "1,3"? Vous n'obtiendrez pas un entier. Aussi, pour le plaisir de quiconque lira le code à l'avenir, vous devriez dire ce que vous voulez dire. Si vous voulez dire "ce devrait être un entier", alors dites (int)non (float).
ToolmakerSteve
7
$int = settype("100", "integer"); //convert the numeric string to int
Elric Wamugu
la source
3
Je crois qu'une référence ou une preuve s'impose!
Mehran
$ int serait en fait un booléen ici, si l'instruction fonctionnait, mais ce ne serait pas le cas puisque le premier paramètre de settype () est passé par référence et doit donc être un var.
File d'attente
7

excréter un entier à partir d'une chaîne

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

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

// $ out = '1231233';

Développeur
la source
Vous êtes le meilleur du meilleur du meilleur! J'ai passé des heures à convertir des var de la chaîne de données json en entier, seule votre méthode a aidé! Merci!
Kamnibula
4

Plus de résultats de référence 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
la source
Il serait préférable d'écrire l'instruction testée 10 fois dans chaque boucle, afin que le temps ne soit pas dominé par la surcharge de la boucle. Par exemple { $i = +"-11"; $i = +"-11"; $i= +"-11"; $i= +"-11"; ... }. Il est également risqué d'utiliser "-11"directement la valeur littérale , sauf si vous êtes certain que la langue ne fera pas une partie du travail au moment de la compilation. Probablement OK pour un langage dynamique comme PHP, mais je mentionne pour référence future si je teste des formules dans d'autres langages. Plus sûr de définir une variable $x = "-11"avant la boucle de test, puis de l'utiliser. Pour que le code interne soit $i =+$x.
ToolmakerSteve
4

Exécuter une référence, et il s'avère que le moyen le plus rapide d'obtenir un entier réel (en utilisant toutes les méthodes disponibles) est

$foo = (int)+"12.345";

Juste en utilisant

$foo = +"12.345";

renvoie un flotteur.

Andrew Plank
la source
2
C'est plus rapide que simplement (int)"12.345"? Plus rapide de quel pourcentage?
ToolmakerSteve