les fonctions beginWith () et endsWith () en PHP

1483

Comment puis-je écrire deux fonctions qui prendraient une chaîne et retourneraient 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
Cliquez Upvote
la source
19
Voir la classe Str de Laravel beginWith () et endsWith () pour des méthodes bien testées . Des cas marginaux ont été rencontrés, l'utilisation généralisée de ce code est donc un avantage.
Gras Double
1
Vous pourriez trouver s($str)->startsWith('|')et s($str)->endsWith('}')utile, comme on le trouve dans cette bibliothèque autonome .
caw
3
Avertissement: la plupart des réponses ici ne sont pas fiables dans les codages multi-octets tels que UTF-8.
Álvaro González
Suite à mon commentaire ci-dessus, vous pouvez vous assurer d'utiliser la dernière version (à partir d'aujourd'hui, 5.4 ). Notamment, startsWith () a été optimisé pour les grandes chaînes de meules de foin.
Gras Double

Réponses:

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

Utilisez-le si vous ne souhaitez pas utiliser d'expression régulière.

MrHus
la source
16
+1 C'est plus propre que la réponse acceptée. En outre, $lengthn'est pas nécessaire dans la dernière ligne du endsWith().
trop de php
13
Je dirais endsWith ('foo', '') == false est le comportement correct. Parce que foo ne se termine pas avec rien. «Foo» se termine par «o», «oo» et «Foo».
MrHus
126
EndsWith peut être écrit beaucoup plus court:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj
12
Vous pouvez éviter le iftout en passant $lengthle troisième paramètre à substr: return (substr($haystack, -$length, $length);. Cela gère le cas de $length == 0en retournant une chaîne vide et non la totalité $haystack.
mxxk
20
@MrHus Je recommanderais d'utiliser des fonctions sûres multi-octets, par exemple mb_strlen et mb_substr
19Gerhard85
1025

Vous pouvez utiliser la substr_comparefonction pour vérifier au début et à la fin:

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 étuis complets, partiels et sans correspondance. strncmpest un peu plus rapide pour les débuts avec mais il ne peut pas vérifier les fins avec.

Salman A
la source
74
Cette réponse est arrivée au Daily WTF! : D Voir thedailywtf.com/articles/…
Wim ten Brink
Veuillez noter que les commentaires @DavidWallace et @FrancescoMM s'appliquent à une ancienne version de cette réponse. La réponse actuelle utilise strrposce qui (devrait) échouer immédiatement si l'aiguille ne correspond pas au début de la botte de foin.
Salman A
2
Je ne comprends pas. Basé sur php.net/manual/en/function.strrpos.php : "Si la valeur est négative, la recherche commencera à partir de ce nombre de caractères à partir de la fin de la chaîne, en effectuant une recherche en arrière." Cela semble indiquer que nous commençons au caractère 0 (en raison de -strlength($haystack)) et recherchons en arrière à partir de là? Cela ne veut-il pas dire que vous ne cherchez rien? Je ne comprends pas non plus les !== falseparties de cela. Je suppose que cela repose sur une bizarrerie de PHP où certaines valeurs sont "véridiques" et d'autres "fausses", mais comment cela fonctionne-t-il dans ce cas?
Welbog
3
@Welbog: par exemple haystack = xxxyyyneedle = yyyet l'utilisation de strrposla recherche commence à partir du premier x. Maintenant, nous n'avons pas de correspondance réussie ici (trouvé x au lieu de y) et nous ne pouvons plus revenir en arrière (nous sommes au début de la chaîne), la recherche échoue immédiatement . À propos de l'utilisation !== false- strrposdans l'exemple ci-dessus, renvoie 0 ou false et pas une autre valeur. De même, strposdans l'exemple ci-dessus, peut retourner $temp(la position attendue) ou false. Je suis allé avec !== falsecohérence, mais vous pouvez utiliser === 0et === $tempdans les fonctions respectivement.
Salman A
8
@spoo il a déjà été établi que strpos === 0 est une solution terrible si la botte de foin est grande et que l'aiguille n'existe pas.
Salman A
243

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

Les 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é du 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é du 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

mpen
la source
3
Si les chaînes ne sont pas vides, comme dans vos tests, c'est en fait en quelque sorte (20-30%) plus rapide: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}j'ai ajouté une réponse ci-dessous.
FrancescoMM
3
@Jronny Parce que 110 est inférieur à 133 ... ??
mpen
2
Bon sang, je ne sais pas ce qui m'est venu à l'esprit cette fois-là. Fier le manque de sommeil.
Jronny
1
@mpen, j'ai remarqué pas du tout l'éléphant :(
Visman
1
Ces tests ne sont pas bons pour tester les performances. Vous utilisez une chaîne aléatoire comme aiguille. Dans 99,99% des cas, il n'y aura AUCUNE correspondance. La plupart des fonctions se termineront après avoir correspondu au premier octet. Qu'en est-il des cas où une correspondance est trouvée? Quelle fonction prend le moins de temps pour conclure un match réussi? Qu'en est-il des cas où 99% des aiguilles correspondent mais pas les derniers octets? Quelle fonction prend le moins de temps pour conclure aucune correspondance?
Salman A
137

Toutes les réponses semblent à ce jour pour faire des charges de travail inutile, strlen calculations, string allocations (substr), etc. 'strpos'et 'stripos'fonctions renvoient l'index de la première occurrence de $needledans $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
la source
2
endsWith()la fonction a une erreur. Sa première ligne devrait être (sans le -1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma
6
La chose strlen () n'est pas inutile. Dans le cas où la chaîne ne commence pas par l'aiguille donnée, votre code analysera inutilement toute la botte de foin.
AppleGrew
5
@ Mark oui, vérifier juste le début est beaucoup plus rapide, surtout si vous faites quelque chose comme vérifier les types MIME (ou tout autre endroit où la chaîne est forcément grande)
chacham15
2
@mark J'ai fait des tests avec une botte de foin de 1000 chars et une aiguille de 10 ou 800 chars et la strpos était 30% plus rapide. Faites vos repères avant d'affirmer que quelque chose est plus rapide ou non ...
wdev
7
Vous devriez fortement envisager de citer l'aiguille comme strpos($haystack, "$needle", 0)s'il y avait une chance que ce ne soit pas déjà une chaîne (par exemple, si elle venait json_decode()). Sinon, le comportement par défaut [étrange] de strpos()peut entraîner des résultats inattendus: " Si l'aiguille n'est pas une chaîne, elle est convertie en entier et appliquée comme valeur ordinale d'un caractère. "
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édit à :

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

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

KdgDev
la source
1
strtolower n'est pas le meilleur moyen de rendre les fonctions insensibles à la casse. Dans certains cas, le boîtier est plus complexe que juste supérieur et inférieur.
Sander Rijken
8
Je vois des plaintes et pas de solution ... Si tu vas dire que c'est mauvais, alors tu devrais donner un exemple de comment ça devrait être aussi.
KdgDev
2
@WebDevHobo: c'est pourquoi j'ai ajouté moi-même une réponse un jour avant votre commentaire. Pour votre code, strcasecmp était en effet la bonne chose à faire.
Sander Rijken
29

Le regex fonctionne ci-dessus, mais avec les autres modifications également suggérées ci-dessus:

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

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
tridian
la source
2
en php pour les opérations sur chaîne, l'ordre des paramètres est $ haystack, $ needle. ces fonctions sont inversées et agissent comme des fonctions de tableau où l'ordre est en fait $ needle, $ haystack.
Andy
29

Cette question a déjà de nombreuses réponses, mais dans certains cas, vous pouvez vous contenter de quelque chose de plus simple que tous. Si la chaîne que vous recherchez est connue (codée en dur), vous pouvez utiliser des expressions régulières sans aucune citation, 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 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 à définir de fonction (comme endsWith()) comme indiqué ci-dessus.

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

noamtm
la source
vous n'avez pas besoin de coder en dur la chaîne. l'expression régulière peut être dynamique.
Ryan
2
@self true, mais si la chaîne n'est pas codée en dur, vous devez y échapper. Actuellement, il y a 2 réponses à cette question qui le font. C'est facile, mais cela complique un peu le code. Mon point était donc que pour les cas très simples, où le codage en dur est possible, vous pouvez rester simple.
noamtm
1
Vous n'avez pas non plus à échapper à la barre oblique, vous pouvez envelopper l'expression régulière dans un autre caractère, comme @, de sorte que la barre oblique ( /) ne doive pas être échappée. Voir l'exemple # 3 ici: php.net/manual/en/function.preg-match.php .
cjbarth
Merci @cjbarth. J'ai changé ma réponse en conséquence. BTW, "#" est l'exemple donné dans php.net/manual/en/regexp.reference.delimiters.php lorsqu'il s'agit d'une barre oblique.
noamtm
23

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

Fonctionne uniquement pour les chaînes et si $ haystack ne contient qu'un seul 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
lepe
la source
1
c'est probablement la réponse la plus efficace car n'utilisant aucune fonction en plus, juste une chaîne habituelle ...
Il devrait probablement vérifier si la chaîne a au moins un caractère et si les deux paramètres ont été échangés
a1an
1
Créatif. Aiguilles contenant des meules de foin. BTW il y a un affaiblissement laid avec:, endsWithChar('','x')mais le résultat est correct
Tino
18

Voici deux fonctions qui n'introduisent pas de chaîne temporaire, ce qui pourrait être utile lorsque les aiguilles sont sensiblement 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;
}
Jack
la source
2
+1 Fonctionne depuis PHP5.1 et IMHO meilleure réponse. Mais endsWidthdevrait faire return $needle==='' || substr_compare(... donc ça marche comme prévu pour -strlen($needle)===0qui, sans le correctif, fait endsWith('a','')retourfalse
Tino
@Tino Merci ... Je pense que c'est un bug en substr_compare()fait, j'ai donc ajouté un PR pour résoudre ce problème :)
Ja͢ck
3
L'appel endsWith('', 'foo')déclenche un avertissement: «substr_compare (): la position de départ ne peut pas dépasser la longueur de chaîne initiale». C'est peut-être un autre bug substr_compare(), mais pour l'éviter, vous avez besoin d'une pré-vérification comme ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_
@gx_ Pas besoin de ralentir avec plus de code. Utilisez simplement return $needle === '' || @substr_compare(.. pour supprimer cet avertissement.
Tino
17

Extrémités les plus rapides Avec la solution ():

# 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
Lucas Bustamante
la source
3
+1 pour avoir pris le temps de comparer différentes solutions et de les comparer! vous devez également mentionner la version de PHP que vous avez utilisée, car les optimisations sont effectuées à mesure que le langage évolue! J'ai vu des améliorations spectaculaires sur les fonctions de comparaison de chaînes d'une version PHP à l'autre :)
Christophe Deliens
1
faisant écho à @ChristopheDeliens et à sa demande de fournir la version PHP. J'ai exécuté votre test sur 7.3.2 et obtenu des résultats similaires FWIW.
Jeff
16

Je me rends compte que cela a été terminé, mais vous voudrez peut-être regarder strncmp car il 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;
}    
James Black
la source
comment feriez-vous avec cela?
mpen
@Mark - vous pouvez regarder la réponse acceptée, mais je préfère utiliser strncmp principalement parce que je pense que c'est plus sûr.
James Black
Je veux dire avec strncmp spécifiquement. Vous ne pouvez pas spécifier de décalage. Cela signifierait que votre fonction endsWith devrait utiliser une méthode entièrement différente.
mpen
@Mark - Pour des fins avec lesquelles j'utiliserais simplement strrpos ( php.net/manual/en/function.strrpos.php ), mais, généralement, chaque fois que vous allez utiliser strcmp, strncmp est probablement une option plus sûre.
James Black
11

Vous pouvez utiliser strposetstrrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
Lex
la source
1
Devriez-vous utiliser le triple égal ici strpos($sHaystack, $sNeedle) == 0comme ça strpos($sHaystack, $sNeedle) === 0? Je vois un bug lors de l' false == 0évaluation true.
Kalyan
11

Voici une version sûre à plusieurs octets de la réponse acceptée, cela fonctionne bien 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);
}
Vahid Amiri
la source
2
Im assez sûr que c'est juste un gaspillage de CPU. tout ce que vous devez vérifier, pour StarstWith et EndsWith, est simplement de vérifier que les octets correspondent, et c'est exactement ce que fait la réponse acceptée. ce 1 gaspille du temps à calculer le nombre de caractères utf8 de l'aiguille, et où la position du nième caractère utf8 de la botte de foin est .. je pense, sans être sûr à 100%, c'est juste un gaspillage de cpu. pouvez-vous trouver un cas de test réel où la réponse acceptée échoue, et cela ne fonctionne pas?
hanshenrik
2
@hanshenrik - cela pourrait arriver btw, dans le cas très rare lorsque vous recherchez une chaîne qui contient les mêmes octets qu'un UTF8 mais avec la moitié du dernier caractère manquant. Par exemple, vous avez unicode C5 91 (lettre "ő") et vous recherchez C5 (lettre "Å"), il ne devrait pas vous donner de correspondance. D'un autre côté, bien sûr, pourquoi chercheriez-vous une botte de foin utf pour une aiguille non utf ... Mais pour les contrôles pare-balles, cela doit être considéré comme une possibilité.
dkellner
Dans startsWithce devrait être$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen
2
@ThomasKekeisen Merci, corrigé.
Vahid Amiri
8

Des lignes simples courtes et faciles à comprendre sans expressions régulières.

startsWith () est simple.

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

endsWith () utilise le strrev () légèrement fantaisie et lent:

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
Dan
la source
@FrancescoMM: strpos n'est pas le "bon outil" ... Pourquoi? Quels sont donc les "bons outils"? EDIT: J'ai lu votre réponse ci-dessous. Je pensais que la programmation était comme une invention en utilisant les ressources dont vous disposez.
Fr0zenFyr
"parce que c'est un outil de recherche, pas de comparaison?" Cit. Aristoteles
FrancescoMM
7

En se concentrant sur les débuts, si vous êtes sûr que les chaînes ne sont pas vides, ajouter un test sur le premier caractère, avant la comparaison, le strlen, 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. L'ajout d'un autre test de caractère, comme $ haystack {1} === $ needle {1} ne semble pas accélérer beaucoup les choses, voire ralentir.

===semble plus rapide que l' == opérateur conditionnel (a)?b:csemble plus rapide queif(a) b; else c;


Pour ceux qui demandent "pourquoi ne pas utiliser des strpos?" qualifier d'autres solutions de "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

Que fait l'ordinateur "à 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.

FrancescoMM
la source
Il n'y a d'accélération que si les premiers personnages sont différents.
Ja͢ck
2
@ Jack oui, bien sûr, l'idée est que cela se produit statistiquement, donc l'accélération est généralement de 20% à 30% sur l'ensemble des tests (y compris les cas où ce n'est pas différent). Vous gagnez beaucoup quand ils sont différents et perdez très peu quand ils ne le sont pas. Dans la moyenne, vous gagnez 30% (varie selon le set, mais surtout vous gagnez de la vitesse sur de gros tests)
FrancescoMM
"mais ce n'est pas le bon outil pour ce métier" ... Une citation?
Fr0zenFyr
1
WTF. J'ai énuméré tout le processus ci-dessous, qui dois-je citer, plus que cela? Souhaitez-vous utiliser une fonction qui recherche jusqu'à la fin d'une chaîne pour vous dire que le premier caractère n'est pas un «a»? Faites-le qui s'en soucie? Ce n'est pas le bon outil car c'est un outil de recherche, pas de comparaison, pas besoin de citer Aristoteles pour dire l'évidence!
FrancescoMM
6

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

Je finis généralement par utiliser une bibliothèque comme underscore-php de nos jours.

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 regorge d'autres fonctions pratiques.

yuvilio
la source
6

La réponse de mpen est incroyablement approfondie, mais, malheureusement, l'indice de référence fourni a une surveillance très importante et préjudiciable.

Étant donné que chaque octet dans les aiguilles et les meules de foin est complètement aléatoire, la probabilité qu'une paire aiguille-meule de foin diffère sur le tout premier octet est de 99,609375%, ce qui signifie qu'en moyenne, environ 99609 des 100000 paires différeront sur le tout premier octet. . En d'autres termes, l'indice de référence est fortement biaisé vers les startswithimplémentations qui vérifient explicitement le premier octet, comme le strncmp_startswith2fait.

Si la boucle de génération de test est à la place 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, ce point de référence n'est peut-être pas encore parfaitement impartial, mais il teste également l'efficacité des algorithmes lorsqu'ils reçoivent des aiguilles partiellement correspondantes.

Veeno
la source
5

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;
}
Vincent Pazeller
la source
5

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 faire revenir la fausse casse immédiatement , rendant ainsi beaucoup de vos comparaisons beaucoup plus rapides (7 fois plus rapides lorsque j'ai mesuré). Dans le vrai cas, vous ne payez pratiquement aucun prix en performances pour cette seule ligne, donc je pense que cela vaut la peine d'être inclus. (De plus, dans la pratique, lorsque vous testez de nombreuses chaînes pour un bloc de départ spécifique, la plupart des comparaisons échouent car dans un cas typique, vous recherchez quelque chose.)

dkellner
la source
2
Bug dans votre code: startsWith("123", "0")donnetrue
Tino
Ouais, mauvais! $ Vérification s'est produite. Désolé! (Je voulais juste illustrer le concept dans la ligne 3)
dkellner
4

La substrfonction peut revenir falsedans de nombreux cas spéciaux, 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 ( truesignifie bon):

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

En outre, la substr_comparefonction mérite également d'être examinée. http://www.php.net/manual/en/function.substr-compare.php

biziclop
la source
4

Cela peut fonctionner

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

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

user507410
la source
4

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;
        }
Jelle Keizer
la source
Oublier de retourner false si cela ne correspond pas. Errgo incorrect, tout comme la valeur de retour d'une fonction ne doit pas être `` supposée '', mais je sais ce que vous recherchez après au moins par rapport aux autres réponses.
Spoo
3

Basé sur la réponse de James Black, voici ses extrémités avec la version:

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 échangé la partie if-else pour la fonction startsWith de James Black, car strncasecmp est en fait la version insensible à la casse de strncmp.

bobo
la source
2
Notez que strrev()c'est créatif mais très coûteux, surtout si vous avez des cordes à dire ... 100Kb.
Alexis Wilke
Utilisez ===au lieu d' ==être sûr. 0est égal à beaucoup de choses en PHP.
nawfal
3

Pourquoi pas les suivants?

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

Production:

Valeur trouvée au début de valuehaystack!

Gardez à l'esprit, strposretournera faux 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'index 0 (AKA au début).

Et voici la fin Avec:

$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, il n'est pas nécessaire pour une fonction démarre avec () comme

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

retournera vrai ou faux avec précision.

Il semble étrange que ce soit aussi simple avec toutes les fonctions sauvages qui sévissent ici.

Kade Hafen
la source
3
Semble étrange que si vous recherchez "xy" à l'intérieur de la chaîne "abcdefghijklmxyz" au lieu de simplement comparer "x" à "a" et renvoyer FAUX, vous regardez chaque caractère de "a" à "m" et vous finissez par trouver "xy" à l'intérieur de la chaîne, et enfin vous retournez FAUX car sa position n'est pas nulle! C'est ce que vous faites, et c'est étrange et plus sauvage que toute autre fonction rampante ici.
FrancescoMM
La simplicité réside dans la frappe, pas dans la logique.
Kade Hafen
Ce n'est pas tant la logique, c'est l'optimisation possible que Francsco soulignait. L'utilisation strpos()sera lente sauf lorsqu'elle correspond. strncmp()serait beaucoup mieux dans ce cas.
Alexis Wilke
Lorsque vous effectuez de telles fonctions de bas niveau, vous voulez généralement opter pour la solution la plus optimisée en termes de vitesse, quelle que soit sa complexité, car cela s'appellera des millions de fois. Chaque microseconde que vous gagnez ou perdez ici fera une réelle différence. Il vaudrait donc mieux peaufiner l'enfer (et ensuite oublier la complexité, maintenant que vous avez la fonction), au lieu d'aller chercher les regards et de perdre un temps horrible plus tard lorsque vous ne savez même pas ce qui ne va pas. Imaginez vérifier une chaîne de 2 Go qui ne correspond pas.
dkellner le
3

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 lui faire faire ce que vous désirez. Vous déclarez simplement que vous aimeriez qu'il «revienne vrai». J'ai donc inclus des solutions qui renvoient booléen true / false et le textuel true / false.

// 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
la source
Vrai. Cependant, Peter demandait une fonction qui fonctionnerait avec des chaînes de caractères. Néanmoins, j'ai mis à jour ma réponse pour vous apaiser.
wynshaft
Après la modification, votre solution est désormais complètement obsolète. Il revient 'true'et 'false'sous forme de chaînes, qui sont toutes deux truedans un sens booléen. C'est un bon modèle pour quelque chose comme underhanded.xcott.com cependant;)
Tino
Eh bien, Peter vient de dire qu'il voulait que cela revienne "vrai". J'ai donc pensé que je retournerais ce qu'il avait demandé. J'ai ajouté les deux versions, juste au cas où ce ne serait pas ce qu'il voulait.
wynshaft
2

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

function endsWith($haystack, $needle, $case=true) {
  return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
Homme libre
la source
3
$ aiguille doit être échappé avec preg_quote($needle, '/').
Timo Tijhof
2

Pas de copie et pas de boucle interne:

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
la source
cela devrait être beaucoup plus rapide que la mise en œuvre de MrHus! je pourrais le comparer
hanshenrik
1

Voici une solution efficace pour PHP 4. Vous pouvez obtenir des résultats plus rapides si vous utilisez PHP 5 à la substr_compareplace 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
la source
0

Vous pouvez utiliser la fonction fnmatch pour cela.

// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);
ya.teck
la source
avertissement, pas sûr pour les binaires, et même pas sûr contre les aiguilles contenant des caractères génériques = /
hanshenrik