¿Cuál es la prueba más eficiente de si una cadena PHP termina con otra cadena?

Respuestas:

152

Lo que Assaf dijo es correcto. Hay una función integrada en PHP para hacer exactamente eso.

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

Si $testes más largo que $strPHP dará una advertencia, por lo que primero debe verificarlo.

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
fuente
Agradable. Parece que comparar in situ sería más rápido que substr (), como señaló Assaf.
Jason Cohen
2
La respuesta de mcrumley es genial, pero debería usar '===' en lugar de '=='. '===' es más estricto y generalmente hace lo que quieres, mientras que '==' puede generar sorpresas desagradables. El tercer fragmento de código de mcrumley es correcto, pero los dos primeros no lo son. substr_compare () devuelve falso en algunos casos de error. En PHP, falso == 0, por lo que los fragmentos de código indicarían que se ha encontrado la cadena. Con ===, esto no sucede.
jcsahnwaldt Restablece a Monica el
1
Me encontré con un error hoy que romperá la solución que ha dado desde PHP 5.5.11 y en adelante. bugs.php.net/bug.php?id=67043
user2180613
La sobrecarga de 3 llamadas de función no lo hace más rápido.
Thanh Trung
66

Este método es un poco más costoso para la memoria, pero es más rápido:

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

Esto es mejor cuando sabes exactamente qué es la aguja, para que puedas codificarla al revés. Si invierte la aguja mediante programación, se vuelve más lenta que el método anterior.

jscheel
fuente
2
-1 Dudo seriamente que esto sea más rápido y que sea complicado (genial, pero no suele ser útil). Si el pajar no termina con la aguja, stripositerará toda la cuerda en el peor de los casos, mientras substr_compareque comparará como máximo la longitud de la aguja. Sí, substr_comparerequiere calcular la longitud del pajar (y una aguja mucho más pequeña), pero este método requiere eso y copiarlo por completo, y posiblemente convertir todo en minúsculas para arrancar.
David Harkness
enfoque genial pero ineficiente (como se menciona en la respuesta en sí) en muchos casos. Sigue siendo un voto positivo por ser extrañamente creativo y demostrar que siempre puede haber más formas de hacer lo mismo que puedas imaginar. ¡Salud!
Fr0zenFyr
1
Probé y encontré este método más rápido que la respuesta aceptada. Incluso si tanto el pajar como la aguja se invierten sobre la marcha, es más rápido.
Shumoapp
@DavidHarkness ¿Has probado para ver si tus dudas estaban justificadas?
Nathan
@Nathan No, no vale la pena el tiempo de referencia ya que substr_compare()funciona muy bien.
David Harkness
61
$endsWith = substr_compare( $str, $test, -strlen( $test ) ) === 0

El desplazamiento negativo "comienza a contar desde el final de la cadena".

Alexander Yanovets
fuente
3
OMI es una de las mejores soluciones proporcionadas
Roman Podlinov
+200 si pudiera. La idea clave aquí es que el uso de una longitud negativa obtiene la parte final de la cadena. También puede usar substr con la longitud -ve y hacer un == contra $ test. Las otras respuestas son pobres.
Nick
11

Aquí hay una manera simple de verificar si una cadena termina con otra, dando strposun desplazamiento justo donde la cadena debe encontrarse:

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

Directo, y creo que esto funcionará en PHP 4.

Patrick Smith
fuente
8

Depende de qué tipo de eficiencia le interese.

Su versión usa más memoria debido a la copia adicional del uso de substr.

Una versión alternativa podría buscar en la cadena original la última aparición de la subcadena sin hacer una copia, pero probablemente sería más lenta debido a más pruebas.

Probablemente la forma más eficiente es hacer un bucle char-by-char desde la posición -sterlen (prueba) hasta el final de la cadena y comparar. Esa es la cantidad mínima de comparaciones que puede esperar hacer y apenas se usa memoria adicional.

Assaf Lavie
fuente
5

Otra forma sería usar la strrposfunción :

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

Pero eso no es más rápido.

Gumbo
fuente
3

Espero que la respuesta a continuación sea eficiente y también simple:

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

No sé si esto es rápido o no, pero para una prueba de un solo personaje, estos también funcionan:

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

La forma más fácil de verificarlo mediante expresiones regulares

por ejemplo para verificar si el correo proporcionado es gmail:

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

Estoy pensando que las funciones inversas como strrchr () te ayudarían a hacer coincidir el final de la cadena más rápido.

tkotitan
fuente
0

Esto es PHP puro, sin invocar funciones externas, excepto 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
fuente
0

para aguja de un solo carbón:

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