Las funciones beginWith () y endsWith () en PHP
Pregunta
¿Cómo puedo escribir dos funciones que tomarían una cadena y la devolverían si comienza con el carácter / cadena especificado o termina con ella?
Por ejemplo:
$str = '|apples}';
echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Solución
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.
Otros consejos
Puede usar substr_compare
función para verificar inicio-con-termina-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. strncmp
es un toque más rápido para comenzar, pero no puede verificar si termina.
Actualizado el 23 de agosto de 2016
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
Todas las respuestas hasta ahora parecen hacer un montón de trabajo innecesario, cálculos strlen
, asignaciones de cadenas (substr)
, etc. El 'strpos'
y 'stripos'
devuelven el índice de la primera aparición de $ needle
en $ 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;
}
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 :
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);
}
Esta pregunta ya tiene muchas respuestas, pero en algunos casos puede conformarse con algo más simple que todas ellas. Si la cadena que está buscando es conocida (codificada), puede usar expresiones regulares sin comillas, etc.
Verifique si una cadena comienza con 'ABC':
preg_match('/^ABC/', $myString); // "^" here means beginning of string
termina con 'ABC':
preg_match('/ABC$/', $myString); // "preg_match('#/$#', $myPath); // Use "#" as delimiter instead of escaping slash
quot; here means end of string
En mi caso simple, quería verificar si una cadena termina con una barra inclinada:
<*> La ventaja: dado que es muy corto y simple, no tiene que definir una función (como endsWith ()
) como se muestra arriba.
Pero, de nuevo, esta no es una solución para cada caso, solo esta muy específica.
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
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;
}
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, entonces:
function startsWith($haystack, $needle, $case=true) {
if ($case)
return strncasecmp($haystack, $needle, strlen($needle)) == 0;
else
return strncmp($haystack, $needle, strlen($needle)) == 0;
}
Fin más rápido con 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
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);
}
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);
}
Centrarse 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: c
parece más rápido que if (a) b; de lo contrario 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 " dentro " ;?
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
Suponiendo que strlen no repite toda la cadena (pero incluso en ese caso) esto no es conveniente en absoluto.
Espero que la respuesta a continuación sea eficiente y también 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';
Generalmente 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.
La respuesta de mpen es increíblemente completo, 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á muy sesgado hacia las implementaciones de comienza con
que verifican el primer byte explícitamente, como lo hace strncmp_startswith2
.
Si el bucle generador de pruebas 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.
La función substr
puede devolver false
en 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 ( verdadero
significa 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 función substr_compare
también vale la pena mirar.
http://www.php.net/manual/en/function. substr-compare.php
en resumen:
function startsWith($str, $needle){
return substr($str, 0, strlen($needle)) === $needle;
}
function endsWith($str, $needle){
$length = strlen($needle);
return !$length || substr($str, - $length) === $needle;
}
Esto puede funcionar
function startsWith($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) == $needle;
}
¿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 que strpos
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í.
Lo haría así
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;
}
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 extra, que compara el primer carácter de las cadenas, puede hacer que el caso falso devuelve inmediatamente , haciendo así muchas de sus comparaciones mucho más rápido (7 veces más rápido cuando medí). En el caso real, prácticamente no paga precio por el rendimiento de esa línea única, así que creo que vale la pena incluirla. (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).
También puede usar expresiones regulares:
function endsWith($haystack, $needle, $case=true) {
return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
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';
}
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 beginWith de James Black, porque strncasecmp es en realidad la versión de strncmp que no distingue entre mayúsculas y minúsculas.
Aquí hay una solución eficiente para PHP 4. Puede obtener resultados más rápidos si está en PHP 5 utilizando substr_compare
en lugar 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;
}
$ends_with = strrchr($text, '.'); // Ends with dot
$start_with = (0 === strpos($text, '.')); // Starts with dot
No estoy seguro de por qué esto es tan difícil para las personas. Substr hace un gran trabajo y es eficiente ya que no necesita buscar en toda la cadena si no coincide.
Además, como no estoy verificando valores enteros sino comparando cadenas, no necesariamente tengo que preocuparme por el estricto caso ===. Sin embargo, === es un buen hábito para entrar.
function startsWith($haystack,$needle) {
substring($haystack,0,strlen($needle)) == $needle) { return true; }
return false;
}
function endsWith($haystack,$needle) {
if(substring($haystack,-strlen($needle)) == $needle) { return true; }
return false;
}
o incluso mejor optimizado.
function startsWith($haystack,$needle) {
return substring($haystack,0,strlen($needle)) == $needle);
}
function endsWith($haystack,$needle) {
return substring($haystack,-strlen($needle)) == $needle);
}