Jaki jest najbardziej skuteczny test sprawdzający, czy ciąg PHP kończy się innym ciągiem?

Odpowiedzi:

152

To, co powiedział Assaf, jest poprawne. W PHP jest wbudowana funkcja, która dokładnie to robi.

substr_compare($str, $test, strlen($str)-strlen($test), strlen($test)) === 0;

Jeśli $testjest dłuższy niż $strPHP, wyświetli ostrzeżenie, więc musisz najpierw to sprawdzić.

function endswith($string, $test) {
    $strlen = strlen($string);
    $testlen = strlen($test);
    if ($testlen > $strlen) return false;
    return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0;
}
mcrumley
źródło
Miły. Wydaje się, że porównywanie w miejscu byłoby szybsze niż substr (), jak zauważył Assaf.
Jason Cohen
2
Odpowiedź mcrumleya jest fajna, ale powinna zawierać „===” zamiast „==”. „===” jest bardziej rygorystyczne i zwykle robi to, co chcesz, podczas gdy „==” może prowadzić do przykrych niespodzianek. Trzeci fragment kodu mcrumley jest poprawny, ale pierwsze dwa nie. substr_compare () zwraca false w niektórych przypadkach błędów. W PHP false == 0, więc fragmenty kodu sygnalizują, że ciąg został znaleziony. Z === to się nie dzieje.
jcsahnwaldt Przywróć Monikę
1
Dzisiaj napotkałem błąd, który zepsuje rozwiązanie, które podałeś w PHP 5.5.11 i nowszych. bugs.php.net/bug.php?id=67043
user2180613
3 wywołania funkcji nie sprawiają, że jest to najszybsze.
Thanh Trung
66

Ta metoda jest trochę droższa w pamięci, ale jest szybsza:

stripos(strrev($haystack), $reversed_needle) === 0;

Najlepiej, gdy wiesz dokładnie, czym jest igła, więc możesz ją na stałe odwrócić. Jeśli programowo odwrócisz igłę, stanie się wolniejsza niż poprzednia metoda.

jscheel
źródło
2
-1 Naprawdę wątpię, żeby to było szybsze i trudne (fajne, ale zwykle nie jest pomocne). Jeśli stóg siana nie kończy się igłą, striposw najgorszym przypadku powtórzy cały sznurek, podczas gdy substr_compareporównuje co najwyżej długość igły. Tak, substr_comparewymaga obliczenia długości stogu siana (i znacznie mniejszej igły), ale ta metoda wymaga tego i skopiowania go w całości i ewentualnie zamiany całości na małe litery do rozruchu.
David Harkness
fajne podejście, ale nieefektywne (jak wspomniano w samej odpowiedzi) w wielu przypadkach! Wciąż pozytywnie oceniam bycie dziwnie kreatywnym i pokazanie, że zawsze może być więcej sposobów na zrobienie tego samego, niż możesz sobie wyobrazić. Twoje zdrowie!
Fr0zenFyr
1
Przetestowałem i znalazłem tę metodę szybciej niż zaakceptowana odpowiedź. Nawet jeśli zarówno stóg siana, jak i igła są odwracane w locie, jest to szybsze.
Shumoapp,
@DavidHarkness Czy przeprowadziłeś testy, aby sprawdzić, czy Twoje wątpliwości były uzasadnione?
Nathan
@Nathan Nie, nie warto poświęcać czasu na testy porównawcze, ponieważ substr_compare()działa dobrze.
David Harkness
61
$endsWith = substr_compare( $str, $test, -strlen( $test ) ) === 0

Ujemne przesunięcie „zaczyna liczyć od końca łańcucha”.

Alexander Yanovets
źródło
3
IMO to jedno z najlepszych spośród dostarczonych rozwiązań
Roman Podlinov
+200 gdybym mógł. Kluczową spostrzeżeniem jest to, że użycie ujemnej długości powoduje uzyskanie końcowej części ciągu. Możesz także użyć substr z -ve length i wykonać == względem $ test. Pozostałe odpowiedzi są słabe.
Nick
11

Oto prosty sposób sprawdzenia, czy jeden ciąg kończy się drugim, poprzez podanie strposprzesunięcia w miejscu, w którym powinien się znaleźć ciąg:

function stringEndsWith($whole, $end)
{
    return (strpos($whole, $end, strlen($whole) - strlen($end)) !== false);
}

Proste i myślę, że to zadziała w PHP 4.

Patrick Smith
źródło
8

To zależy od tego, na jakiej wydajności Ci zależy.

Twoja wersja zajmuje więcej pamięci z powodu dodatkowej kopii z użycia substr.

Alternatywna wersja mogłaby przeszukiwać oryginalny ciąg pod kątem ostatniego wystąpienia podciągu bez wykonywania kopii, ale prawdopodobnie byłaby wolniejsza z powodu większej liczby testów.

Prawdopodobnie najbardziej wydajnym sposobem jest wykonanie pętli znak po znaku od pozycji -sterlen (test) do końca łańcucha i porównanie. To minimalna liczba porównań, na które możesz liczyć, i nie ma prawie żadnej dodatkowej pamięci.

Assaf Lavie
źródło
5

Innym sposobem byłoby użycie strrposfunkcji :

strrpos($str, $test) == strlen($str) - strlen($test)

Ale to nie jest szybsze.

Gumbo
źródło
3

Mam nadzieję, że poniższa odpowiedź okaże się skuteczna i jednocześnie prosta:

$content = "The main string to search";
$search = "search";
//For compare the begining string with case insensitive. 
if(stripos($content, $search) === 0) echo 'Yes';
else echo 'No';

//For compare the begining string with case sensitive. 
if(strpos($content, $search) === 0) echo 'Yes';
else echo 'No';

//For compare the ending string with case insensitive. 
if(stripos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';

//For compare the ending string with case sensitive. 
if(strpos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';
Srinivasan.S
źródło
2

Nie wiem, czy jest to szybkie, czy nie, ale w przypadku testu jednej postaci te również działają:

(array_pop(str_split($string)) === $test) ? true : false;
($string[strlen($string)-1] === $test) ? true : false;
(strrev($string)[0] === $test) ? true : false;
wloske
źródło
1

najłatwiej to sprawdzić za pomocą wyrażenia regularnego

na przykład, aby sprawdzić, czy podana poczta to gmail:

echo (preg_match("/@gmail\.com$/","[email protected]"))?'true':'false';
Biskrem Muhammad
źródło
0

Myślę, że funkcje odwrotne, takie jak strrchr (), pomogłyby w najszybszym dopasowaniu końca ciągu.

tkotitan
źródło
0

To jest czysty PHP, bez wywoływania funkcji zewnętrznych, z wyjątkiem strlen .

function endsWith ($ends, $string)
{
    $strLength = strlen ($string);
    $endsLength = strlen ($ends);
    for ($i = 0; $i < $endsLength; $i++)
    {
        if ($string [$strLength - $i - 1] !== $ends [$i])
            return false;
    }
    return true;
}
Xesau
źródło
0

dla igły jednoznakowej:

if (@strrev($haystack)[0] == $needle) {
   // yes, it ends...
}
kabantejay
źródło