PHP'de startsWith () ve endsWith () işlevleri


1480

Belirtilen karakter / dize ile başlıyor veya onunla bitiyorsa, bir dize alıp geri dönen iki işlevi nasıl yazabilirim?

Örneğin:

$str = '|apples}';

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

19
Laravel en görün Str sınıfı için startswith () ve endswith () iyi test edilmiş yöntemlerle. Edge vakalarıyla karşılaşıldı, bu nedenle bu kodun yaygın kullanımı bir avantajdır.
Gras Double

1
Bu bağımsız kitaplıkta bulunan s($str)->startsWith('|')ve bulabileceğiniz s($str)->endsWith('}')faydalı olabilir .
caw

3
Uyarı: Buradaki yanıtların çoğu UTF-8 gibi çok baytlı kodlamalarda güvenilir değildir.
Álvaro González

Yukarıdaki yorumumu takiben, en son sürümü kullandığınızdan emin olabilirsiniz (bugün itibariyle, 5.4 ). Özellikle, startsWith () büyük samanlık dizeleri için optimize edilmiştir.
Gras Double

Yanıtlar:


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

Normal ifade kullanmak istemiyorsanız bunu kullanın.


16
+1 Bu, kabul edilen cevaptan daha temiz. Ayrıca, $lengthson satırında gerekli değildir endsWith().
Çok fazla php

13
EndsWith ('foo', '') == false doğru davranış olduğunu söyleyebilirim. Çünkü foo hiçbir şeyle bitmez. 'Foo', 'o', 'oo' ve 'Foo' ile biter.
MrHus

125
Çok daha kısa yazılabilir:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj

12
Sen önleyebilirsiniz ifgeçirerek tamamen $lengthüçüncü parametre olarak substr: return (substr($haystack, -$length, $length);. Bu $length == 0, bütünü değil boş bir dize döndürerek durumu ele alır $haystack.
mxxk

20
@MrHus Çok baytlı güvenli işlevler kullanmanızı öneririm, örn. Mb_strlen ve
mb_substr

1024

substr_compareBaşlangıç ​​ve bitiş noktalarını kontrol etmek için işlevi kullanabilirsiniz :

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

Bu PHP 7 ( benchmark script ) üzerindeki en hızlı çözümlerden biri olmalıdır . 8KB haystacks, çeşitli uzunluktaki iğneler ve tam, kısmi ve eşleşmeyen durumlara karşı test edilmiştir. strncmpbaşlangıç ​​için daha hızlı bir dokunuştur ancak bitiş noktalarını kontrol edemez.


74
Bu cevap Daily WTF'ye ulaştı! : D Bkz. Thedailywtf.com/articles/…
Wim ten Brink

@DavidWallace ve @FrancescoMM yorumlarının bu yanıtın eski bir sürümü için geçerli olduğunu lütfen unutmayın. Mevcut yanıt, strrposiğne samanlığın başlangıcıyla eşleşmezse hangisinin derhal başarısız olması gerektiğini kullanır .
Salman A

2
Anlamıyorum. Dayanarak php.net/manual/en/function.strrpos.php : "değeri negatif ise, arama yerine geriye arama, dizenin sonundan itibaren birçok karakter başlayacaktır." Bu, 0 karakterinden başladığımızı -strlength($haystack)ve oradan geriye doğru aradığımızı gösteriyor mu? Bu hiçbir şey aramayacağınız anlamına gelmiyor mu? Bunun !== falseparçalarını da anlamıyorum . Bu bazı değerler "doğruluk" ve diğerleri "falsy" olan PHP bir tuhaflık dayanıyor tahmin ediyorum ama bu durumda nasıl çalışır?
Welbog

3
@Welbog: örneğin haystack = xxxyyyneedle = yyyve strrposaramayı kullanmak ilkinden başlar x. Şimdi burada başarılı bir eşleşmemiz yok (y yerine x bulundu) ve artık geriye gidemiyoruz (dizginin başlangıcındayız) arama hemen başarısız oluyor . Kullanma hakkında !== false- strrposyukarıdaki örnekte 0 ya da yanlış değil, başka bir değer dönecektir. Benzer şekilde, strposyukarıdaki örnekte geri dönebilir $temp(beklenen konum) veya yanlış olabilir. Ben gittim !== falsetutarlılık için ancak kullanabilirsiniz === 0ve === $tempsırasıyla fonksiyonlarda.
Salman A

8
Samanlık büyükse ve iğne yoksa strpos === 0'ın korkunç bir çözüm olduğu zaten tespit edilmiştir.
Salman A

243

Güncelleme 23 Ağustos 2016

Fonksiyonlar

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

Testler

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

Sonuçlar (PHP 7.0.9)

(En hızlıdan en yavaşına)

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

Sonuçlar (PHP 5.3.29)

(En hızlıdan en yavaşına)

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
Dizeler boşsa, testlerinizde olduğu gibi, bu aslında bir şekilde (% 20-30) daha hızlıdır: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}Aşağıya bir cevap ekledim.
FrancescoMM

3
@Jronny Çünkü 110 133'ten az ... ??
mpen

2
Lanet olsun, o zaman kafama ne gitti bilmiyorum. Prolly uyku eksikliği.
Jronny

1
@mpen, ben fil hiç fark
etmedim

1
Bu testler performansı test etmede iyi değildir. Yaptığınız şey iğne olarak rastgele ip kullanmaktır. % 99,99 vakada HİÇBİR eşleşme olmayacaktır. İşlevlerin çoğu ilk bayt eşleştirildikten sonra çıkacaktır. Bir eşleşme bulunduğu durumlarda ne olur? Hangi fonksiyon başarılı eşleşmeyi tamamlamak için en az zaman alır? İğnenin% 99'unun eşleştiği ancak son birkaç baytın eşleşmediği durumlar ne olacak? Hangi fonksiyon eşleşmeyi sonuçlandırmak için en az zaman alır?
Salman A

137

Bütün cevaplar şu ana kadar gereksiz iş yükleri yapmak gibi görünüyor, strlen calculations, string allocations (substr)vb 'strpos've 'stripos'işlevleri ilk geçtiği dizinini döndürür $needleiçinde $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()işlevinde bir hata var. İlk satırı (-1 olmadan) olmalıdır:$expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma

6
Strlen () işlemi gereksiz değildir. Eğer ipin verilen iğne ile başlamaması durumunda, ur kodu gereksiz yere tüm samanlığı tarar.
AppleGrew

5
@Mark evet, sadece başlangıcı kontrol etmek çok daha hızlı, özellikle de MIME türlerini (veya dizenin büyük olması gereken başka bir yeri) kontrol etmek gibi bir şey yapıyorsanız
chacham15

2
@mark 1000 char haystack ve 10 veya 800 char iğne ile bazı kriterler yaptım ve strpos% 30 daha hızlıydı. Bir şeyin daha hızlı olup olmadığını belirtmeden önce kriterlerinizi yapın ...
wdev

7
Sen şiddetle gibi iğne alıntı düşünmelisiniz strpos($haystack, "$needle", 0)varsa herhangi (o geliyor eğer, örneğin şans zaten bir dize değil json_decode()). Aksi takdirde, [tek] varsayılan davranışı strpos()beklenmedik sonuçlara neden olabilir: " İğne bir dize değilse, bir tamsayıya dönüştürülür ve bir karakterin sıra değeri olarak uygulanır. "
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);
}

Kredi Alıcı :

Bir dizenin başka bir dizeyle bitip bitmediğini kontrol edin

Bir dizenin başka bir dizeyle başlayıp başlamadığını kontrol edin


1
strtolower, büyük / küçük harfe duyarsız işlevler yapmanın en iyi yolu değildir. Bazı bölgelerde gövde sadece üst ve alt kısımlardan daha karmaşıktır.
Sander Rijken

8
Şikayet görüyorum ve çözüm yok ... Kötü olduğunu söyleyecekseniz, nasıl olması gerektiğine de bir örnek vermelisiniz.
KdgDev

2
@WebDevHobo: Bu yüzden yorumundan bir gün önce kendime bir cevap ekledim. Kodunuz için strcasecmp gerçekten yapılacak doğru şeydi.
Sander Rijken

29

Normal ifade yukarıda işlev görür, ancak yukarıda önerilen diğer ayarlamalar ile:

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

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

2
php dizge işlemleri için parametrelerin sıralaması $ haystack, $ iğne. bu işlevler geriye dönüktür ve sıralamanın aslında $ iğne, $ samanlık olduğu dizi işlevleri gibi işlev görür.
Andy

29

Bu sorunun zaten birçok cevabı var, ancak bazı durumlarda hepsinden daha basit bir şeye razı olabilirsiniz. Aradığınız dize biliniyorsa (sabit kodlanmışsa), herhangi bir alıntı yapmadan normal ifadeleri kullanabilirsiniz.

Bir dizenin 'ABC' ile başlayıp başlamadığını kontrol edin:

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

'ABC' ile biter:

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

Basit durumumda, bir dizenin eğik çizgi ile bitip bitmediğini kontrol etmek istedim:

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

Avantajı: çok kısa ve basit olduğu için, endsWith()yukarıda gösterildiği gibi bir işlev (örneğin ) tanımlamanız gerekmez .

Ama yine de - bu her durum için bir çözüm değil, sadece bu çok spesifik olanı.


dizeyi sabit olarak kodlamanız gerekmez. normal ifade dinamik olabilir .
Ryan

2
@self true, ancak dize sabit kodlanmamışsa, bundan kaçmanız gerekir. Şu anda bu soruda bunu yapan 2 cevap var. Bu kolaydır, ancak kodu biraz karmaşık hale getirir. Demek istediğim, kodlamanın mümkün olduğu çok basit durumlar için bunu basit tutabilirsiniz.
noamtm

1
Ayrıca eğik çizgiden kaçmak zorunda değilsiniz, normal ifadeyi başka bir karaktere sarabilirsiniz @, böylece eğik çizgi ( /) 'den kaçmak zorunda kalmazsınız. Örnek 3'e bakın: php.net/manual/tr/function.preg-match.php .
cjbarth

Teşekkürler @cjbarth. Cevabımı buna göre değiştirdi. BTW, "#", eğik çizgi ile uğraşırken php.net/manual/tr/regexp.reference.delimiters.php dosyasında verilen örnektir .
noamtm

23

Hız sizin için önemliyse, bunu deneyin. (Bunun en hızlı yöntem olduğuna inanıyorum)

Yalnızca dizeler için çalışır ve $ haystack yalnızca 1 karakter ise

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
Bu muhtemelen en verimli cevap çünkü ekstra, sadece normal dize olarak herhangi bir işlevi kullanmıyorum ...

Dizenin en az bir karakteri olup olmadığını ve iki parametrenin değiştirildiğini kontrol etmelidir
a1an

1
Yaratıcı. Samanlık içeren iğneler. BTW ile çirkin bir zayıflama var: endsWithChar('','x')ama sonuç doğru
Tino

18

İğneler büyük ölçüde büyük olduğunda yararlı olabilecek geçici bir dize tanımayan iki işlev şunlardır:

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 PHP5.1 ve IMHO'nun en iyi cevabından beri çalışır. Ama endsWidthyapmalı return $needle==='' || substr_compare(... yani beklendiği gibi çalışıyor -strlen($needle)===0, düzeltmeden endsWith('a','')geri dönüyorfalse
Tino

@Tino Teşekkürler ... substr_compare()Aslında bunun bir hata olduğunu hissediyorum , bu yüzden bunu düzeltmek için bir PR ekledim :)
Ja͢ck

3
Çağrı endsWith('', 'foo')bir Uyarı başlatır: “substr_compare (): Başlangıç ​​konumu başlangıç ​​dize uzunluğunu aşamaz”. Belki bu başka bir hata substr_compare(), ama önlemek için, gibi bir ön kontrol gerekir ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_

@gx_ Daha fazla kodla yavaşlamaya gerek yok. return $needle === '' || @substr_compare(Bu uyarıyı bastırmak için .. kullanın.
Tino

17

En hızlı uçlarWith () çözümü:

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

Karşılaştırma:

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

Deney Sonuçları:

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
Farklı çözümleri karşılaştırmak ve bunları karşılaştırmak için zaman ayırdığınız için +1! dil geliştikçe optimizasyonlar yapıldığından PHP'nin hangi sürümünü kullandığınızı da belirtmelisiniz! Bir PHP sürümünden diğerine dize karşılaştırma işlevlerinde çarpıcı iyileştirmeler gördüm :)
Christophe Deliens

1
echoing @ChristopheDeliens ve PHP sürümünü sağlama isteği. Testinizi 7.3.2'de çalıştırdım ve benzer sonuçlar aldım FWIW.
Jeff

16

Bunun bittiğini anlıyorum, ancak karşılaştırmak için dizenin uzunluğunu koymanıza izin verdiği için strncmp'ye bakmak isteyebilirsiniz , bu yüzden:

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

bununla nasıl bitirdin?
mpen

@ Mark - kabul edilen cevaba bakabilirsiniz, ancak strncmp'yi kullanmayı tercih ediyorum çünkü daha güvenli olduğunu düşünüyorum.
James Black

Özellikle strncmp ile. Bir ofset belirtemezsiniz. Bu, bitmelerinizin işlevinin tamamen farklı bir yöntem kullanması gerektiği anlamına gelir.
mpen

@Mark - Uçlar için Ben sadece strrpos ( php.net/manual/en/function.strrpos.php ) kullanmak istiyorum , ama genellikle, strcmp strncmp kullanmaya her zaman muhtemelen daha güvenli bir seçenektir.
James Black

11

Kullanabilirsiniz strposvestrrpos

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

1
Burada bunun strpos($sHaystack, $sNeedle) == 0gibi üçlü eşitler strpos($sHaystack, $sNeedle) === 0mi kullanmalısınız ? Bir hatayı görmek false == 0değerlendirir için true.
Kalyan

11

Kabul edilen cevabın çok baytlı güvenli bir sürümü, UTF-8 dizeleri için iyi çalışıyor:

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
im sadece bu sadece CPU israfı olduğundan eminim. StarstWith ve EndsWith için kontrol etmeniz gereken tek şey baytların eşleşip eşleşmediğini kontrol etmek ve kabul edilen cevabın yaptığı şey tam olarak bu. Bu 1 iğnenin utf8 karakter sayısını hesaplamak zaman harcıyor ve samanlıkta n'th utf8 karakterin konumu .. Bence,% 100 kesin olmadan, bu sadece cpu israfı. kabul edilen cevabın başarısız olduğu gerçek bir test vakası bulabilir misiniz?
hanshenrik

2
@hanshenrik - UTF8 ile aynı baytları içeren ancak son karakterin yarısı eksik olan bir dize ararken çok nadir durumlarda btw olabilir. Gibi, unicode C5 91 ("have" harfi) var ve C5 ("Å" harfi) için bir eşleşme vermemelisiniz. Öte yandan, emin değilim, neden utf samanlıkta utf olmayan bir iğne ararsınız ... Ama kurşun geçirmez kontroller için bu bir olasılık olarak düşünülmelidir.
dkellner

İçinde startsWitholmalı$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen

2
@ThomasKekeisen Teşekkürler, düzelttim.
Vahid Amiri

8

Düzenli ifadeler olmadan kısa ve anlaşılması kolay tek gömlekler.

startsWith () doğrudan ileri gider.

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

endsWith () hafif süslü ve yavaş strrev () kullanır:

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

@FrancescoMM: strpos "doğru araç" değil ... Neden? O zaman "doğru araçlar" nedir? EDIT: Cevabınızı aşağıda okudum. Programlamanın sahip olduğunuz kaynakları kullanarak buluş gibi olduğunu düşündüm .. Yani doğru ya da yanlış yok ... sadece çalışıyor ya da çalışmıyor ... performans ikincil.
Fr0zenFyr

"çünkü karşılaştırma için değil, arama için bir araçtır?" Cit. Aristoteles
FrancescoMM

7

Dizgelerin boş olmadığından eminseniz, odaklanmadan önce, karşılaştırmadan önce, stlen, vb. İlk karaktere bir test eklenir,

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

Bir şekilde (% 20 -% 30) daha hızlı. $ Haystack {1} === $ {{}} gibi başka bir char testi eklemek işleri hızlandırmıyor gibi görünebilir, hatta yavaşlayabilir.

===daha hızlı görünüyor == Koşullu operatör (a)?b:cdaha hızlı görünüyorif(a) b; else c;


"Neden strpos kullanmıyorsunuz?" diğer çözümlere "gereksiz çalışma" diyoruz


strpos hızlıdır, ancak bu iş için doğru araç değildir.

Anlamak için, örnek olarak küçük bir simülasyon var:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

Bilgisayar "içeride" ne yapar?

    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

Strlen'in tüm dizeyi yinelemediğini varsayarsak (ancak bu durumda bile) bu hiç de uygun değildir.


Sadece ilk karakterler farklıysa bir hızlanma olur.
Ja͢ck

2
@ Evet, tabii ki, fikir istatistiksel olarak gerçekleşir, bu yüzden hızlanma genel olarak tüm test setinde% 20 ila% 30'dur (farklı olmadığı durumlar dahil). Farklı olduklarında çok kazanırlar, çok az gevşek olurlar. Ortalama olarak% 30 kazanırsınız (sete bağlı olarak değişir, ancak çoğunlukla büyük testlerde hız kazanırsınız)
FrancescoMM

“ama bu iş için doğru araç değil” ... Herhangi bir atıf var mı?
1818

1
O NE LAN. Bundan daha fazla alıntı yapmam gereken tüm süreci aşağıda listeledim. Yumruk karakterinin 'a' olmadığını söylemek için bir dizenin sonuna kadar arama yapan bir işlev kullanır mısınız? Kimin umurunda? Bu doğru bir araç değil çünkü arama yapmak için bir araç, karşılaştırmak için değil, Aristoteles'i açıkça belirtmek için alıntı yapmaya gerek yok!
FrancescoMM

6

Umarım aşağıdaki cevap etkili ve basit olabilir:

$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

Genellikle bugünlerde alt çizgi-php gibi bir kütüphane ile devam ediyorum .

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   

Kütüphane diğer kullanışlı işlevlerle doludur.


6

Cevap tarafından mpen maalesef sağlanan kriter çok önemli ve zararlı gözetim vardır, inanılmaz titiz, ama olmaz.

İğnelerdeki ve samanlıklardaki her bayt tamamen rasgele olduğundan, bir iğne samanlığı çiftinin ilk baytta farklı olma olasılığı% 99.609375'tir, yani ortalama 100000 çiftinin yaklaşık 99609'u ilk baytta farklı olacaktır. . Diğer bir deyişle, karşılaştırma ölçütü, startswithilk baytı olduğu gibi açık bir şekilde kontrol eden uygulamalara büyük ölçüde önyargılıdır strncmp_startswith2.

Test üreten döngü bunun yerine aşağıdaki gibi uygulanırsa:

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

kıyaslama sonuçları biraz farklı bir hikaye anlatıyor:

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

Tabii ki, bu kıyaslama hala mükemmel derecede tarafsız olmayabilir, ancak kısmen eşleşen iğneler verildiğinde algoritmaların verimliliğini test eder.


5

Kısacası:

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

Sadece bir öneri:

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

Dizelerin ilk karakterini karşılaştıran bu ekstra çizgi, yanlış durumun hemen geri dönmesini sağlayabilir , bu nedenle karşılaştırmalarınızın çoğunu çok daha hızlı hale getirir (ölçtüğümde 7 kat daha hızlı). Gerçek durumda, bu tek hat için performansta neredeyse hiç fiyat ödemezsiniz, bu yüzden dahil etmeye değer olduğunu düşünüyorum. (Ayrıca, pratikte, belirli bir başlangıç ​​parçası için birçok dizeyi test ettiğinizde, çoğu karşılaştırma, tipik bir durumda bir şey aradığınız için başarısız olur.)


2
Kodunuzdaki hata: startsWith("123", "0")verirtrue
Tino

Evet, kötü! $ Kontrol oldu. Afedersiniz! (Sadece 3. satırdaki konsepti göstermek istedim)
dkellner

4

substrİşlev dönebilir falseişte benim sürüm, bu konuyla ilgileniyor, birçok özel durumlarda:

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
}

Testler ( trueiyi anlamına gelir):

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

Ayrıca, substr_compareişlev de bakmaya değer. http://www.php.net/manual/en/function.substr-compare.php



4

Böyle yapardım

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

Eşleşmezse yanlış döndürmeyi unutmak. Bir fonksiyonun dönüş değeri 'yanlış' olduğu için yanlış Errgo, ama en azından diğer cevaplara kıyasla ne peşinde olduğunu biliyorum.
Spoo

3

James Black'in cevabına dayanarak, işte sonu:

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

}

Not: James Black'in startsWith işlevi için if-else parçasını değiştirdim, çünkü strncasecmp aslında strncmp'ın büyük / küçük harfe duyarlı olmayan sürümüdür.


2
Not strrev()olan yaratıcı sen ... 100KB söz hakkından dizeleri var, özellikle ama çok masraflı.
Alexis Wilke

Kullanım ===yerine ==emin olmak için. 0PHP'deki birçok şeye eşittir.
nawfal

3

Neden aşağıdakiler olmasın?

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

Çıktı:

Valuehaystack başlangıcında bulunan değer!

Unutmayın strpos, iğne samanlıkta bulunmazsa false değerini döndürür ve yalnızca ve eğer indeks 0'da (başlangıç ​​AKA) iğne bulunursa 0 değerini döndürür.

Ve işte bitiyor

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

Bu senaryoda, startWith () işlevine gerek yoktur.

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

doğru veya yanlış döndürür.

Burada çok vahşi çalışan tüm vahşi fonksiyonları ile bu kadar basit görünüyor.


3
"X" ile "a" kelimesini karşılaştırmak ve FALSE değerini döndürmek yerine "ab" de "abcdefghijklmxyz" içinde "xy" araması yaparsanız, "a" dan "m" ye kadar olan her karaktere bakıp "xy" kelimesini bulmanızın tuhaf görünmesi ve sonunda FALSE döndürürsünüz çünkü konumu sıfır değildir! Yaptığınız şey bu ve buradaki diğer yaygın işlevlerden daha garip ve daha vahşi.
FrancescoMM

Basitlik mantıkta değil, yazımdadır.
Kade Hafen

Mantık o kadar da değil, Francsco'nun işaret ettiği olası optimizasyon. Kullanımı strpos(), eşleşmesi dışında yavaş olacaktır. strncmp()bu durumda çok daha iyi olurdu.
Alexis Wilke

Bu kadar düşük seviyeli fonksiyonlar yaparken, ne kadar karmaşık olursa olsun, milyonlarca kez adlandırılacağı için genellikle en hızlı optimize edilmiş çözümü tercih etmek istersiniz. Burada kazandığınız veya kaybettiğiniz her mikrosaniye çok gerçek bir fark yaratacaktır. Öyleyse cehennemden daha iyi tweak yapın (ve sonra fonksiyona sahip olduğunuz karmaşıklığı unutun), neyin yanlış gittiğini bile bilmediğinizde, görünüşe gitmek ve daha sonra korkunç bir süre kaybetmek yerine. Eşleşmeyen 2 GB'lik bir dizeyi kontrol ettiğinizi düşünün.
dkellner

3

Önceki cevapların birçoğu da aynı şekilde işe yarayacaktır. Ancak, bu muhtemelen yapabileceğiniz kadar kısa ve istediğinizi yapmasını sağlar. Siz sadece 'gerçeğe dönmesini' istediğinizi söylersiniz. Bu yüzden boolean true / false ve textual true / false döndüren çözümler ekledim.

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

Doğru. Ancak Peter, karakter dizileriyle çalışacak bir işlev istiyordu. Yine de, sizi yatıştırmak için cevabımı güncelledim.
wynshaft

Düzenlemeden sonra çözümünüz artık tamamen eski. Bu döner 'true've 'false'her ikisi dizeleri gibi truebir boolean anlamda. Underhanded.xcott.com gibi bir şey için iyi bir model olsa da;)
Tino

Peter sadece 'gerçek'e' geri dönmek istediğini söyledi. Bu yüzden istediğini geri vereceğimi düşündüm. Her iki sürümü de ekledim, sadece istediği gibi değilse.
wynshaft

2

Ayrıca düzenli ifadeler kullanabilirsiniz:

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

3
$ iğne ile kaçmalıdır preg_quote($needle, '/').
Timo Tijhof

2

Kopyasız ve stajyersiz döngü:

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

bu MrHus'un uygulanmasından çok daha hızlı olmalı! Ben kıyaslamak olabilir
hanshenrik

1

İşte PHP 4 için verimli bir çözüm. PHP 5 substr_compareyerine kullanarak daha hızlı sonuç alabilirsiniz 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

Bunun için fnmatch işlevini kullanabilirsiniz .

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

uyarı, ikili değil güvenli ve hatta joker karakterler içeren iğnelere karşı güvenli değil = /
hanshenrik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.