Najszybszy sposób na konwersję łańcucha na liczbę całkowitą w PHP

251

Za pomocą PHP jaki jest najszybszy sposób na konwersję takiego ciągu: "123"na liczbę całkowitą?

Dlaczego ta metoda jest najszybsza? Co się stanie, jeśli otrzyma nieoczekiwane dane wejściowe, takie jak "hello"tablica?

pseudonim
źródło
12
cóż, jeśli to nie boli (czytelność), dlaczego nie robić rzeczy w najbardziej efektywny możliwy sposób?
nickf
19
Jeśli nie zaszkodzi to prędkości, dlaczego nie robić rzeczy w możliwie najbardziej czytelny sposób?
Andy Lester
4
@Andy, spójrz na testy porównawcze poniżej. Różnica między (int)i intval()może wynosić ponad 400%!
nickf
6
najszybszy ma znaczenie, ponieważ szybkość ma znaczenie dla wygody użytkownika. Kiedy prowadzisz wiele operacji, chcesz mieć je SZYBKO!
philipp
13
bez kopania martwego konia powiedziałbym również, że kwestia prędkości w porównaniu z czytelnością jest w tym przypadku nieistotna, ponieważ pytanie zostało oznaczone jako zoptymalizowane. Powód, dla którego chcę prędkości w pytaniu oznaczonym jako optymalizacja, jest oczywisty.
totallyNotLizards

Odpowiedzi:

371

Właśnie skonfigurowałem szybkie ćwiczenie porównawcze:

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

Wywołanie intval () jest średnio dwa i pół raza wolniejsze, a różnica jest największa, jeśli dane wejściowe są już liczbą całkowitą.

Chciałbym wiedzieć, dlaczego .


Aktualizacja: Ponownie uruchomiłem testy, tym razem z przymusem (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   |
|-----------------|------------|-----------|-----------|

Dodatek: Właśnie natknąłem się na nieco nieoczekiwane zachowanie, o którym powinieneś wiedzieć wybierając jedną z tych metod:

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

Testowane przy użyciu PHP 5.3.1

pseudonim
źródło
9
Prawdopodobnie ma to coś wspólnego z faktem, że intval () wywołuje wywołanie funkcji, podczas gdy rzutowanie jest obsługiwane bezpośrednio w kalkulatorze wyrażeń interpretera. Może to być również przyczyną, dla której koercja jest jeszcze szybsza.
staticsan
10
Twój przykład przymusu można dodatkowo uprościć, używając mało znanego operatora Unary Plus. $ x + 0 -> + $ x
Ozzy
@Ozzy To niesamowite. Dzięki za wskazówkę! +"15" == 15
caiosm1005
1
@John, ponieważ testuje tylko dwa przypadki w tym pierwszym kodzie, (int)i intval, w każdej parze, podaje% intval, podstawową sprawą musi być (int). Ale masz rację, że byłoby wyraźniej, gdyby powiedział to wprost, zwłaszcza, że ​​później dodał trzeci przypadek!
ToolmakerSteve,
2
Czy są jakieś zmiany w tych wynikach w nowszych wersjach PHP?
Artyom
34

Osobiście uważam, że casting jest najładniejszy.

$iSomeVar = (int) $sSomeOtherVar;

Jeśli zostanie wysłany ciąg typu „Witaj”, zostanie on rzutowany na liczbę całkowitą 0. W przypadku ciągu takiego jak „22 lata” zostanie on rzutowany na liczbę całkowitą 22. Wszystko, czego nie może parsować na liczbę, ma wartość 0.

Jeśli naprawdę POTRZEBUJESZ prędkości, myślę, że pozostałe sugestie są poprawne, zakładając, że przymus jest najszybszy.

Rexxars
źródło
7
co ciekawe, tablice są rzutowane na 1. przejdź do figury.
nickf
2
@nickf Nie tak - można go również ustawić na 0. Rzuca swoją wartość logiczną (prawda | fałsz) na liczbę całkowitą - „fałsz” = 0, „prawda” = 1. Tablica jest fałszem, jeśli jest w 100% pusta, i jest prawdziwa, jeśli zawiera DOWOLNE dane, nawet jeśli jest po prostu pusta ciągi lub wartości NULL. Gdyby rzucić pustą tablicę na liczbę całkowitą, stałaby się ona 0. (Tak, wiem, że to stare!)
Super Cat
15

Uruchom test.

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

To był test, który przeprowadził każdy scenariusz 10 000 000 razy. :-)

Koercja jest 0 + "123"

Casting jest (integer)"123"

Myślę, że Co-ercion jest trochę szybszy. Aha, a próba 0 + array('123')jest krytycznym błędem w PHP. Możesz chcieć, aby Twój kod sprawdzał typ podanej wartości.

Mój kod testowy znajduje się poniżej.


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
źródło
Dodałem settypedo tego testu i uruchomiłem go przy użyciu PHP 7. Rzutowanie wyszło nieco z przodu i duża poprawa wydajności w stosunku do wszystkich: przymus ciągów: 1,9255340099335 ciąg rzutów: 1,5142338275909 typ ciągów: 4.149735212326 przymus ciągów niepowodzeń: 1.2346560955048 ciąg niepowodzeń rzutów: 1,3967711925507 porażka ciągów settype: 4.149735212326
bstoney
9

Możesz po prostu przekonwertować długi ciąg znaków na liczbę całkowitą za pomocą FLOAT

$float = (float)$num;

Lub jeśli chcesz, żeby liczba całkowita nie była zmienną, to idź z

$float = (int)$num;

Np.

(int)   "1212.3"   = 1212 
(float) "1212.3"   = 1212.3
Nishchit Dhanani
źródło
1
Co? Jeśli chcesz int, dlaczego nie używałbyś (int)? Może być prawdą, że jeśli łańcuch zawiera liczbę całkowitą, to (float)zwróci wartość, która działa podobnie jak liczba całkowita (nawet jeśli jego wewnętrzny typ jest prawdopodobnie float), ale dlaczego miałbyś to zrobić, jeśli specyfikacja ma zwrócić wartość całkowitą ? Załóżmy, że ciąg przychodzący to „1.3”? Nie dostaniesz liczby całkowitej. Również ze względu na każdego, kto czyta kod w przyszłości, powinieneś powiedzieć, co masz na myśli. Jeśli masz na myśli „powinna to być liczba całkowita”, powiedz (int)nie (float).
ToolmakerSteve
7
$int = settype("100", "integer"); //convert the numeric string to int
Elric Wamugu
źródło
3
Wierzę, że jakieś referencje lub dowody są w porządku!
Mehran
$ int byłby w rzeczywistości wartością logiczną, gdyby instrukcja działała, ale nie zrobiłby tego, ponieważ pierwszy parametr settype () jest przekazywany przez referencję, a więc musi być var.
Kolejka
7

liczba całkowita wyciąg z dowolnego ciągu

$ in = „tel.123-12-33”;

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

// $ out = '1231233';

Deweloper
źródło
Jesteś najlepszy z najlepszych z najlepszych! Spędziłem godziny, aby przekonwertować niektóre zmienne z ciągu danych JSON na liczby całkowite, pomogła tylko Twoja metoda! Dziękuję Ci!
Kamnibula,
4

Więcej wyników testu 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
źródło
Lepiej byłoby napisać testowaną instrukcję 10x w każdej pętli, aby czas nie był zdominowany przez narzut pętli. Np { $i = +"-11"; $i = +"-11"; $i= +"-11"; $i= +"-11"; ... }. Ryzykowne jest także "-11"bezpośrednie użycie wartości dosłownej , chyba że masz pewność, że język nie wykona części pracy w czasie kompilacji. Prawdopodobnie OK dla dynamicznego języka takiego jak PHP, ale wspominam o tym w przyszłości, jeśli testujemy formuły w innych językach. Bezpieczniej jest ustawić zmienną $x = "-11"przed pętlą testową, a następnie użyć jej. Tak więc wewnętrzny kod jest $i =+$x.
ToolmakerSteve
4

Przeprowadziłem test porównawczy i okazuje się, że najszybszym sposobem uzyskania prawdziwej liczby całkowitej (przy użyciu wszystkich dostępnych metod) jest

$foo = (int)+"12.345";

Po prostu używam

$foo = +"12.345";

zwraca liczbę zmiennoprzecinkową.

Andrew Plank
źródło
2
To jest szybsze niż po prostu (int)"12.345"? Szybciej o jaki procent?
ToolmakerSteve