startWith () e limitsWith () funzioni in PHP


1482

Come posso scrivere due funzioni che prenderebbero una stringa e ritornerebbero se inizia con il carattere / stringa specificato o finisce con esso?

Per esempio:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true

19
Vedi la classe Str di Laravel da startWith () e limitsWith () per metodi ben testati . Si sono verificati casi limite , quindi l'uso diffuso di questo codice è un vantaggio.
Gras Double,

1
Potresti trovare utile s($str)->startsWith('|')e s($str)->endsWith('}')utile, come si trova in questa libreria autonoma .
Caw,

3
Avvertenza: la maggior parte delle risposte qui non sono affidabili nelle codifiche multi-byte come UTF-8.
Álvaro González,

In seguito al mio commento sopra, puoi assicurarti di utilizzare l'ultima versione (ad oggi, 5.4 ). In particolare, startWith () è stato ottimizzato per stringhe di pagliaio di grandi dimensioni.
Gras Double

Risposte:


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

Usa questo se non vuoi usare un regex.


16
+1 Questo è più pulito della risposta accettata. Inoltre, $lengthnon è necessario nell'ultima riga di endsWith().
troppo php,

13
DireiendsWith ('pippo', '') == false è il comportamento corretto. Perché foo non finisce con niente. 'Foo' termina con 'o', 'oo' e 'Foo'.
MrHus,

125
EndsWith può essere scritto molto più breve:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj l'

12
È possibile evitare il iftutto passando $lengthcome terzo parametro substr: return (substr($haystack, -$length, $length);. Questo gestisce il caso $length == 0restituendo una stringa vuota e non l'intero $haystack.
mxxk,

20
@MrHus Consiglierei di utilizzare le funzioni di sicurezza multi-byte, ad esempio mb_strlen e mb_substr
19 Gerhard85

1025

È possibile utilizzare la substr_comparefunzione per verificare l'inizio-e la fine-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;
}

Questa dovrebbe essere una delle soluzioni più veloci su PHP 7 ( benchmark script ). Testato contro covoni di fieno da 8KB, aghi di varie lunghezze e casi completi, parziali e senza corrispondenza. strncmpè un tocco più veloce per iniziare-con ma non può controllare finisce-con.


74
Questa risposta è arrivata al WTF giornaliero! : D Vedi thedailywtf.com/articles/…
Wim ten Brink,

Si noti che i commenti @DavidWallace e @FrancescoMM si applicano a una versione precedente di questa risposta. La risposta attuale utilizza strrposquale (dovrebbe) fallire immediatamente se l'ago non corrisponde all'inizio del pagliaio.
Salman,

2
Non capisco Basato su php.net/manual/en/function.strrpos.php : "Se il valore è negativo, la ricerca inizierà invece da quel numero di caratteri dalla fine della stringa, cercando all'indietro." Questo sembra indicare che stiamo iniziando dal carattere 0 (a causa di -strlength($haystack)) e stiamo cercando all'indietro da lì? Ciò non significa che non stai cercando nulla? Inoltre non capisco le !== falseparti di questo. Immagino che questo si basi su una stranezza di PHP in cui alcuni valori sono "veritieri" e altri "falsi", ma come funziona in questo caso?
Welbog,

3
@Welbog: ad esempio pagliaio = xxxyyyago = yyye l'utilizzo strrposdella ricerca inizia dal primo x. Ora non abbiamo una corrispondenza corretta qui (trovato x invece di y) e non possiamo più tornare indietro (siamo all'inizio della stringa) la ricerca fallisce immediatamente . Informazioni sull'uso !== false: strrposnell'esempio precedente verrà restituito 0 o false e non un altro valore. Allo stesso modo, strposnell'esempio sopra può restituire $temp(la posizione prevista) o falso. Sono andato con !== falsecoerenza, ma è possibile utilizzare === 0e === $tempnelle funzioni rispettivamente.
Salman,

8
@spoo è già stato stabilito che strpos === 0 è una soluzione terribile se il pagliaio è grande e l'ago non esiste.
Salman

243

Aggiornato il 23 agosto 2016

funzioni

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

test

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

Risultati (PHP 7.0.9)

(Ordinati dal più veloce al più 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

Risultati (PHP 5.3.29)

(Ordinati dal più veloce al più 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


3
Se le stringhe non sono vuote, come nei tuoi test, questo è in realtà in qualche modo (20-30%) più veloce: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}ho aggiunto una risposta di seguito.
FrancescoMM,

3
@Jronny Perché 110 è meno di 133 ... ??
Aprire il

2
Accidenti, non so cosa mi è venuto in mente quella volta. Prolly la mancanza di sonno.
Jronny,

1
@mpen, non ho notato affatto l'elefante :(
Visman,

1
Questi test non sono utili per testare le prestazioni. Quello che stai facendo è usare una stringa casuale come ago. Nel 99,99% dei casi NON ci sarà alcuna corrispondenza. La maggior parte delle funzioni uscirà dopo aver abbinato il primo byte. E i casi in cui viene trovata una corrispondenza? Quale funzione richiede meno tempo per concludere una partita di successo? Che dire dei casi in cui il 99% dell'ago corrisponde ma non gli ultimi byte? Quale funzione richiede meno tempo per concludere nessuna corrispondenza?
Salman A,

137

Tutte le risposte sembrano finora per fare i carichi di lavoro inutile, strlen calculations, string allocations (substr), ecc 'strpos'e 'stripos'funzioni restituiscono l'indice della prima occorrenza di $needlea $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;
}

2
endsWith()la funzione ha un errore. La sua prima riga dovrebbe essere (senza -1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma

6
La cosa strlen () non è superflua. Nel caso in cui la stringa non inizi con l'ago indicato, il tuo codice urterà inutilmente l'intero pagliaio.
Apple è cresciuto il

5
@Mark sì, controllare solo l'inizio è MOLTO più veloce, specialmente se stai facendo qualcosa come controllare i tipi MIME (o qualsiasi altro posto in cui la stringa è destinata a essere grande)
chacham15

2
@mark Ho fatto alcuni benchmark con un pagliaio da 1000 caratteri e un ago da 10 o 800 caratteri e strpos è stato del 30% più veloce. Fai i tuoi benchmark prima di affermare che qualcosa è più veloce o no ...
wdev,

7
Dovresti considerare fortemente di citare l'ago come strpos($haystack, "$needle", 0) se ci fosse qualche possibilità che non sia già una stringa (ad esempio, se proviene da json_decode()). Altrimenti, il comportamento [dispari] di default strpos()può causare risultati imprevisti: " Se l'ago non è una stringa, viene convertito in un numero intero e applicato come valore ordinale di un carattere. "
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);
}

Credito a :

Controlla se una stringa termina con un'altra stringa

Controlla se una stringa inizia con un'altra stringa


1
strtolower non è il modo migliore per rendere insensibili alle maiuscole. In alcuni casi, l'involucro è più complesso di quello superiore e inferiore.
Sander Rijken,

8
Vedo lamentarmi e nessuna soluzione ... Se dirai che è male, allora dovresti dare un esempio di come dovrebbe essere.
KdgDev,

2
@WebDevHobo: ecco perché ho aggiunto una risposta me stesso un giorno prima del tuo commento. Per il tuo codice strcasecmp era davvero la cosa giusta da fare.
Sander Rijken il

29

Il regex funziona sopra, ma con le altre modifiche suggerite sopra:

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

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

2
in php per operazioni su stringa l'ordinamento dei parametri è $ pagliaio, $ ago. queste funzioni sono all'indietro e agiscono come funzioni di matrice in cui l'ordinamento è in realtà $ ago, $ pagliaio.
Andy,

29

Questa domanda ha già molte risposte, ma in alcuni casi puoi accontentarti di qualcosa di più semplice di tutte. Se la stringa che stai cercando è nota (codificata), puoi usare espressioni regolari senza virgolette, ecc.

Controlla se una stringa inizia con "ABC":

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

termina con "ABC":

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

Nel mio caso semplice, volevo verificare se una stringa termina con una barra:

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

Il vantaggio: poiché è molto breve e semplice, non è necessario definire una funzione (come endsWith() ) come mostrato sopra.

Ma ancora una volta: questa non è una soluzione per tutti i casi, solo questa molto specifica.


non è necessario codificare la stringa. la regex può essere dinamica.
Ryan,

2
È vero, ma se la stringa non è hardcoded, devi scappare. Attualmente ci sono 2 risposte su questa domanda che lo fanno. Questo è facile, ma complica un po 'il codice. Quindi il mio punto era che per casi molto semplici, dove l'hardcoding è possibile, puoi mantenerlo semplice.
noamtm,

1
Inoltre non devi sfuggire alla barra, puoi avvolgere la regex in qualche altro personaggio, come @, in modo che la barra ( /) non debba essere sfuggita. Vedi l'esempio n. 3 qui: php.net/manual/en/function.preg-match.php .
cjbarth,

Grazie @cjbarth. Modificata la mia risposta di conseguenza. A proposito, "#" è l'esempio fornito in php.net/manual/en/regexp.reference.delimiters.php quando si ha a che fare con una barra.
noamtm,

23

Se la velocità è importante per te, prova questo (credo che sia il metodo più veloce)

Funziona solo per le stringhe e se $ pagliaio ha solo 1 carattere

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

1
questa è probabilmente la risposta più efficace perché non usa alcuna funzione come extra, solo la solita stringa ...

Dovrebbe probabilmente verificare se la stringa ha almeno un carattere e ha i due parametri scambiati
a1an

1
Creativo. Aghi che contengono pagliai. A proposito c'è un brutto declino con:, endsWithChar('','x')ma il risultato è corretto
Tino

18

Ecco due funzioni che non introducono una stringa temporanea, che potrebbero essere utili quando gli aghi sono sostanzialmente grandi:

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

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

2
+1 Funziona dalla migliore risposta di PHP5.1 e IMHO. Ma endsWidthdovrebbe fare return $needle==='' || substr_compare(... quindi funziona come previsto per il -strlen($needle)===0quale, senza la correzione, fa endsWith('a','')ritornofalse
Tino

@Tino Grazie ... Sento che in substr_compare()realtà è un bug , quindi ho aggiunto un PR per risolverlo :)
Ja 21ck

3
La chiamata endsWith('', 'foo')attiva un avviso: "substr_compare (): la posizione iniziale non può superare la lunghezza della stringa iniziale". Forse è un altro bug substr_compare(), ma per evitarlo, è necessario un controllo preliminare come ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_

@gx_ Non è necessario rallentare con più codice. Basta usare return $needle === '' || @substr_compare(.. per sopprimere questo avviso.
Tino,

17

Estremità più veloci Con la soluzione ():

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

Prova delle prestazioni:

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

Risultati benchmark:

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

3
+1 per il tempo dedicato a confrontare soluzioni diverse e confrontarle effettivamente! dovresti anche menzionare quale versione di PHP hai usato, poiché le ottimizzazioni vengono fatte man mano che la lingua si evolve! Ho visto notevoli miglioramenti nelle funzioni di confronto delle stringhe da una versione PHP all'altra :)
Christophe Deliens,

1
riecheggiando @ChristopheDeliens e la sua richiesta di fornire la versione di PHP. Ho eseguito il test su 7.3.2 e ho ottenuto risultati simili FWIW.
Jeff,

16

Mi rendo conto che questo è stato finito, ma potresti voler guardare strncmp in quanto ti permette di mettere la lunghezza della stringa da confrontare, quindi:

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

come faresti a finire con questo?
Aprire il

@Mark - puoi guardare la risposta accettata, ma preferisco usare strncmp principalmente perché penso che sia più sicuro.
James Black,

Intendo con strncmp in particolare. Non è possibile specificare un offset. Ciò significherebbe che il tuo fine con la funzione dovrebbe utilizzare un metodo completamente diverso.
Aprire il

@ Marco - Per EndsWith vorrei solo usare strrpos ( php.net/manual/en/function.strrpos.php ), ma, in generale, ogni volta che si va ad utilizzare strncmp strcmp è probabilmente una scelta più sicura.
James Black,

11

Puoi usare strposestrrpos

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

1
Dovresti usare triple uguale qui in strpos($sHaystack, $sNeedle) == 0questo modo strpos($sHaystack, $sNeedle) === 0? Vedo un bug, quando viene false == 0valutato true.
Kalyan,

11

Ecco una versione sicura multi-byte della risposta accettata, funziona benissimo per le stringhe 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);
}

2
Sono abbastanza sicuro che questo è solo uno spreco di CPU. tutto quello che devi controllare, per StarstWith e EndsWith, è solo verificare che i byte corrispondano, ed è esattamente quello che sta facendo la risposta accettata. questo 1 fa perdere tempo a calcolare il numero di caratteri utf8 dell'ago e dove si trova la posizione dell'ennesimo carattere utf8 del pagliaio .. penso, senza essere sicuro al 100%, questo è solo uno spreco di CPU. puoi trovare un vero e proprio caso di prova in cui la risposta accettata fallisce e questo no?
Hanshenrik,

2
@hanshenrik - potrebbe succedere tra l'altro, nel caso molto raro quando cerchi una stringa che contenga gli stessi byte di un UTF8 ma con la metà dell'ultimo carattere mancante. Ad esempio, hai unicode C5 91 (lettera "ő") e cerchi C5 (lettera "Å") che non dovrebbe darti una corrispondenza. D'altra parte, certo, perché dovresti cercare un pagliaio utf per un ago non utf ... Ma per i controlli a prova di proiettile, questo deve essere considerato una possibilità.
dkellner,

In startsWithesso dovrebbe essere$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen

2
@ThomasKekeisen Grazie, risolto.
Vahid Amiri,

8

Fodere di linea brevi e facili da capire senza espressioni regolari.

startWith () è semplice.

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

limitsWith () usa lo strrev () leggermente elaborato e lento:

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

@FrancescoMM: strpos non è lo "strumento giusto" ... Perché? Quali sono gli "strumenti giusti" allora? EDIT: ho letto la tua risposta qui sotto. Pensavo che programmare fosse come un'invenzione usando le risorse che hai ... Quindi non c'è giusto o sbagliato ... solo lavorare o non funzionare ... le prestazioni sono secondarie.
Fr0zenFir

"perché è uno strumento per la ricerca, non per il confronto?" Cit. Aristoteles
FrancescoMM,

7

Concentrandosi sugli avvii, se si è sicuri che le stringhe non siano vuote, l'aggiunta di un test sul primo carattere, prima del confronto, della tensione, ecc., Velocizza un po 'le cose:

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

È in qualche modo (20% -30%) più veloce. L'aggiunta di un altro test del carattere, come $ haystack {1} === $ ago {1} non sembra accelerare molto le cose, potrebbe persino rallentare.

===sembra più veloce di == Operatore condizionale (a)?b:csembra più veloce diif(a) b; else c;


Per quelli che chiedono "perché non usare strpos?" chiamando altre soluzioni "lavoro inutile"


strpos è veloce, ma non è lo strumento giusto per questo lavoro.

Per capire, ecco una piccola simulazione come esempio:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

Cosa fa "dentro" il computer?

    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

Supponendo che strlen non esegua l'iterazione dell'intera stringa (ma anche in quel caso) ciò non è affatto conveniente.


C'è solo una velocità se i primi personaggi sono diversi.
Ja͢ck,

2
@Jack sì, certo, l'idea è che statisticamente ciò accade, quindi l'accelerazione è generalmente del 20% -30% sull'intero set di test (compresi i casi in cui non è diverso). Guadagni molto quando sono diversi e perdono molto poco quando non lo sono. In media guadagni quel 30% (varia a seconda del set, ma soprattutto ottieni velocità su test di grandi dimensioni)
FrancescoMM,

"ma non è lo strumento giusto per questo lavoro" ... Qualche citazione?
Fr0zenFir

1
WTF. Ho elencato di seguito tutto il processo a chi dovrei citare, più di questo? Utilizzeresti una funzione che cerca fino alla fine di una stringa per dirti che il carattere del pugno non è una "a"? Fallo a chi importa? Non è lo strumento giusto perché è uno strumento per la ricerca, non per il confronto, non è necessario citare Aristotele per affermare l'ovvio!
FrancescoMM,

6

Spero che la risposta di seguito possa essere efficace e anche semplice:

$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

Di solito finisco con una libreria come underscore-php in questi giorni.

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 libreria è piena di altre utili funzioni.


6

La risposta di mpen è incredibilmente completa, ma, sfortunatamente, il benchmark fornito ha una supervisione molto importante e dannosa.

Poiché ogni byte negli aghi e nei covoni di fieno è completamente casuale, la probabilità che una coppia ago-pagliaio differisca sul primo byte è del 99.609375%, il che significa che, in media, circa 99609 delle 100000 coppie differiranno sul primo byte . In altre parole, il benchmark è fortemente distorto verso startswithimplementazioni che controllano esplicitamente il primo byte, comestrncmp_startswith2 lo è.

Se il ciclo di generazione del test viene invece implementato come segue:

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

i risultati del benchmark raccontano una storia leggermente diversa:

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

Naturalmente, questo punto di riferimento potrebbe non essere ancora perfettamente imparziale, ma verifica l'efficienza degli algoritmi quando vengono forniti anche aghi parzialmente corrispondenti.


5

in breve:

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

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

5

Solo una raccomandazione:

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

Quella riga aggiuntiva, confrontando il primo carattere delle stringhe, può far tornare immediatamente il caso falso , rendendo molti dei tuoi confronti molto più veloci (7 volte più veloce quando ho misurato). Nel vero caso non paghi praticamente nessun prezzo in termini di prestazioni per quella singola linea, quindi penso che valga la pena includerlo. (Inoltre, in pratica, quando si verificano molte stringhe per un blocco iniziale specifico, la maggior parte dei confronti fallisce poiché in un caso tipico si sta cercando qualcosa.)


2
Bug nel tuo codice: startsWith("123", "0")true
Tino il

Sì, male! $ Controllo è successo. Scusate! (Volevo solo illustrare il concetto nella riga 3)
dkellner,

4

La substrfunzione può tornare falsein molti casi speciali, quindi ecco la mia versione, che affronta questi problemi:

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
}

Test ( truesignifica buono):

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

Inoltre, substr_comparevale la pena cercare anche la funzione. http://www.php.net/manual/en/function.substr-compare.php



4

Lo farei così

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

Dimenticare di restituire false se non corrisponde. L'errore errato come il valore di ritorno di una funzione non dovrebbe essere "assunto", ma so cosa stai cercando almeno rispetto ad altre risposte.
Spoo,

3

Basato sulla risposta di James Black, ecco le sue estremità Con la versione:

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: ho scambiato la parte if-else con la funzione start di James Black con la funzione, perché strncasecmp è in realtà la versione senza distinzione tra maiuscole e minuscole di strncmp.


2
Nota che strrev()è creativo ma molto costoso, soprattutto se hai stringhe di parole ... 100Kb.
Alexis Wilke,

Usa ===invece di ==per essere sicuro. 0è uguale a molte cose in PHP.
nawfal,

3

Perché non il seguente?

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

Produzione:

Valore trovato all'inizio di valuehaystack!

Tieni a mente, strpos che restituirà falso se l'ago non è stato trovato nel pagliaio e restituirà 0 se, e solo se, l'ago è stato trovato nell'indice 0 (AKA all'inizio).

Ed ecco finisce 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 . "!";
}

In questo scenario non è necessaria una funzione startWith () come

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

restituirà vero o falso con precisione.

Sembra strano che sia così semplice con tutte le funzioni jolly che imperversano qui.


3
Sembra strano che se stai cercando "xy" all'interno della stringa "abcdefghijklmxyz" invece di confrontare semplicemente "x" con "a" e di restituire FALSE, cerchi tutti i caratteri da "a" a "m" e poi trovi "xy" all'interno della stringa e alla fine si restituisce FALSO perché la sua posizione non è zero! Questo è ciò che stai facendo ed è strano e selvaggio rispetto a qualsiasi altra funzione dilagante qui.
FrancescoMM,

La semplicità sta nella digitazione, non nella logica.
Kade Hafen,

Non è tanto la logica, è la possibile ottimizzazione che Francsco stava sottolineando. L'uso strpos()sarà lento tranne quando corrisponde. strncmp()sarebbe molto meglio in questo caso.
Alexis Wilke,

Quando si svolgono funzioni di livello così basso, in genere si desidera scegliere la soluzione più ottimizzata per la velocità, non importa quanto complessa, poiché questa verrà chiamata milioni di volte. Ogni microsecondo che guadagni o perdi qui farà davvero la differenza. Quindi meglio risolverlo (e poi dimenticare la complessità, ora che hai la funzione), invece di cercare gli sguardi e perdere una quantità orribile di tempo in seguito quando non sai nemmeno cosa è andato storto. Immagina di controllare una stringa da 2 GB che non corrisponde.
dkellner,

3

Molte delle risposte precedenti funzioneranno altrettanto bene. Tuttavia, questo è probabilmente il più breve possibile e farlo fare quello che desideri. Afferma semplicemente che ti piacerebbe che "ritornasse vero". Quindi ho incluso soluzioni che restituiscono il vero / falso booleano e il vero / falso testuale.

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

Vero. Tuttavia, Peter stava chiedendo una funzione che avrebbe funzionato con le stringhe di caratteri. Tuttavia, ho aggiornato la mia risposta per placarti.
wynshaft,

Dopo la modifica la tua soluzione ora è completamente obsoleta. Ritorna 'true'e 'false'come stringhe, che sono entrambe truein senso booleano. È un buon modello per qualcosa come underhanded.xcott.com ;)
Tino,

Bene, Peter ha appena dichiarato che voleva che tornasse "vero". Quindi ho pensato di restituire quello che mi aveva chiesto. Ho aggiunto entrambe le versioni, nel caso non fosse quello che voleva.
Wynshaft,

2

Puoi anche usare espressioni regolari:

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

3
$ ago dovrebbe essere evitato con preg_quote($needle, '/').
Timo Tijhof,

2

No-copy e no-intern-loop:

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

questo dovrebbe essere molto più veloce dell'implementazione di MrHus! potrei confrontarlo
hanshenrik

1

Ecco una soluzione efficiente per PHP 4. Puoi ottenere risultati più veloci se su PHP 5 usando substr_compareinvece di 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;
}

0

È possibile utilizzare la funzione fnmatch per questo.

// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);

attenzione, non binario sicuro, e nemmeno sicuro contro gli aghi contenenti caratteri jolly = /
hanshenrik
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.