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
¿Fue útil?

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

startswith_benchmark.php

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 :

Verifique si una cadena termina con otra cadena

Compruebe si una cadena comienza con otra cadena

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

Puede usar strpos y strrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);

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;
}

Fuente: https://stackoverflow.com/a/4419658

¿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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top