web-dev-qa-db-fra.com

startsWith () et endsWith () dans PHP

Comment puis-je écrire deux fonctions qui prendraient une chaîne et renverraient si elle commence par le caractère/chaîne spécifié ou se termine par elle?

Par exemple:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
1374
Click Upvote
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);
}

Utilisez ceci si vous ne voulez pas utiliser une expression régulière.

1476
MrHus

Vous pouvez utiliser la fonction substr_compare pour vérifier les débuts et les fins:

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

Cela devrait être l’une des solutions les plus rapides sur PHP 7 ( script de référence ). Testé contre des meules de foin de 8 Ko, des aiguilles de différentes longueurs et des plaquettes complètes, partielles et sans correspondance. strncmp est une touche plus rapide pour les débuts avec, mais il ne peut pas vérifier les fins avec.

979
Salman A

Mis à jour le 23 août 2016

Les fonctions

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

Des tests

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

Résultats (PHP 7.0.9)

(Tri le plus rapide au plus lent)

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

Résultats (PHP 5.3.29)

(Tri le plus rapide au plus lent)

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

234
mpen

Jusqu'à présent, toutes les réponses semblent faire beaucoup de travail inutile, strlen calculations, string allocations (substr), etc. Les fonctions 'strpos' et 'stripos' renvoient l'index de la première occurrence de $needle dans $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;
}
135
Sander Rijken
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édit à :

Vérifier si une chaîne se termine par une autre chaîne

Vérifier si une chaîne commence par une autre chaîne

46
KdgDev

La regex fonctionne ci-dessus, mais avec les autres réglages également suggérés ci-dessus:

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

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
28
tridian

Cette question a déjà de nombreuses réponses, mais dans certains cas, vous pouvez vous contenter de quelque chose de plus simple. Si la chaîne que vous recherchez est connue (codée en dur), vous pouvez utiliser des expressions régulières sans guillemets, etc.

Vérifiez si une chaîne commence par 'ABC':

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

se termine par 'ABC':

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

Dans mon cas simple, je voulais vérifier si une chaîne de caractères se termine par une barre oblique:

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

L'avantage: comme c'est très court et simple, vous n'avez pas besoin de définir une fonction (telle que endsWith()) comme indiqué ci-dessus.

Mais encore une fois - ce n'est pas une solution pour chaque cas, juste celui-ci très spécifique.

24
noamtm

Si la vitesse est importante pour vous, essayez ceci (je crois que c'est la méthode la plus rapide)

Ne fonctionne que pour les chaînes et si $ haystack ne contient qu'un caractère

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
22
lepe

Voici deux fonctions qui n'introduisent pas de chaîne temporaire, ce qui pourrait être utile lorsque les aiguilles sont très grosses:

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

function endsWith($haystack, $needle)
{
    return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
17
Ja͢ck

Je réalise que ceci est terminé, mais vous voudrez peut-être regarder strncmp car cela vous permet de mettre la longueur de la chaîne à comparer, donc:

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

La solution la plus rapide endsWith ():

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

Référence:

# 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();

Résultats de référence:

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
14
Lucas Bustamante

Vous pouvez utiliser strpos et strrpos

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

Lignes simples, faciles à comprendre, sans expressions régulières.

startsWith () est simple.

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

endsWith () utilise les strrev () légèrement fantaisistes et lents:

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
8
Dan

Voici une version multi-octets sécurisée de la réponse acceptée, elle fonctionne correctement pour les chaînes 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);
}
8
Vahid Amiri

Si vous êtes certain que les chaînes ne sont pas vides, ajouter un test sur le premier caractère, avant la comparaison, la chaîne, etc. accélère un peu les choses:

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

C'est en quelque sorte (20% à 30%) plus rapide. Ajouter un autre test de caractère, comme $ haystack {1} === $ needle {1} ne semble pas accélérer beaucoup les choses, peut même ralentir.

=== semble plus rapide que == Opérateur conditionnel (a)?b:c semble plus rapide que if(a) b; else c;


Pour ceux qui demandent "pourquoi ne pas utiliser strpos?" Appelant d'autres solutions "travail inutile"


strpos est rapide, mais ce n’est pas le bon outil pour ce travail.

Pour comprendre, voici une petite simulation à titre d'exemple:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

Qu'est-ce que l'ordinateur fait "à l'intérieur"?

    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

En supposant que strlen n'itère pas la chaîne entière (mais même dans ce cas), cela n'est pas du tout pratique.

7
FrancescoMM

Je finis généralement par aller avec une bibliothèque comme nderscore-php ces jours-ci.

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 bibliothèque est pleine d'autres fonctions utiles.

6
yuvilio

Le réponse by mpen est incroyablement complet, mais, malheureusement, le repère fourni fournit une surveillance très importante et préjudiciable.

Comme chaque octet d'aiguilles et de meules de foin est complètement aléatoire, la probabilité qu'une paire aiguille-meule de foin diffère dès le premier octet est de 99,609375%, ce qui signifie qu'en moyenne, environ 99 609 des 100 000 paires diffèrent le premier octet. . En d'autres termes, le repère est fortement biaisé vers les implémentations de startswith qui vérifient le premier octet de manière explicite, comme le fait strncmp_startswith2.

Si la boucle génératrice de test est implémentée comme suit:

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 />";

les résultats de référence racontent une histoire légèrement différente:

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

Bien sûr, cette référence peut ne pas toujours être parfaitement impartiale, mais elle teste également l'efficacité des algorithmes lorsque des aiguilles partiellement appariées sont également fournies.

6
Veeno

J'espère que la réponse ci-dessous sera efficace et 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';
6
Srinivasan.S

Juste une recommandation:

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

Cette ligne supplémentaire, comparant le premier caractère des chaînes, peut renvoyer la casse fausse immédiatement, rendant ainsi beaucoup de vos comparaisons beaucoup plus rapides (7 fois plus rapidement lorsque j'ai mesuré). Dans le cas réel, vous ne payez pratiquement aucun prix en termes de performances pour cette ligne unique, donc je pense que cela vaut la peine d'être inclus. (En outre, en pratique, lorsque vous testez de nombreuses chaînes pour un morceau de départ spécifique, la plupart des comparaisons échoueront car, dans un cas typique, vous recherchez quelque chose.)

4
dkellner

Pourquoi pas ce qui suit?

//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 . "!";
}

Sortie:

Valeur trouvée au début de valuehaystack!

Gardez à l'esprit, strpos retournera false si l'aiguille n'a pas été trouvée dans la botte de foin, et retournera 0 si, et seulement si, l'aiguille a été trouvée à l'indice 0 (AKA au début).

Et voici la finAvec:

$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 . "!";
}

Dans ce scénario, une fonction startsWith () n'est pas nécessaire.

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

retournera vrai ou faux avec précision.

Cela semble étrange, c'est aussi simple que cela avec toutes les fonctions sauvages qui sévissent ici.

4
Kade Hafen

Cela peut fonctionner

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

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

4
user507410

La fonction substr peut renvoyer false dans de nombreux cas particuliers. Voici donc ma version, qui traite de ces problèmes:

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
}

Tests (true signifie bien):

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'));

De plus, la fonction substr_compare vaut également la peine d'être regardée. http://www.php.net/manual/en/function.substr-compare.php

4
biziclop

en bref:

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

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

Je le ferais comme ça

     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;
        }
4
Jelle Keizer

Vous pouvez également utiliser des expressions régulières:

function endsWith($haystack, $needle, $case=true) {
  return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
3
Freeman

Bon nombre des réponses précédentes fonctionneront tout aussi bien. Cependant, c'est peut-être aussi court que vous pouvez le faire et le faire faire ce que vous désirez. Vous dites simplement que vous aimeriez que cela "retourne vrai". J'ai donc inclus des solutions qui renvoient le booléen vrai/faux et le texte vrai/faux.

// 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';
}
3
wynshaft

Basé sur la réponse de James Black, en voici la version finale:

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

}

Remarque: j'ai remplacé la partie if-else par la fonction startsWith de James Black, car strncasecmp est en fait la version de strncmp qui respecte la casse.

2
bobo

Voici une solution efficace pour PHP 4. Vous pourriez obtenir des résultats plus rapides si vous utilisiez PHP 5 en utilisant substr_compare au lieu 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;
}
1
Patrick Smith

Pas de copie ni de boucle interne:

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

function endsWith($string, $end)
{
    return ($offset = strlen($string) - strlen($end)) >= 0
        && strpos($string, $end, $offset) !== false;
}
0
mazatwork