Qual é o teste mais eficiente para determinar se uma string PHP termina com outra string?

Respostas:

152

O que Assaf disse está correto. Existe uma função integrada no PHP para fazer exatamente isso.

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

Se $testfor mais longo do que o $strPHP emitirá um aviso, você deve verificar isso primeiro.

function endswith($string, $test) {
    $strlen = strlen($string);
    $testlen = strlen($test);
    if ($testlen > $strlen) return false;
    return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0;
}
Mccrumley
fonte
Agradável. Parece que comparar no local seria mais rápido que substr (), como Assaf apontou.
21330 Jason Cohen
2
A resposta de mcrumley é legal, mas deve usar '===' em vez de '=='. '===' é mais rigoroso e geralmente faz o que você deseja, enquanto '==' pode levar a surpresas desagradáveis. O terceiro trecho de código de mcrumley está correto, mas os dois primeiros não. substr_compare () retorna false em alguns casos de erro. No PHP, false == 0, para que os trechos de código sinalizem que a sequência foi encontrada. Com ===, isso não acontece.
Jcsahnwaldt Restabelecer Monica 3/11/09
1
Hoje eu encontrei um bug que quebrará a solução que você forneceu a partir do PHP 5.5.11. bugs.php.net/bug.php?id=67043
user2180613
A sobrecarga de três chamadas de função não o torna mais rápido.
Thanh Trung
66

Esse método é um pouco mais caro em termos de memória, mas é mais rápido:

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

É melhor quando você sabe exatamente o que é a agulha, para codificá-la invertida. Se você inverter a agulha programaticamente, ela se tornará mais lenta que o método anterior.

jscheel
fonte
2
-1 Duvido seriamente que isso seja mais rápido e complicado (legal, mas geralmente não é útil). Se o palheiro não terminar com a agulha, stripositerará toda a corda no pior caso, enquanto substr_comparecomparará no máximo o comprimento da agulha. Sim, substr_comparerequer o cálculo do comprimento do palheiro (e uma agulha muito menor), mas esse método exige isso e a cópia completa, e possivelmente a conversão de toda a coisa em minúscula para a inicialização.
David Harkness
abordagem legal, mas ineficiente (como mencionado na própria resposta) em muitos casos! Ainda é um voto positivo por ser estranhamente criativo e demonstrar que sempre pode haver mais maneiras de fazer a mesma coisa que você pode imaginar. Felicidades!
Fr0zenFyr
1
Testei e encontrei esse método mais rapidamente do que a resposta aceita. Mesmo que o palheiro e a agulha sejam revertidos em tempo real, é mais rápido.
Shumoapp 06/07/19
@DavidHarkness Você testou para ver se suas dúvidas eram justificadas?
Nathan
@ Nathan Não, não vale a pena compará-lo, pois substr_compare()funciona muito bem.
David Harkness
61
$endsWith = substr_compare( $str, $test, -strlen( $test ) ) === 0

O deslocamento negativo "começa a contar a partir do final da string".

Alexander Yanovets
fonte
3
OMI é um dos melhores das soluções fornecidas
Roman Podlinov 22/09/16
+200 se eu puder. O principal insight aqui é que usar um comprimento negativo obtém a parte final da string. Você também pode usar substr com o comprimento -ve e fazer um == contra $ test. As outras respostas são ruins.
Nick
11

Aqui está uma maneira simples de verificar se uma string termina com outra, fornecendo strposum deslocamento exatamente onde a string deve ser encontrada:

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

Simples, e acho que isso funcionará no PHP 4.

Patrick Smith
fonte
8

Depende de qual tipo de eficiência você se importa.

Sua versão usa mais memória devido à cópia extra do uso de substr.

Uma versão alternativa pode pesquisar a sequência original pela última ocorrência da substring sem fazer uma cópia, mas provavelmente seria mais lenta devido a mais testes.

Provavelmente, a maneira mais eficiente é fazer loop char a char da posição -sterlen (test) até o final da string e comparar. Essa é a quantidade mínima de comparações que você pode esperar fazer e quase não há memória extra usada.

Assaf Lavie
fonte
5

Outra maneira seria usar a strrposfunção :

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

Mas isso não é mais rápido.

quiabo
fonte
3

Espero que a resposta abaixo seja eficiente e também simples:

$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
fonte
2

Não sei se isso é rápido ou não, mas para um teste de um único caractere, eles também funcionam:

(array_pop(str_split($string)) === $test) ? true : false;
($string[strlen($string)-1] === $test) ? true : false;
(strrev($string)[0] === $test) ? true : false;
wloske
fonte
1

maneira mais fácil de verificá-lo através de expressões regulares

por exemplo, para verificar se o email fornecido é gmail:

echo (preg_match("/@gmail\.com$/","[email protected]"))?'true':'false';
Biskrem Muhammad
fonte
0

Eu estou pensando que as funções reversas como strrchr () ajudariam você a combinar o final da string o mais rápido.

tkotitan
fonte
0

Isso é puro PHP, sem chamar funções externas, exceto 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
fonte
0

para agulha single-char:

if (@strrev($haystack)[0] == $needle) {
   // yes, it ends...
}
kabantejay
fonte