Funções beginWith () e endsWith () no PHP

1481

Como posso escrever duas funções que pegam uma string e retornam se ela começa com o caractere / string especificado ou termina com ela?

Por exemplo:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Click Voto a favor
fonte
19
Veja a classe Str do Laravel, beginWith () e endsWith () para métodos bem testados . Foram encontrados casos extremos, portanto, o uso generalizado desse código é uma vantagem.
Gras Double
1
Você pode achar s($str)->startsWith('|')e s($str)->endsWith('}')útil, conforme encontrado nesta biblioteca autônoma .
caw
3
Aviso: a maioria das respostas aqui não é confiável em codificações de vários bytes, como UTF-8.
Álvaro González
Seguindo o meu comentário acima, você pode se certificar de usar a versão mais recente (a partir de hoje, 5.4 ). Notavelmente, o beginWith () foi otimizado para grandes seqüências de feno.
Gras Double

Respostas:

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

function endsWith($haystack, $needle)
{
    $length = strlen($needle);
    if ($length == 0) {
        return true;
    }

    return (substr($haystack, -$length) === $needle);
}

Use isso se você não quiser usar uma regex.

MrHus
fonte
16
+1 Isso é mais limpo que a resposta aceita. Além disso, $lengthnão é necessário na última linha do endsWith().
demasiado php
13
Eu diria endsWith ('foo', '') == false é o comportamento correto. Porque foo não termina com nada. 'Foo' termina com 'o', 'oo' e 'Foo'.
MrHus
125
É possível escrever muito mais curto:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj
12
Você pode evitar o ifpor completo, passando $lengthcomo o terceiro parâmetro para substr: return (substr($haystack, -$length, $length);. Isso lida com o caso $length == 0retornando uma string vazia e não o todo $haystack.
Mxxk
20
@MrHus eu recomendo usar funções de segurança multi-byte, por exemplo mb_strlen e mb_substr
19Gerhard85
1025

Você pode usar a substr_comparefunção para verificar o início e o término com:

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

Essa deve ser uma das soluções mais rápidas do PHP 7 ( script de benchmark ). Testado em palheiros de 8 KB, agulhas de vários comprimentos e maletas cheias, parciais e sem correspondência. strncmpé um toque mais rápido para começar, mas não pode verificar as pontas.

Salman A
fonte
74
Esta resposta chegou ao Daily WTF! : D Veja thedailywtf.com/articles/…
Wim ten Brink
Observe que os comentários @DavidWallace e @FrancescoMM se aplicam a uma versão mais antiga desta resposta. A resposta atual usa strrposqual (deveria) falhar imediatamente se a agulha não coincidir com o início do palheiro.
Salman A
2
Eu não entendo. Baseado em php.net/manual/pt/function.strrpos.php : "Se o valor for negativo, a pesquisa começará com muitos caracteres do final da string, pesquisando para trás." Isso parece indicar que estamos começando no caractere 0 (devido a -strlength($haystack)) e pesquisando para trás a partir daí? Isso não significa que você não está procurando nada? Eu também não entendo as !== falsepartes disso. Eu estou supondo que isso depende de uma peculiaridade do PHP, onde alguns valores são "verdadeiros" e outros "falsos", mas como isso funciona nesse caso?
Welbog
3
@ Welbog: por exemplo, palheiro = xxxyyyagulha = yyye o uso strrposda pesquisa começa desde o início x. Agora não temos uma correspondência bem-sucedida aqui (encontrado x em vez de y) e não podemos mais voltar atrás (estamos no início da string), a pesquisa falha imediatamente . Sobre o uso !== false- strrposno exemplo acima, retornará 0 ou false e não outro valor. Da mesma forma, strposno exemplo acima, pode retornar $temp(a posição esperada) ou falsa. Eu fui com !== falsea consistência, mas você poderia usar === 0e === $tempnas funções, respectivamente.
Salman Um
8
@spoo já foi estabelecido que strpos === 0 é uma solução terrível se o palheiro for grande e a agulha não existir.
Salman A
243

Atualizado 23 de agosto de 2016

Funções

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

Testes

echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
    if($i % 2500 === 0) echo '.';
    $test_cases[] = [
        random_bytes(random_int(1, 7000)),
        random_bytes(random_int(1, 3000)),
    ];
}
echo "done!\n";


$functions = ['substr_startswith', 'preg_match_startswith', 'substr_compare_startswith', 'strpos_startswith', 'strncmp_startswith', 'strncmp_startswith2'];
$results = [];

foreach($functions as $func) {
    $start = microtime(true);
    foreach($test_cases as $tc) {
        $func(...$tc);
    }
    $results[$func] = (microtime(true) - $start) * 1000;
}

asort($results);

foreach($results as $func => $time) {
    echo "$func: " . number_format($time, 1) . " ms\n";
}

Resultados (PHP 7.0.9)

(Classificado da mais rápida para a mais lenta)

strncmp_startswith2: 40.2 ms
strncmp_startswith: 42.9 ms
substr_compare_startswith: 44.5 ms
substr_startswith: 48.4 ms
strpos_startswith: 138.7 ms
preg_match_startswith: 13,152.4 ms

Resultados (PHP 5.3.29)

(Classificado da mais rápida para a mais lenta)

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

beginwith_benchmark.php

mpen
fonte
3
Se as seqüências de caracteres não estiverem vazias, como nos seus testes, isso é realmente de alguma forma (20 a 30%) mais rápido: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}adicionei uma resposta abaixo.
28813 FrancescoMM
3
@ Jonny Porque 110 é menor que 133 ... ??
MPEN
2
Droga, eu não sei o que passou pela minha cabeça naquele momento. Alegre a falta de sono.
Jronny
1
@pen, eu não notei o elefante em tudo :(
Visman
1
Esses testes não são bons para testar o desempenho. O que você está fazendo é usar cordas aleatórias como agulha. Em 99,99% dos casos, NÃO haverá correspondência. A maioria das funções será encerrada após o primeiro byte correspondente. E os casos em que uma correspondência é encontrada? Qual função leva menos tempo para concluir uma correspondência bem-sucedida? E os casos em que 99% da agulha correspondem, mas não os últimos bytes? Qual função leva menos tempo para concluir nenhuma correspondência?
Salman A
137

Todas as respostas até agora parecem fazer um monte de trabalho desnecessário, strlen calculations, string allocations (substr), etc. A 'strpos'e 'stripos'funções retornam o índice da primeira ocorrência $needleem $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
fonte
2
endsWith()função tem um erro. Sua primeira linha deve ser (sem o -1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma
6
A coisa strlen () não é desnecessária. Caso a string não comece com a agulha especificada, seu código varrerá desnecessariamente todo o palheiro.
precisa saber é o seguinte
5
@ Marcos sim, verificando apenas o começo é muito mais rápido, especialmente se você está fazendo algo como a verificação de tipos MIME (ou qualquer outro lugar onde a corda é obrigado a ser grande)
chacham15
2
@mark Eu fiz alguns benchmarks com palheiro de 1000 char e agulha de 10 ou 800 char e strpos foi 30% mais rápido. Será que os seus pontos de referência antes de afirmar que algo é mais rápido ou não ...
wdev
7
Você deve citar fortemente a agulha como strpos($haystack, "$needle", 0)se houvesse alguma chance de ela ainda não ser uma corda (por exemplo, se é proveniente json_decode()). Caso contrário, o comportamento padrão [ímpar] de strpos()pode causar resultados inesperados: " Se a agulha não for uma sequência, ela será convertida em um número inteiro e aplicada como o valor ordinal de um caractere. "
quietmint 3/12/12
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édito para :

Verifique se uma string termina com outra string

Verifique se uma sequência começa com outra sequência

KdgDev
fonte
1
strtolower não é a melhor maneira de criar funções que não diferenciam maiúsculas de minúsculas. Em alguns locais, o revestimento é mais complexo do que apenas superior e inferior.
Sander Rijken
8
Vejo queixas e nenhuma solução ... Se você vai dizer que é ruim, deve dar um exemplo de como deve ser também.
KdgDev 14/05/2009
2
@WebDevHobo: foi por isso que adicionei uma resposta um dia antes do seu comentário. Para seu código, o strcasecmp era realmente a coisa certa a se fazer.
Sander Rijken
29

O regex funciona acima, mas com os outros ajustes também sugeridos acima:

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

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
tridian
fonte
2
no php para operações de string, a ordem dos parâmetros é $ haystack, $ needle. essas funções são invertidas e agem como funções de matriz em que o pedido é realmente $ needle, $ haystack.
Andy
29

Essa pergunta já tem muitas respostas, mas em alguns casos você pode se contentar com algo mais simples do que todas elas. Se a sequência que você procura for conhecida (codificada), você poderá usar expressões regulares sem citar etc.

Verifique se uma sequência começa com 'ABC':

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

termina com 'ABC':

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

No meu caso simples, eu queria verificar se uma string termina com barra:

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

A vantagem: como é muito curto e simples, você não precisa definir uma função (como endsWith() ) como mostrado acima.

Mas, novamente - isso não é uma solução para todos os casos, apenas esse muito específico.

noamtm
fonte
você não precisa codificar a string. o regex pode ser dinâmico.
Ryan
2
@self true, mas se a string não estiver codificada, você deve escapar dela. Atualmente, existem 2 respostas sobre essa pergunta que o fazem. Isso é fácil, mas complica um pouco o código. Então, o que quero dizer é que, em casos muito simples, onde a codificação é possível, você pode mantê-la simples.
Noamtm
1
Você também não precisa escapar da barra, pode envolver o regex em algum outro caractere, como @, para que a barra ( /) não precise ser escapada. Veja o Exemplo 3 aqui: php.net/manual/en/function.preg-match.php .
cjbarth
Obrigado @cjbarth. Mudou minha resposta de acordo. BTW, "#" é o exemplo dado em php.net/manual/en/regexp.reference.delimiters.php ao lidar com uma barra.
30818 noamtm
23

Se a velocidade é importante para você, tente isso (acredito que seja o método mais rápido)

Funciona apenas para strings e se $ haystack tiver apenas 1 caractere

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
fonte
1
esta é provavelmente a resposta mais eficiente porque não usar qualquer função como extra, apenas corda costume ...
Ele provavelmente deve verificar se string tem pelo menos um caractere e tem dois parâmetros trocados
a1an
1
Criativo. Agulhas que contêm palheiros. BTW há algum declínio feio com: endsWithChar('','x'), mas o resultado está correto
Tino
18

Aqui estão duas funções que não introduzem uma sequência temporária, que pode ser útil quando as agulhas são substancialmente grandes:

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

function endsWith($haystack, $needle)
{
    return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
Ja͢ck
fonte
2
+1 Funciona desde PHP5.1 e IMHO melhor resposta. Mas endsWidthdeve fazer return $needle==='' || substr_compare(... por isso funciona como esperado para -strlen($needle)===0que, sem a correção, faz endsWith('a','')retornofalse
Tino
@Tino Graças ... Eu sinto que é um bug na substr_compare()verdade, então eu adicionei um PR a correção que :)
Jack
3
A chamada endsWith('', 'foo')dispara um aviso: “substr_compare (): a posição inicial não pode exceder o comprimento inicial da string”. Talvez esse seja outro problema substr_compare(), mas, para evitá-lo, você precisa de uma verificação prévia como ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_
@gx_ Não há necessidade de desacelerar com mais código. Basta usar return $needle === '' || @substr_compare(.. para suprimir esse aviso.
Tino
17

Solução mais rápida de extremidades com ():

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

Referência:

# This answer
function endsWith($haystack, $needle) {
    return substr($haystack,-strlen($needle))===$needle;
}

# Accepted answer
function endsWith2($haystack, $needle) {
    $length = strlen($needle);

    return $length === 0 ||
    (substr($haystack, -$length) === $needle);
}

# Second most-voted answer
function endsWith3($haystack, $needle) {
    // search forward starting from end minus needle length characters
    if ($needle === '') {
        return true;
    }
    $diff = \strlen($haystack) - \strlen($needle);
    return $diff >= 0 && strpos($haystack, $needle, $diff) !== false;
}

# Regex answer
function endsWith4($haystack, $needle) {
    return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
}

function timedebug() {
    $test = 10000000;

    $time1 = microtime(true);
    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith('TestShortcode', 'Shortcode');
    }
    $time2 = microtime(true);
    $result1 = $time2 - $time1;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith2('TestShortcode', 'Shortcode');
    }
    $time3 = microtime(true);
    $result2 = $time3 - $time2;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith3('TestShortcode', 'Shortcode');
    }
    $time4 = microtime(true);
    $result3 = $time4 - $time3;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith4('TestShortcode', 'Shortcode');
    }
    $time5 = microtime(true);
    $result4 = $time5 - $time4;

    echo $test.'x endsWith: '.$result1.' seconds # This answer<br>';
    echo $test.'x endsWith2: '.$result4.' seconds # Accepted answer<br>';
    echo $test.'x endsWith3: '.$result2.' seconds # Second most voted answer<br>';
    echo $test.'x endsWith4: '.$result3.' seconds # Regex answer<br>';
    exit;
}
timedebug();

Resultados de referência:

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
fonte
3
+1 por reservar um tempo para comparar soluções diferentes e compará-las de fato! você também deve mencionar qual versão do PHP você usou, pois as otimizações são feitas à medida que a linguagem evolui! Eu vi melhorias dramáticas sobre as funções de comparação de string de uma versão PHP para outro :)
Christophe Deliens
1
ecoando @ChristopheDeliens e seu pedido para fornecer a versão PHP. Eu executei seu teste no 7.3.2 e obtive resultados semelhantes no FWIW.
19419 Jeff
16

Sei que isso foi concluído, mas você pode olhar para o strncmp, pois ele permite comparar o comprimento da string, portanto:

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
fonte
como você terminaria com isso?
MPEN
@ Mark - você pode olhar para a resposta aceita, mas eu prefiro usar o strncmp principalmente porque acho que é mais seguro.
James Black
Quero dizer com strncmp especificamente. Você não pode especificar um deslocamento. Isso significaria que a função endsWith teria que usar um método completamente diferente.
MPEN
@ Mark - For endsWith Eu usaria apenas strrpos ( php.net/manual/en/function.strrpos.php ), mas, geralmente, sempre que você usa strcmp, strncmp é provavelmente uma opção mais segura.
James Black
11

Você pode usar strposestrrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
Lex
fonte
1
Você deve usar iguais triplos aqui strpos($sHaystack, $sNeedle) == 0como este strpos($sHaystack, $sNeedle) === 0? Eu vejo um bug, quando false == 0avalia para true.
precisa
11

Aqui está uma versão segura de vários bytes da resposta aceita, ela funciona bem para cadeias 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
fonte
2
tenho certeza de que isso é apenas um desperdício de CPU. tudo o que você precisa verificar, para StarstWith e EndsWith, é apenas verificar se os bytes correspondem e é exatamente isso que a resposta aceita está fazendo. isso desperdiça tempo calculando o número de caracteres utf8 da agulha e onde está a posição do n-ésimo caractere utf8 do palheiro .. acho que, sem ter 100% de certeza, isso é apenas um desperdício de CPU. você pode criar um caso de teste real em que a resposta aceita falhe, e isso não?
hanshenrik
2
@hanshenrik - isso poderia acontecer, no caso muito raro, quando você procura uma string que contenha os mesmos bytes que um UTF8, mas com metade do último caractere ausente. Por exemplo, você tem o unicode C5 91 (letra "ő") e procura o C5 (letra "Å"), que não deve corresponder. Por outro lado, com certeza, por que você procuraria em um palheiro utf por uma agulha não utf ... Mas para verificações à prova de balas, isso deve ser considerado uma possibilidade.
dkellner
Em startsWithque deveria ser$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen
2
@ThomasKekeisen Obrigado, corrigiu.
Vahid Amiri
8

Linhas de linha curtas e fáceis de entender, sem expressões regulares.

começa com () é simples.

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

endsWith () usa o strrev () levemente sofisticado e lento:

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
Dan
fonte
@FrancescoMM: strpos não é a "ferramenta certa" ... Por quê? Quais são as "ferramentas certas" então? EDIT: Eu li sua resposta abaixo. Eu pensei que a programação é como invenção usando os recursos que você tem .. Portanto, não há certo ou errado ... apenas trabalhando ou não trabalhando ... o desempenho é secundário.
Fr0zenFyr
"porque é uma ferramenta para pesquisar, não para comparar?" Cit. Aristoteles
FrancescoMM
7

Focalizando o startwith, se você tiver certeza de que as strings não estão vazias, adicionar um teste no primeiro caractere, antes da comparação, do strlen etc., acelera um pouco as coisas:

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

É de alguma forma (20% -30%) mais rápido. Adicionar outro teste de char, como $ haystack {1} === $ needle {1}, não parece acelerar muito as coisas, pode até diminuir a velocidade.

===parece mais rápido que o == operador condicional (a)?b:cparece mais rápido queif(a) b; else c;


Para aqueles que perguntam "por que não usar strpos?" chamar outras soluções de "trabalho desnecessário"


O strpos é rápido, mas não é a ferramenta certa para este trabalho.

Para entender, aqui está uma pequena simulação como exemplo:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

O que o computador faz "dentro"?

    With strccmp, etc...

    is a===b? NO
    return false



    With strpos

    is a===b? NO -- iterating in haysack
    is a===c? NO
    is a===d? NO
    ....
    is a===g? NO
    is a===g? NO
    is a===a? YES
    is 1===1? YES -- iterating in needle
    is 2===3? YES
    is 4===4? YES
    ....
    is 8===8? YES
    is c===x? NO: oh God,
    is a===1? NO -- iterating in haysack again
    is a===2? NO
    is a===3? NO
    is a===4? NO
    ....
    is a===x? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    is a===b? NO
    ...
    ... may many times...
    ...
    is a===b? NO
    is a===a? YES -- iterating in needle again
    is 1===1? YES
    is 2===3? YES
    is 4===4? YES
    is 8===8? YES
    is c===c? YES YES YES I have found the same string! yay!
    was it at position 0? NOPE
    What you mean NO? So the string I found is useless? YEs.
    Damn.
    return false

Supondo que strlen não itere toda a string (mas mesmo nesse caso) isso não é conveniente.

FrancescoMM
fonte
Existe apenas uma velocidade se os primeiros caracteres forem diferentes.
Ja͢ck
2
@ Jack: sim, é claro, a idéia é que estatisticamente isso aconteça; portanto, a aceleração é geralmente entre 20% e 30% em todo o conjunto de testes (incluindo casos em que não é diferente). Você ganha muito quando são diferentes e perde muito pouco quando não são. Na média você ganha que 30% (varia de acordo com set, mas principalmente você ganhar velocidade em grandes testes)
FrancescoMM
"mas não é a ferramenta certa para este trabalho" ... Alguma citação?
Fr0zenFyr
1
WTF. Listei todo o processo abaixo de quem devo citar, mais do que isso? Você usaria uma função que pesquisa até o final de uma string para dizer que o caractere punho não é um 'a'? Quem se importa? Não é a ferramenta certa, porque é uma ferramenta para pesquisar, não para comparar, não há necessidade de citar Aristoteles para afirmar o óbvio!
FrancescoMM
6

Espero que a resposta abaixo seja eficiente e também simples:

$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
fonte
6

Normalmente, acabo indo a uma biblioteca como o underscore-php atualmente.

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   

A biblioteca está cheia de outras funções úteis.

yuvilio
fonte
6

A resposta da mpen é incrivelmente completa, mas, infelizmente, o benchmark fornecido tem uma supervisão muito importante e prejudicial.

Como cada byte em agulhas e palheiros é completamente aleatório, a probabilidade de um par de agulha e palheiro diferir no primeiro byte é de 99,609375%, o que significa que, em média, cerca de 99609 dos 100000 pares diferem no primeiro byte . Em outras palavras, a referência é fortemente influenciada por startswithimplementações que verificam o primeiro byte explicitamente, assim como strncmp_startswith2.

Se o loop de geração de teste for implementado da seguinte maneira:

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

os resultados do benchmark contam uma história um pouco diferente:

strncmp_startswith: 223.0 ms
substr_startswith: 228.0 ms
substr_compare_startswith: 238.0 ms
strncmp_startswith2: 253.0 ms
strpos_startswith: 349.0 ms
preg_match_startswith: 20,828.7 ms

Obviamente, essa referência ainda pode não ser perfeitamente imparcial, mas testa a eficiência dos algoritmos quando também são fornecidas agulhas parcialmente correspondentes.

Veeno
fonte
5

em resumo:

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
fonte
5

Apenas uma recomendação:

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

Essa linha extra, comparando o primeiro caractere das seqüências, pode fazer com que o caso falso retorne imediatamente , tornando muitas das suas comparações muito mais rápidas (7x mais rápidas quando eu medi). No caso real, você não paga praticamente nenhum preço em desempenho por essa linha única, então acho que vale a pena incluir. (Além disso, na prática, quando você testa muitas seqüências de caracteres para um pedaço inicial específico, a maioria das comparações falha desde que, em um caso típico, você está procurando algo.)

dkellner
fonte
2
Erro no seu código: startsWith("123", "0")givestrue
Tino
Sim, aconteceu uma verificação ruim! $. Desculpa! (Só queria ilustrar o conceito na linha 3)
dkellner
4

A substrfunção pode retornar falseem muitos casos especiais, então aqui está minha versão, que lida com esses problemas:

function startsWith( $haystack, $needle ){
  return $needle === ''.substr( $haystack, 0, strlen( $needle )); // substr's false => empty string
}

function endsWith( $haystack, $needle ){
  $len = strlen( $needle );
  return $needle === ''.substr( $haystack, -$len, $len ); // ! len=0
}

Testes ( truesignifica bom):

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

Além disso, a substr_comparefunção também vale a pena procurar. http://www.php.net/manual/en/function.substr-compare.php

Biziclop
fonte
4

Isso pode funcionar

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

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

user507410
fonte
4

Eu faria assim

     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 Keiser
fonte
Esquecer de retornar false se não corresponder. Errgo incorreto como é o valor de retorno de uma função não deve ser 'assumido', mas eu sei o que você está procurando, pelo menos em comparação com outras respostas.
Spoo
3

Baseado na resposta de James Black, aqui estão os seus fins

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: Troquei a parte if-else pela função startWith de James Black, porque strncasecmp é realmente a versão que diferencia maiúsculas de minúsculas do strncmp.

bobo
fonte
2
Observe que o arquivo strrev()é criativo, mas muito caro, especialmente se você tiver seqüências de digamos ... 100 KB.
Alexis Wilke
Use em ===vez de ==ter certeza. 0é igual a muitas coisas em PHP.
Nawfal
3

Por que não o seguinte?

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

Resultado:

Valor encontrado no início do palheiro de valores!

Lembre-se de strposque retornará falso se a agulha não foi encontrada no palheiro e retornará 0 se, e somente se, a agulha foi encontrada no índice 0 (AKA no início).

E aqui termina:

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

Nesse cenário, não há necessidade de uma função beginWith () como

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

retornará verdadeiro ou falso com precisão.

Parece estranho que seja simples assim, com todas as funções selvagens rodando desenfreadas aqui.

Kade Hafen
fonte
3
Parece estranho que, se você estiver procurando por "xy" dentro da string "abcdefghijklmxyz", em vez de comparar "x" a "a" e retornar FALSE, procure todos os caracteres de "a" a "m" e acabe encontrando "xy" dentro da string e, finalmente, você retorna FALSE porque a posição dela não é zero! É isso que você está fazendo, e é estranho e selvagem do que qualquer outra função desenfreada aqui.
FrancescoMM
A simplicidade está na digitação, não na lógica.
Kade Hafen
Não é tanto a lógica, é a possível otimização que Francsco estava apontando. O uso strpos()será lento, exceto quando corresponder. strncmp()seria muito melhor neste caso.
Alexis Wilke
Quando você executa essas funções de baixo nível, normalmente deseja a solução mais otimizada para velocidade, não importa quão complexa, pois isso será chamado milhões de vezes. Cada microssegundo que você ganha ou perde aqui fará uma diferença muito real. É melhor ajustar isso de vez em quando (e depois esquecer a complexidade, agora que você tem a função), em vez de procurar pela aparência e perder um tempo horrível depois, quando você nem sabe o que deu errado. Imagine verificar uma sequência de 2 GB que não corresponde.
Dkellner 03/09/19
3

Muitas das respostas anteriores funcionarão igualmente. No entanto, isso é possivelmente o mais curto possível e você pode fazer o que deseja. Você acabou de declarar que deseja "retornar verdadeiro". Portanto, incluí soluções que retornam booleano true / false e textual 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
fonte
Verdade. No entanto, Peter estava solicitando uma função que funcionasse com cadeias de caracteres. No entanto, atualizei minha resposta para apaziguar você.
wynshaft
Após a edição, sua solução agora está completamente obsoleta. Ele retorna 'true'e 'false'como strings, que são ambos trueno sentido booleano. No entanto, é um bom padrão para algo como underhanded.xcott.com ;)
Tino
Bem, Peter acabou de declarar que queria que fosse "verdadeiro". Então, pensei em devolver o que ele pediu. Eu adicionei as duas versões, apenas no caso de não ser o que ele queria.
wynshaft
2

Você também pode usar expressões regulares:

function endsWith($haystack, $needle, $case=true) {
  return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
Freeman
fonte
3
$ agulha deve ser escapada com preg_quote($needle, '/').
Timo Tijhof
2

Sem cópia e sem loop interno:

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
fonte
isso deve ser muito mais rápido que a implementação do MrHus! eu poderia
compará-
1

Aqui está uma solução eficiente para o PHP 4. Você pode obter resultados mais rápidos se estiver usando o PHP 5 em substr_comparevez 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
fonte
0

Você pode usar a função fnmatch para isso.

// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);
ya.teck
fonte
aviso, não é binário seguro e nem mesmo é seguro contra agulhas contendo caracteres curinga = /
hanshenrik