Las funciones beginWith () y endsWith () en PHP

1483

¿Cómo puedo escribir dos funciones que tomarían una cadena y regresarían si comienza con el carácter / cadena especificado o termina con él?

Por ejemplo:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Haga clic en Upvote
fuente
19
Vea la clase Str de Laravel comienza con () y termina con () para métodos bien probados . Se han encontrado casos extremos, por lo que el uso generalizado de este código es una ventaja.
Gras Double
1
Puede encontrar s($str)->startsWith('|') y s($str)->endsWith('}')útil, como se encuentra en esta biblioteca independiente .
graznar
3
Advertencia: la mayoría de las respuestas aquí no son confiables en codificaciones de varios bytes como UTF-8.
Álvaro González
Siguiendo mi comentario anterior, puede asegurarse de usar la última versión (a partir de hoy, 5.4 ). En particular, beginWith () ha sido optimizado para cadenas grandes de pajar.
Gras Double

Respuestas:

1615
function startsWith($haystack, $needle)
{
     $length = strlen($needle);
     return (substr($haystack, 0, $length) === $needle);
}

function endsWith($haystack, $needle)
{
    $length = strlen($needle);
    if ($length == 0) {
        return true;
    }

    return (substr($haystack, -$length) === $needle);
}

Use esto si no quiere usar una expresión regular.

MrHus
fuente
16
+1 Esto es más limpio que la respuesta aceptada. Además, $lengthno es necesario en la última línea de la endsWith().
demasiado php
13
Yo diría que endsWith ('foo', '') == false es el comportamiento correcto. Porque foo no termina con nada. 'Foo' termina con 'o', 'oo' y 'Foo'.
MrHus
126
EndsWith se puede escribir mucho más corto:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj
12
Puede evitar el ifcompleto haciendo pasar $lengthcomo tercer parámetro a substr: return (substr($haystack, -$length, $length);. Esto maneja el caso $length == 0devolviendo una cadena vacía y no la totalidad $haystack.
mxxk
20
@MrHus Recomendaría usar funciones seguras de varios bytes, por ejemplo mb_strlen y mb_substr
19Gerhard85
1025

Puede usar la substr_comparefunción para verificar el inicio y el final con:

function startsWith($haystack, $needle) {
    return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
function endsWith($haystack, $needle) {
    return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}

Esta debería ser una de las soluciones más rápidas en PHP 7 ( script de referencia ). Probado contra pajar de 8KB, agujas de diferentes longitudes y casos completos, parciales y sin coincidencia. strncmpes un toque más rápido para comenzar, pero no puede verificar termina con.

Salman A
fuente
74
¡Esta respuesta llegó al Daily WTF! : D Ver thedailywtf.com/articles/…
Wim ten Brink
Tenga en cuenta que los comentarios @DavidWallace y @FrancescoMM se aplican a una versión anterior de esta respuesta. La respuesta actual usa strrposcuál (debería) fallar inmediatamente si la aguja no coincide con el comienzo del pajar.
Salman A
2
No lo entiendo Basado en php.net/manual/en/function.strrpos.php : "Si el valor es negativo, la búsqueda comenzará desde esa cantidad de caracteres desde el final de la cadena, buscando hacia atrás". Esto parece indicar que estamos comenzando en el carácter 0 (debido a -strlength($haystack)) y buscando hacia atrás desde allí. ¿Eso no significa que no estás buscando nada? Tampoco entiendo las !== falsepartes de esto. Supongo que esto se basa en una peculiaridad de PHP donde algunos valores son "verdaderos" y otros "falsos", pero ¿cómo funciona eso en este caso?
Welbog
3
@Welbog: por ejemplo haystack = xxxyyyneedle = yyyy el uso de strrposla búsqueda comienza desde el primero x. Ahora no tenemos una coincidencia exitosa aquí (se encuentra x en lugar de y) y no podemos retroceder más (estamos al comienzo de la cadena) la búsqueda falla inmediatamente . Acerca del uso !== false: strrposen el ejemplo anterior devolverá 0 o falso y no otro valor. Del mismo modo, strposen el ejemplo anterior puede devolver $temp(la posición esperada) o falso. Fui con !== falsecoherencia, pero podría usar === 0y === $tempen las funciones respectivamente.
Salman A
8
@spoo ya se ha establecido que strpos === 0 es una solución terrible si el pajar es grande y la aguja no existe.
Salman A
243

Actualizado 23-ago-2016

Las funciones

function substr_startswith($haystack, $needle) {
    return substr($haystack, 0, strlen($needle)) === $needle;
}

function preg_match_startswith($haystack, $needle) {
    return preg_match('~' . preg_quote($needle, '~') . '~A', $haystack) > 0;
}

function substr_compare_startswith($haystack, $needle) {
    return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}

function strpos_startswith($haystack, $needle) {
    return strpos($haystack, $needle) === 0;
}

function strncmp_startswith($haystack, $needle) {
    return strncmp($haystack, $needle, strlen($needle)) === 0;
}

function strncmp_startswith2($haystack, $needle) {
    return $haystack[0] === $needle[0]
        ? strncmp($haystack, $needle, strlen($needle)) === 0
        : false;
}

Pruebas

echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
    if($i % 2500 === 0) echo '.';
    $test_cases[] = [
        random_bytes(random_int(1, 7000)),
        random_bytes(random_int(1, 3000)),
    ];
}
echo "done!\n";


$functions = ['substr_startswith', 'preg_match_startswith', 'substr_compare_startswith', 'strpos_startswith', 'strncmp_startswith', 'strncmp_startswith2'];
$results = [];

foreach($functions as $func) {
    $start = microtime(true);
    foreach($test_cases as $tc) {
        $func(...$tc);
    }
    $results[$func] = (microtime(true) - $start) * 1000;
}

asort($results);

foreach($results as $func => $time) {
    echo "$func: " . number_format($time, 1) . " ms\n";
}

Resultados (PHP 7.0.9)

(Ordenado más rápido a más lento)

strncmp_startswith2: 40.2 ms
strncmp_startswith: 42.9 ms
substr_compare_startswith: 44.5 ms
substr_startswith: 48.4 ms
strpos_startswith: 138.7 ms
preg_match_startswith: 13,152.4 ms

Resultados (PHP 5.3.29)

(Ordenado más rápido a más lento)

strncmp_startswith2: 477.9 ms
strpos_startswith: 522.1 ms
strncmp_startswith: 617.1 ms
substr_compare_startswith: 706.7 ms
substr_startswith: 756.8 ms
preg_match_startswith: 10,200.0 ms

comienza con_benchmark.php

mpen
fuente
3
Si las cadenas no están vacías, como en sus pruebas, esto es en realidad de alguna manera (20-30%) más rápido: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}agregué una respuesta a continuación.
FrancescoMM
3
@Jronny Porque 110 es menos de 133 ... ??
mpen
2
Maldición, no sé lo que se me ocurrió esa vez. Prolly la falta de sueño.
Jronny
1
@mpen, no noté el elefante en absoluto :(
Visman
1
Estas pruebas no son buenas para probar el rendimiento. Lo que estás haciendo es usar una cuerda aleatoria como aguja. En el 99.99% de los casos NO habrá coincidencias. La mayoría de las funciones saldrán después de hacer coincidir el primer byte. ¿Qué pasa con los casos en que se encuentra una coincidencia? ¿Qué función lleva menos tiempo para concluir una coincidencia exitosa? ¿Qué pasa con los casos en que el 99% de la aguja coincide pero no los últimos bytes? ¿Qué función lleva menos tiempo para concluir que no hay coincidencia?
Salman A
137

Todas las respuestas parecen tan lejos para hacer un montón de trabajo innecesario, strlen calculations, string allocations (substr), etc. 'strpos'y 'stripos'funciones devuelven el índice de la primera aparición de $needleen $haystack:

function startsWith($haystack,$needle,$case=true)
{
    if ($case)
        return strpos($haystack, $needle, 0) === 0;

    return stripos($haystack, $needle, 0) === 0;
}

function endsWith($haystack,$needle,$case=true)
{
    $expectedPosition = strlen($haystack) - strlen($needle);

    if ($case)
        return strrpos($haystack, $needle, 0) === $expectedPosition;

    return strripos($haystack, $needle, 0) === $expectedPosition;
}
Sander Rijken
fuente
2
endsWith()La función tiene un error. Su primera línea debería ser (sin el -1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma
66
Lo strlen () no es innecesario. En caso de que la cadena no comience con la aguja dada, entonces su código escaneará innecesariamente todo el pajar.
AppleGrew
55
@Mark sí, verificar solo el comienzo es MUCHO más rápido, especialmente si está haciendo algo como verificar los tipos MIME (o en cualquier otro lugar donde la cadena sea grande)
chacham15
2
@mark Hice algunos puntos de referencia con un pajar de 1000 char y una aguja de 10 u 800 char y strpos fue 30% más rápido Haga sus puntos de referencia antes de afirmar que algo es más rápido o no ...
wdev
77
Debería considerar la posibilidad de citar la aguja como strpos($haystack, "$needle", 0)si hubiera alguna posibilidad de que no sea una cuerda (por ejemplo, si proviene json_decode()). De lo contrario, el comportamiento predeterminado [impar] destrpos() puede causar resultados inesperados: " Si la aguja no es una cadena, se convierte en un entero y se aplica como el valor ordinal de un carácter " .
quietmint
46
function startsWith($haystack, $needle, $case = true) {
    if ($case) {
        return (strcmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
    }
    return (strcasecmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
}

function endsWith($haystack, $needle, $case = true) {
    if ($case) {
        return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
    }
    return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
}

Crédito a :

Compruebe si una cadena termina con otra cadena

Compruebe si una cadena comienza con otra cadena

KdgDev
fuente
1
strtolower no es la mejor manera de realizar funciones que no distingan entre mayúsculas y minúsculas. En algunos entornos locales, la carcasa es más compleja que solo superior e inferior.
Sander Rijken
8
Veo quejas y no hay solución ... Si vas a decir que es malo, entonces también deberías dar un ejemplo de cómo debería ser.
KdgDev
2
@WebDevHobo: por eso mismo agregué una respuesta un día antes de tu comentario. Para su código, strcasecmp fue de hecho lo correcto.
Sander Rijken el
29

El regex funciona arriba, pero con los otros ajustes también sugeridos arriba:

 function startsWith($needle, $haystack) {
     return preg_match('/^' . preg_quote($needle, '/') . '/', $haystack);
 }

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
tridian
fuente
2
en php para operaciones de cadena, el orden de los parámetros es $ haystack, $ needle. Estas funciones son al revés y actúan como funciones de matriz donde el orden es en realidad $ needle, $ haystack.
Andy
29

Esta pregunta ya tiene muchas respuestas, pero en algunos casos puede conformarse con algo más simple que todas. Si la cadena que está buscando es conocida (codificada), puede usar expresiones regulares sin comillas, etc.

Compruebe si una cadena comienza con 'ABC':

preg_match('/^ABC/', $myString); // "^" here means beginning of string

termina con 'ABC':

preg_match('/ABC$/', $myString); // "$" here means end of string

En mi caso simple, quería verificar si una cadena termina con una barra inclinada:

preg_match('#/$#', $myPath);   // Use "#" as delimiter instead of escaping slash

La ventaja: dado que es muy corto y simple, no tiene que definir una función (como endsWith() ) como se muestra arriba.

Pero, una vez más, esta no es una solución para cada caso, solo esta muy específica.

noamtm
fuente
No necesita codificar la cadena. La expresión regular puede ser dinámica.
Ryan
2
@self true, pero si la cadena no está codificada, debe escapar de ella. Actualmente hay 2 respuestas sobre esta pregunta que lo hacen. Esto es fácil, pero complica un poco el código. Entonces, mi punto fue que para casos muy simples, donde es posible la codificación rígida, puede mantenerlo simple.
noamtm
1
Tampoco tiene que escapar de la barra oblicua, puede envolver la expresión regular en algún otro carácter, por ejemplo @, para que la barra ( /) no tenga que escaparse. Vea el Ejemplo # 3 aquí: php.net/manual/en/function.preg-match.php .
cjbarth
Gracias @ cjbarth. Cambié mi respuesta en consecuencia. Por cierto, "#" es el ejemplo dado en php.net/manual/en/regexp.reference.delimiters.php cuando se trata de una barra oblicua.
noamtm
23

Si la velocidad es importante para usted, intente esto (creo que es el método más rápido)

Funciona solo para cadenas y si $ haystack es solo 1 carácter

function startsWithChar($needle, $haystack)
{
   return ($needle[0] === $haystack);
}

function endsWithChar($needle, $haystack)
{
   return ($needle[strlen($needle) - 1] === $haystack);
}

$str='|apples}';
echo startsWithChar($str,'|'); //Returns true
echo endsWithChar($str,'}'); //Returns true
echo startsWithChar($str,'='); //Returns false
echo endsWithChar($str,'#'); //Returns false
lepe
fuente
1
esta es probablemente la respuesta más eficiente porque no usa ninguna función como cadena extra, solo habitual ...
Probablemente debería verificar si la cadena tiene al menos un carácter y tiene los dos parámetros intercambiados
a1an
1
Creativo. Agujas que contienen pajares. Por cierto, hay una disminución fea con: endsWithChar('','x')pero el resultado es correcto
Tino
18

Aquí hay dos funciones que no introducen una cadena temporal, que podrían ser útiles cuando las agujas son sustancialmente grandes:

function startsWith($haystack, $needle)
{
    return strncmp($haystack, $needle, strlen($needle)) === 0;
}

function endsWith($haystack, $needle)
{
    return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
Jack
fuente
2
+1 Funciona desde PHP5.1 y la mejor respuesta de mi humilde opinión. Pero endsWidthdebería hacerlo return $needle==='' || substr_compare(... así que funciona como se esperaba para lo -strlen($needle)===0cual, sin la solución, endsWith('a','')regresafalse
Tino
@Tino Gracias ... Siento que es un error en substr_compare()realidad, así que agregué un RP para arreglar eso :)
Ja͢ck
3
La llamada endsWith('', 'foo')activa una Advertencia: "substr_compare (): La posición de inicio no puede exceder la longitud inicial de la cadena". Tal vez sea otro error substr_compare(), pero para evitarlo, necesita una verificación previa como ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_
@gx_ No es necesario reducir la velocidad con más código. Solo use return $needle === '' || @substr_compare(... para suprimir esta advertencia.
Tino
17

Fin más rápido con la solución ():

# Checks if a string ends in a string
function endsWith($haystack, $needle) {
    return substr($haystack,-strlen($needle))===$needle;
}

Punto de referencia:

# This answer
function endsWith($haystack, $needle) {
    return substr($haystack,-strlen($needle))===$needle;
}

# Accepted answer
function endsWith2($haystack, $needle) {
    $length = strlen($needle);

    return $length === 0 ||
    (substr($haystack, -$length) === $needle);
}

# Second most-voted answer
function endsWith3($haystack, $needle) {
    // search forward starting from end minus needle length characters
    if ($needle === '') {
        return true;
    }
    $diff = \strlen($haystack) - \strlen($needle);
    return $diff >= 0 && strpos($haystack, $needle, $diff) !== false;
}

# Regex answer
function endsWith4($haystack, $needle) {
    return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
}

function timedebug() {
    $test = 10000000;

    $time1 = microtime(true);
    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith('TestShortcode', 'Shortcode');
    }
    $time2 = microtime(true);
    $result1 = $time2 - $time1;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith2('TestShortcode', 'Shortcode');
    }
    $time3 = microtime(true);
    $result2 = $time3 - $time2;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith3('TestShortcode', 'Shortcode');
    }
    $time4 = microtime(true);
    $result3 = $time4 - $time3;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith4('TestShortcode', 'Shortcode');
    }
    $time5 = microtime(true);
    $result4 = $time5 - $time4;

    echo $test.'x endsWith: '.$result1.' seconds # This answer<br>';
    echo $test.'x endsWith2: '.$result4.' seconds # Accepted answer<br>';
    echo $test.'x endsWith3: '.$result2.' seconds # Second most voted answer<br>';
    echo $test.'x endsWith4: '.$result3.' seconds # Regex answer<br>';
    exit;
}
timedebug();

Resultados de referencia:

10000000x endsWith: 1.5760900974274 seconds # This answer
10000000x endsWith2: 3.7102129459381 seconds # Accepted answer
10000000x endsWith3: 1.8731069564819 seconds # Second most voted answer
10000000x endsWith4: 2.1521229743958 seconds # Regex answer
Lucas Bustamante
fuente
3
¡+1 por tomarse el tiempo para comparar diferentes soluciones y realmente compararlas! También debe mencionar qué versión de PHP utilizó, ya que las optimizaciones se realizan a medida que evoluciona el lenguaje. He visto mejoras dramáticas en las funciones de comparación de cadenas de una versión PHP a otra :)
Christophe Deliens
1
haciéndose eco de @ChristopheDeliens y su solicitud de proporcionar la versión de PHP. Ejecuté su prueba en 7.3.2 y obtuve resultados similares FWIW.
Jeff
16

Me doy cuenta de que esto se ha terminado, pero es posible que desee ver strncmp ya que le permite poner la longitud de la cadena para comparar, por lo que:

function startsWith($haystack, $needle, $case=true) {
    if ($case)
        return strncasecmp($haystack, $needle, strlen($needle)) == 0;
    else
        return strncmp($haystack, $needle, strlen($needle)) == 0;
}    
James Black
fuente
¿Cómo terminarías con esto?
mpen
@ Mark: puede ver la respuesta aceptada, pero prefiero usar strncmp principalmente porque creo que es más seguro.
James Black el
Me refiero a strncmp específicamente. No puede especificar un desplazamiento. Eso significaría que la función endsWith tendría que usar un método completamente diferente.
mpen
@ Mark - Para fines Con solo usaría strrpos ( php.net/manual/en/function.strrpos.php ), pero, en general, cada vez que vaya a usar strcmp strncmp es probablemente una opción más segura.
James Black el
11

Puedes usar strposystrrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
Lex
fuente
1
¿Deberías estar usando triples iguales aquí strpos($sHaystack, $sNeedle) == 0como este strpos($sHaystack, $sNeedle) === 0? Veo un error, cuando se false == 0evalúa true.
Kalyan
11

Aquí hay una versión segura de varios bytes de la respuesta aceptada, funciona bien para cadenas UTF-8:

function startsWith($haystack, $needle)
{
    $length = mb_strlen($needle, 'UTF-8');
    return (mb_substr($haystack, 0, $length, 'UTF-8') === $needle);
}

function endsWith($haystack, $needle)
{
    $length = mb_strlen($needle, 'UTF-8');
    return $length === 0 ||
        (mb_substr($haystack, -$length, $length, 'UTF-8') === $needle);
}
Vahid Amiri
fuente
2
Estoy bastante seguro de que esto es solo un desperdicio de CPU. todo lo que necesita verificar, para StarstWith y EndsWith, es simplemente verificar que los bytes coincidan, y eso es exactamente lo que está haciendo la respuesta aceptada. este 1 desperdicia tiempo calculando el número de caracteres utf8 de la aguja, y dónde está la posición del enésimo carácter utf8 del pajar ... creo que, sin estar 100% seguro, esto es solo un desperdicio de CPU. ¿Puedes llegar a un caso de prueba real donde la respuesta aceptada falla, y esto no?
hanshenrik
2
@hanshenrik: por cierto, podría suceder, en el caso muy raro cuando busca una cadena que contiene los mismos bytes que un UTF8 pero falta la mitad del último carácter. Por ejemplo, tienes unicode C5 91 (letra "ő") y buscas C5 (letra "Å"), no debería darte una coincidencia. Por otro lado, claro, ¿por qué buscarías en un pajar utf para una aguja no utf ... Pero para los controles a prueba de balas, esto debe considerarse una posibilidad.
dkellner
En startsWithdebe ser$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen
2
@ThomasKekeisen Gracias, lo arreglé.
Vahid Amiri
8

Líneas cortas y fáciles de entender sin expresiones regulares.

comienza con () es sencillo.

function startsWith($haystack, $needle) {
   return (strpos($haystack, $needle) === 0);
}

endsWith () usa el strrev () ligeramente elegante y lento:

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
Dan
fuente
@FrancescoMM: strpos no es la "herramienta correcta" ... ¿Por qué? ¿Cuáles son las "herramientas adecuadas" entonces? EDITAR: leí tu respuesta a continuación. Pensé que programar es como inventar usando los recursos que tienes ... Entonces, no hay correcto o incorrecto ... solo funciona o no funciona ... el rendimiento es secundario.
Fr0zenFyr
"¿Porque es una herramienta para buscar, no para comparar?" Cit. Aristoteles
FrancescoMM
7

Centrándose en el inicio, si está seguro de que las cadenas no están vacías, agregar una prueba en el primer carácter, antes de la comparación, el strlen, etc., acelera un poco las cosas:

function startswith5b($haystack, $needle) {
    return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;
}

Es de alguna manera (20% -30%) más rápido. Agregar otra prueba de caracteres, como $ haystack {1} === $ needle {1} no parece acelerar mucho las cosas, incluso puede disminuir la velocidad.

===parece más rápido que el == operador condicional (a)?b:cparece más rápido queif(a) b; else c;


Para aquellos que preguntan "¿por qué no usar strpos?" llamando a otras soluciones "trabajo innecesario"


strpos es rápido, pero no es la herramienta adecuada para este trabajo.

Para entender, aquí hay una pequeña simulación como ejemplo:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

¿Qué hace la computadora "adentro"?

    With strccmp, etc...

    is a===b? NO
    return false



    With strpos

    is a===b? NO -- iterating in haysack
    is a===c? NO
    is a===d? NO
    ....
    is a===g? NO
    is a===g? NO
    is a===a? YES
    is 1===1? YES -- iterating in needle
    is 2===3? YES
    is 4===4? YES
    ....
    is 8===8? YES
    is c===x? NO: oh God,
    is a===1? NO -- iterating in haysack again
    is a===2? NO
    is a===3? NO
    is a===4? NO
    ....
    is a===x? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    ...
    ... may many times...
    ...
    is a===b? NO
    is a===a? YES -- iterating in needle again
    is 1===1? YES
    is 2===3? YES
    is 4===4? YES
    is 8===8? YES
    is c===c? YES YES YES I have found the same string! yay!
    was it at position 0? NOPE
    What you mean NO? So the string I found is useless? YEs.
    Damn.
    return false

Asumir que strlen no itera la cadena completa (pero incluso en ese caso) esto no es conveniente en absoluto.

FrancescoMM
fuente
Solo hay una aceleración si los primeros personajes son diferentes.
Ja͢ck
2
@Jack sí, por supuesto, la idea es que estadísticamente eso sucede, por lo que la aceleración es generalmente un 20% -30% en todo el conjunto de prueba (incluidos los casos en que no es diferente). Ganas mucho cuando son diferentes y pierdes muy poco cuando no lo son. En el promedio, obtienes ese 30% (varía según el conjunto, pero sobre todo ganas velocidad en pruebas grandes)
FrancescoMM
"pero no es la herramienta adecuada para este trabajo" ... ¿Alguna cita?
Fr0zenFyr
1
WTF Enumeré todo el proceso a continuación, ¿a quién debo citar, más que eso? ¿Usaría una función que busca hasta el final de una cadena para decirle que el carácter de puño no es una 'a'? ¿A quién le importa? No es la herramienta correcta porque es una herramienta para buscar, no para comparar, ¡no hay necesidad de citar a Aristoteles para decir lo obvio!
FrancescoMM
6

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

$content = "The main string to search";
$search = "T";
//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
6

Por lo general, termino yendo con una biblioteca como underscore-php en estos días.

require_once("vendor/autoload.php"); //use if needed
use Underscore\Types\String; 

$str = "there is a string";
echo( String::startsWith($str, 'the') ); // 1
echo( String::endsWith($str, 'ring')); // 1   

La biblioteca está llena de otras funciones útiles.

yuvilio
fuente
6

La respuesta de mpen es increíblemente exhaustiva, pero, desafortunadamente, el punto de referencia proporcionado tiene una supervisión muy importante y perjudicial.

Debido a que cada byte en agujas y pajares es completamente aleatorio, la probabilidad de que un par aguja-pajar difiera en el primer byte es 99.609375%, lo que significa que, en promedio, aproximadamente 99609 de los 100000 pares diferirán en el primer byte . En otras palabras, el punto de referencia está fuertemente sesgado hacia startswithimplementaciones que verifican el primer byte explícitamente, comostrncmp_startswith2 hace.

Si el bucle generador de prueba se implementa de la siguiente manera:

echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
    if($i % 2500 === 0) echo '.';

    $haystack_length = random_int(1, 7000);
    $haystack = random_bytes($haystack_length);

    $needle_length = random_int(1, 3000);
    $overlap_length = min(random_int(0, $needle_length), $haystack_length);
    $needle = ($needle_length > $overlap_length) ?
        substr($haystack, 0, $overlap_length) . random_bytes($needle_length - $overlap_length) :
        substr($haystack, 0, $needle_length);

    $test_cases[] = [$haystack, $needle];
}
echo " done!<br />";

Los resultados de referencia cuentan una historia ligeramente diferente:

strncmp_startswith: 223.0 ms
substr_startswith: 228.0 ms
substr_compare_startswith: 238.0 ms
strncmp_startswith2: 253.0 ms
strpos_startswith: 349.0 ms
preg_match_startswith: 20,828.7 ms

Por supuesto, este punto de referencia todavía puede no ser perfectamente imparcial, pero también prueba la eficiencia de los algoritmos cuando se les dan agujas que coinciden parcialmente.

Veeno
fuente
5

en breve:

function startsWith($str, $needle){
   return substr($str, 0, strlen($needle)) === $needle;
}

function endsWith($str, $needle){
   $length = strlen($needle);
   return !$length || substr($str, - $length) === $needle;
}
Vincent Pazeller
fuente
5

Solo una recomendación:

function startsWith($haystack,$needle) {
    if($needle==="") return true;
    if($haystack[0]<>$needle[0]) return false; // ------------------------- speed boost!
    return (0===substr_compare($haystack,$needle,0,strlen($needle)));
}

Esa línea adicional, que compara el primer carácter de las cadenas, puede hacer que el caso falso regrese de inmediato , por lo que muchas de sus comparaciones son mucho más rápidas (7 veces más rápido cuando medí). En el caso real, prácticamente no paga precio por el rendimiento de esa línea, así que creo que vale la pena incluirlo. (Además, en la práctica, cuando prueba muchas cadenas para un fragmento inicial específico, la mayoría de las comparaciones fallarán, ya que en un caso típico está buscando algo).

dkellner
fuente
2
Error en su código: startsWith("123", "0")datrue
Tino
Sí, mal! $ Comprobación de verificación sucedió. ¡Lo siento! (Solo quería ilustrar el concepto en la línea 3)
dkellner
4

La substrfunción puede volver falseen muchos casos especiales, así que aquí está mi versión, que trata estos problemas:

function startsWith( $haystack, $needle ){
  return $needle === ''.substr( $haystack, 0, strlen( $needle )); // substr's false => empty string
}

function endsWith( $haystack, $needle ){
  $len = strlen( $needle );
  return $needle === ''.substr( $haystack, -$len, $len ); // ! len=0
}

Pruebas ( truesignifica bueno):

var_dump( startsWith('',''));
var_dump( startsWith('1',''));
var_dump(!startsWith('','1'));
var_dump( startsWith('1','1'));
var_dump( startsWith('1234','12'));
var_dump(!startsWith('1234','34'));
var_dump(!startsWith('12','1234'));
var_dump(!startsWith('34','1234'));
var_dump('---');
var_dump( endsWith('',''));
var_dump( endsWith('1',''));
var_dump(!endsWith('','1'));
var_dump( endsWith('1','1'));
var_dump(!endsWith('1234','12'));
var_dump( endsWith('1234','34'));
var_dump(!endsWith('12','1234'));
var_dump(!endsWith('34','1234'));

Además, la substr_comparefunción también vale la pena mirar. http://www.php.net/manual/en/function.substr-compare.php

biziclop
fuente
4

Lo haría asi

     function startWith($haystack,$needle){
              if(substr($haystack,0, strlen($needle))===$needle)
              return true;
        }

  function endWith($haystack,$needle){
              if(substr($haystack, -strlen($needle))===$needle)
              return true;
        }
Jelle Keizer
fuente
Olvidarse de devolver falso si no coincide. Errgo incorrecto como es el valor de retorno de una función no debe ser 'asumido', pero sé lo que busca al menos en comparación con otras respuestas.
Spoo
3

Basado en la respuesta de James Black, aquí está su final con la versión:

function startsWith($haystack, $needle, $case=true) {
    if ($case)
        return strncmp($haystack, $needle, strlen($needle)) == 0;
    else
        return strncasecmp($haystack, $needle, strlen($needle)) == 0;
}

function endsWith($haystack, $needle, $case=true) {
     return startsWith(strrev($haystack),strrev($needle),$case);

}

Nota: He cambiado la parte if-else por la función startWith de James Black, porque strncasecmp es en realidad la versión de strncmp que no distingue entre mayúsculas y minúsculas.

bobo
fuente
2
Tenga en cuenta que strrev()es creativo pero muy costoso, especialmente si tiene cadenas de decir ... 100Kb.
Alexis Wilke
Use en ===lugar de ==estar seguro. 0es igual a muchas cosas en PHP.
nawfal
3

¿Por qué no lo siguiente?

//How to check if a string begins with another string
$haystack = "valuehaystack";
$needle = "value";
if (strpos($haystack, $needle) === 0){
    echo "Found " . $needle . " at the beginning of " . $haystack . "!";
}

Salida:

¡Valor encontrado al comienzo de valuehaystack!

Tenga en cuenta strposque devolverá falso si no se encontró la aguja en el pajar y devolverá 0 si, y solo si, se encontró la aguja en el índice 0 (también conocido como el comienzo).

Y aquí termina: con:

$haystack = "valuehaystack";
$needle = "haystack";

//If index of the needle plus the length of the needle is the same length as the entire haystack.
if (strpos($haystack, $needle) + strlen($needle) === strlen($haystack)){
    echo "Found " . $needle . " at the end of " . $haystack . "!";
}

En este escenario no hay necesidad de que una función empiece con () como

(strpos($stringToSearch, $doesItStartWithThis) === 0)

devolverá verdadero o falso con precisión.

Parece extraño que sea así de simple con todas las funciones salvajes que se ejecutan desenfrenadamente aquí.

Kade Hafen
fuente
3
Parece extraño que si está buscando "xy" dentro de la cadena "abcdefghijklmxyz" en lugar de simplemente comparar "x" con "a" y devolver FALSE, busca todos los caracteres de "a" a "m" y luego termina encontrando "xy" dentro de la cadena, y finalmente devuelve FALSO porque su posición no es cero. Esto es lo que está haciendo, y es extraño y más salvaje que cualquier otra función desenfrenada aquí.
FrancescoMM
La simplicidad está en la escritura, no en la lógica.
Kade Hafen
No es tanto la lógica, es la posible optimización que Francsco estaba señalando. El uso strpos()será lento, excepto cuando coincida. strncmp()Sería mucho mejor en este caso.
Alexis Wilke
Cuando realiza funciones de nivel tan bajo, generalmente desea buscar la solución con mayor velocidad optimizada, sin importar cuán compleja sea, ya que esto se llamará millones de veces. Cada microsegundo que ganes o pierdas aquí hará una diferencia muy real. Así que mejor modifícalo (y luego olvídate de la complejidad, ahora que tienes la función), en lugar de buscar el aspecto y perder una cantidad de tiempo horrible cuando ni siquiera sabes qué salió mal. Imagine verificar una cadena de 2GB que no coincide.
dkellner
3

Muchas de las respuestas anteriores funcionarán igual de bien. Sin embargo, esto es posiblemente lo más corto posible y lograr que haga lo que desee. Simplemente declaras que te gustaría que 'vuelva verdadero'. Así que he incluido soluciones que devuelven boolean verdadero / falso y el texto verdadero / falso.

// boolean true/false
function startsWith($haystack, $needle)
{
    return strpos($haystack, $needle) === 0 ? 1 : 0;
}

function endsWith($haystack, $needle)
{
    return stripos($haystack, $needle) === 0 ? 1 : 0;
}


// textual true/false
function startsWith($haystack, $needle)
{
    return strpos($haystack, $needle) === 0 ? 'true' : 'false';
}

function endsWith($haystack, $needle)
{
    return stripos($haystack, $needle) === 0 ? 'true' : 'false';
}
Wynshaft
fuente
Cierto. Sin embargo, Peter estaba pidiendo una función que funcionara con cadenas de caracteres. No obstante, he actualizado mi respuesta para apaciguarte.
wynshaft
Después de editar, su solución ahora es completamente obsoleta. Regresa 'true'y 'false'como cadenas, que son ambas trueen un sentido booleano. Sin embargo, es un buen patrón para algo como underhanded.xcott.com ;)
Tino
Bueno, Peter acaba de decir que quería que volviera "verdadero". Así que pensé que le devolvería lo que me pidió. He agregado ambas versiones, por si acaso eso no es lo que él quería.
wynshaft
2

También puedes usar expresiones regulares:

function endsWith($haystack, $needle, $case=true) {
  return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
Hombre libre
fuente
3
$ aguja se debe escapar con preg_quote($needle, '/').
Timo Tijhof
2

Sin copia y sin bucle interno:

function startsWith(string $string, string $start): bool
{
    return strrpos($string, $start, - strlen($string)) !== false;
}

function endsWith(string $string, string $end): bool
{
    return ($offset = strlen($string) - strlen($end)) >= 0 
    && strpos($string, $end, $offset) !== false;
}
mazatwork
fuente
¡Esto debería ser mucho más rápido que la implementación de MrHus! podría compararlo
hanshenrik
1

Aquí hay una solución eficiente para PHP 4. Puede obtener resultados más rápidos si usa PHP 5 utilizando en substr_comparelugar de strcasecmp(substr(...)).

function stringBeginsWith($haystack, $beginning, $caseInsensitivity = false)
{
    if ($caseInsensitivity)
        return strncasecmp($haystack, $beginning, strlen($beginning)) === 0;
    else
        return strncmp($haystack, $beginning, strlen($beginning)) === 0;
}

function stringEndsWith($haystack, $ending, $caseInsensitivity = false)
{
    if ($caseInsensitivity)
        return strcasecmp(substr($haystack, strlen($haystack) - strlen($ending)), $haystack) === 0;
    else
        return strpos($haystack, $ending, strlen($haystack) - strlen($ending)) !== false;
}
Patrick Smith
fuente
0

Puede usar la función fnmatch para esto.

// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);
ya.teck
fuente
advertencia, no es binario seguro, y ni siquiera es seguro contra agujas que contienen comodines = /
hanshenrik