fungsi beginWith () dan endsWith () di PHP

1482

Bagaimana saya bisa menulis dua fungsi yang akan mengambil string dan kembali jika dimulai dengan karakter / string yang ditentukan atau diakhiri dengan itu?

Sebagai contoh:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Klik Upvote
sumber
19
Lihat kelas Str Laravel beginWith () dan endsWith () untuk metode yang telah teruji . Kasus tepi telah ditemui, jadi penggunaan luas kode ini merupakan keuntungan.
Gras Double
1
Anda mungkin menemukan s($str)->startsWith('|')dan s($str)->endsWith('}')membantu, seperti yang ditemukan di perpustakaan mandiri ini .
gak
3
Peringatan: sebagian besar jawaban di sini tidak dapat diandalkan dalam pengkodean multi-byte seperti UTF-8.
Álvaro González
Menindaklanjuti komentar saya di atas, Anda dapat memastikan untuk menggunakan versi terbaru (mulai hari ini, 5.4 ). Khususnya, beginWith () telah dioptimalkan untuk string tumpukan jerami besar.
Gras Double

Jawaban:

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

Gunakan ini jika Anda tidak ingin menggunakan regex.

Tuan
sumber
16
+1 Ini lebih bersih daripada jawaban yang diterima. Juga, $lengthtidak diperlukan di baris terakhir endsWith().
terlalu banyak php
13
Saya akan mengatakan endsWith ('foo', '') == false adalah perilaku yang benar. Karena foo tidak berakhir tanpa apa-apa. 'Foo' diakhiri dengan 'o', 'oo' dan 'Foo'.
MrHus
125
BerakhirDapat ditulis jauh lebih pendek:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj
12
Anda dapat menghindari ifsama sekali dengan melewati $lengthsebagai parameter ketiga untuk substr: return (substr($haystack, -$length, $length);. Ini menangani kasus $length == 0dengan mengembalikan string kosong dan bukan keseluruhan $haystack.
mxxk
20
@MrHus Saya akan merekomendasikan menggunakan fungsi aman multi-byte, mis. Mb_strlen dan mb_substr
19Gerhard85
1025

Anda dapat menggunakan substr_comparefungsi untuk memeriksa mulai-dengan dan berakhir-dengan:

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

Ini harus menjadi salah satu solusi tercepat di PHP 7 ( skrip benchmark ). Diuji terhadap tumpukan jerami 8KB, berbagai jarum panjang dan penuh, sebagian dan tidak ada kotak korek api. strncmpadalah sentuhan yang lebih cepat untuk memulai-dengan tetapi tidak dapat memeriksa ujung-dengan.

Salman A
sumber
74
Jawaban ini sampai ke Daily WTF! : D Lihat thedailywtf.com/articles/…
Wim ten Brink
Harap perhatikan bahwa komentar @DavidWallace dan @FrancescoMM berlaku untuk versi yang lebih lama dari jawaban ini. Jawaban saat ini menggunakan strrposyang (harus) gagal segera jika jarum tidak cocok dengan awal tumpukan jerami.
Salman A
2
Saya tidak mengerti. Berdasarkan pada php.net/manual/en/function.strrpos.php : "Jika nilainya negatif, pencarian akan dimulai dari banyak karakter dari akhir string, mencari mundur." Ini sepertinya menunjukkan bahwa kita mulai dari karakter 0 (karena -strlength($haystack)) dan mencari mundur dari sana? Bukankah itu berarti Anda tidak mencari apa-apa? Saya juga tidak mengerti !== falsebagian dari ini. Saya menduga ini bergantung pada kekhasan PHP di mana beberapa nilai "benar" dan yang lain "palsu" tetapi bagaimana cara kerjanya dalam kasus ini?
Welbog
3
@Welbog: misalnya haystack = xxxyyyneedle = yyydan menggunakan strrpospencarian dimulai dari yang pertama x. Sekarang kami tidak memiliki kecocokan yang berhasil di sini (ditemukan x bukan y) dan kami tidak dapat mundur lagi (kami berada di awal string) pencarian gagal dengan segera . Tentang menggunakan !== false- strrposdalam contoh di atas akan mengembalikan 0 atau salah dan bukan nilai lainnya. Demikian juga, strposdalam contoh di atas dapat mengembalikan $temp(posisi yang diharapkan) atau salah. Saya pergi dengan !== falsekonsistensi tetapi Anda dapat menggunakan === 0dan === $tempdalam fungsi masing-masing.
Salman A
8
@ spoo sudah ditetapkan bahwa strpos === 0 adalah solusi buruk jika tumpukan jerami besar dan jarum tidak ada.
Salman A
243

Diperbarui 23-Agu-2016

Fungsi

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

Tes

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

Hasil (PHP 7.0.9)

(Diurutkan paling cepat hingga paling lambat)

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

Hasil (PHP 5.3.29)

(Diurutkan paling cepat hingga paling lambat)

strncmp_startswith2: 477.9 ms
strpos_startswith: 522.1 ms
strncmp_startswith: 617.1 ms
substr_compare_startswith: 706.7 ms
substr_startswith: 756.8 ms
preg_match_startswith: 10,200.0 ms

startswith_benchmark.php

Mpen
sumber
3
Jika string tidak kosong, seperti dalam tes Anda, ini sebenarnya entah bagaimana (20-30%) lebih cepat: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}Saya menambahkan balasan di bawah ini.
FrancescoMM
3
@Jronny Karena 110 kurang dari 133 ... ??
mpen
2
Sial, aku tidak tahu apa yang ada di kepalaku saat itu. Boleh jadi kurang tidur.
Jronny
1
@pen, saya tidak memperhatikan gajah sama sekali :(
Visman
1
Tes-tes ini tidak baik dalam menguji kinerja. Apa yang Anda lakukan adalah menggunakan string acak sebagai jarum. Dalam 99,99% kasus tidak akan ada kecocokan. Sebagian besar fungsi akan keluar setelah mencocokkan byte pertama. Bagaimana dengan kasus ketika kecocokan ditemukan? Fungsi mana yang paling sedikit menghabiskan waktu untuk menyelesaikan pertandingan yang berhasil? Bagaimana dengan kasus di mana 99% jarum cocok tetapi bukan beberapa byte terakhir? Fungsi mana yang paling sedikit menghabiskan waktu untuk menyimpulkan tidak ada yang cocok?
Salman A
137

Semua jawaban sejauh tampaknya melakukan banyak pekerjaan yang tidak perlu, strlen calculations, string allocations (substr), dll 'strpos'dan 'stripos'fungsi mengembalikan indeks dari kejadian pertama $needledi $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
sumber
2
endsWith()fungsi memiliki kesalahan. Baris pertamanya adalah (tanpa -1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma
6
Hal strlen () tidak perlu. Jika string tidak dimulai dengan jarum yang diberikan maka kode Anda akan memindai seluruh tumpukan jerami yang tidak perlu.
AppleGrew
5
@Mark ya, memeriksa hanya awal adalah BANYAK lebih cepat, terutama jika Anda melakukan sesuatu seperti memeriksa jenis MIME (atau tempat lain di mana string terikat menjadi besar)
chacham15
2
@mark Saya melakukan beberapa tolok ukur dengan 1000 arang jerami dan 10 atau 800 arang jarum dan tegukan adalah 30% lebih cepat. Lakukan tolok ukur Anda sebelum menyatakan bahwa ada sesuatu yang lebih cepat atau tidak ...
wdev
7
Anda harus sangat mempertimbangkan mengutip jarum seperti strpos($haystack, "$needle", 0)jika ada setiap kesempatan itu belum string (misalnya, jika itu berasal dari json_decode()). Jika tidak, perilaku default [aneh] strpos()dapat menyebabkan hasil yang tidak terduga: " Jika jarum bukan string, itu dikonversi ke integer dan diterapkan sebagai nilai ordinal karakter. "
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);
}

Kredit untuk :

Periksa apakah string berakhir dengan string lain

Periksa apakah string dimulai dengan string lain

KdgDev
sumber
1
strtolower bukanlah cara terbaik untuk membuat fungsi case case-sensitive. Di beberapa tempat, casing lebih kompleks daripada hanya bagian atas dan bawah.
Sander Rijken
8
Saya melihat mengeluh dan tidak ada solusi ... Jika Anda akan mengatakan itu buruk, maka Anda harus memberikan contoh bagaimana seharusnya.
KdgDev
2
@ WebDevHobo: itu sebabnya saya menambahkan jawaban sendiri sehari sebelum komentar Anda. Untuk kode Anda, strcasecmp memang benar untuk dilakukan.
Sander Rijken
29

Fungsi regex di atas, tetapi dengan tweak lainnya juga disarankan di atas:

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

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
tridian
sumber
2
di php untuk operasi string urutan parameter adalah $ tumpukan jerami, $ jarum. fungsi-fungsi ini mundur dan bertindak seperti fungsi array di mana pemesanan sebenarnya $ jarum, $ tumpukan jerami.
Andy
29

Pertanyaan ini sudah memiliki banyak jawaban, tetapi dalam beberapa kasus Anda dapat menerima sesuatu yang lebih sederhana daripada semuanya. Jika string yang Anda cari diketahui (hardcoded), Anda dapat menggunakan ekspresi reguler tanpa mengutip dll.

Periksa apakah string dimulai dengan 'ABC':

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

diakhiri dengan 'ABC':

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

Dalam kasus sederhana saya, saya ingin memeriksa apakah string diakhiri dengan garis miring:

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

Keuntungan: karena sangat pendek dan sederhana, Anda tidak perlu mendefinisikan fungsi (seperti endsWith()) seperti yang ditunjukkan di atas.

Tetapi sekali lagi - ini bukan solusi untuk setiap kasus, hanya ini yang sangat spesifik.

noamtm
sumber
Anda tidak perlu membuat kode string. regex bisa dinamis.
Ryan
2
@ sendiri benar, tetapi jika string tidak hardcoded, Anda harus menghindarinya. Saat ini ada 2 jawaban untuk pertanyaan ini yang melakukannya. Ini mudah, tetapi sedikit menyulitkan kode. Jadi poin saya adalah bahwa untuk kasus yang sangat sederhana, di mana hardcoding dimungkinkan, Anda dapat membuatnya tetap sederhana.
noamtm
1
Anda juga tidak harus melarikan diri dari garis miring, Anda dapat membungkus regex dengan beberapa karakter lain, seperti @, sehingga garis miring ( /) tidak harus keluar. Lihat Contoh # 3 di sini: php.net/manual/en/function.preg-match.php .
cjbarth
Terima kasih @cjbarth. Mengubah jawaban saya sesuai dengan itu. BTW, "#" adalah contoh yang diberikan di php.net/manual/en/regexp.reference.delimiters.php saat berurusan dengan garis miring.
noamtm
23

Jika kecepatan penting bagi Anda, coba ini. (Saya percaya ini adalah metode tercepat)

Hanya berfungsi untuk string dan jika $ haystack hanya 1 karakter

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
sumber
1
ini mungkin jawaban yang paling efisien karena tidak menggunakan fungsi apa pun sebagai string ekstra, hanya biasa ...
Seharusnya memeriksa apakah string memiliki setidaknya satu karakter dan menukar dua parameter
a1an
1
Kreatif. Jarum yang berisi tumpukan jerami. BTW ada beberapa buruk menyusut dengan:, endsWithChar('','x')tetapi hasilnya benar
Tino
18

Berikut adalah dua fungsi yang tidak memperkenalkan string sementara, yang bisa berguna ketika jarum secara substansial besar:

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

function endsWith($haystack, $needle)
{
    return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
Mendongkrak
sumber
2
+1 Berfungsi sejak PHP5.1 dan IMHO jawaban terbaik. Tetapi endsWidthharus dilakukan return $needle==='' || substr_compare(... jadi itu berfungsi seperti yang diharapkan -strlen($needle)===0yang, tanpa perbaikan, membuat endsWith('a','')kembalifalse
Tino
@Tino Terima kasih ... Saya merasa itu adalah bug substr_compare()sebenarnya, jadi saya telah menambahkan PR untuk memperbaikinya :)
Ja͢ck
3
Panggilan endsWith('', 'foo')memicu Peringatan: “substr_compare (): Posisi awal tidak boleh melebihi panjang string awal”. Mungkin itu bug lain substr_compare(), tetapi untuk menghindarinya, Anda perlu melakukan pra-cek seperti ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_
@gx_ Tidak perlu melambat dengan lebih banyak kode. Cukup gunakan return $needle === '' || @substr_compare(.. untuk menekan peringatan ini.
Tino
17

Solusi tercepat dengan solusi ():

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

Benchmark:

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

Hasil 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
Lucas Bustamante
sumber
3
+1 untuk meluangkan waktu untuk membandingkan berbagai solusi dan benar-benar membandingkannya! Anda juga harus menyebutkan versi PHP yang Anda gunakan, karena optimisasi dilakukan seiring perkembangan bahasa! Saya telah melihat peningkatan dramatis pada fungsi perbandingan string dari satu versi PHP ke yang lain :)
Christophe Deliens
1
echoing @ChristopheDeliens dan permintaannya untuk menyediakan versi PHP. Saya menjalankan tes Anda pada 7.3.2 dan mendapatkan hasil yang serupa FWIW.
Jeff
16

Saya menyadari ini telah selesai, tetapi Anda mungkin ingin melihat strncmp karena memungkinkan Anda untuk meletakkan panjang string untuk dibandingkan, jadi:

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
sumber
bagaimana Anda akan melakukannya dengan ini?
mpen
@ Mark - Anda dapat melihat jawaban yang diterima, tetapi saya lebih suka menggunakan strncmp terutama karena saya pikir ini lebih aman.
James Black
Maksud saya dengan strncmp khusus. Anda tidak dapat menentukan offset. Itu berarti fungsi Anda berakhir. Dengan harus menggunakan metode yang berbeda sama sekali.
mpen
@ Mark - Untuk tujuan Dengan saya hanya akan menggunakan strrpos ( php.net/manual/en/function.strrpos.php ), tetapi, secara umum, kapan saja Anda pergi menggunakan strcmp strncmp mungkin merupakan opsi yang lebih aman.
James Black
11

Anda bisa menggunakan strposdanstrrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
Lex
sumber
1
Haruskah Anda menggunakan triple sama dengan di sini strpos($sHaystack, $sNeedle) == 0seperti ini strpos($sHaystack, $sNeedle) === 0? Saya melihat bug, ketika false == 0dievaluasi true.
Kalyan
11

Inilah versi aman multi-byte dari jawaban yang diterima, itu berfungsi baik untuk string 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
sumber
2
Saya cukup yakin ini hanya buang-buang CPU. yang perlu Anda periksa, untuk StarstWith dan EndsWith, hanya memeriksa apakah byte tersebut cocok, dan itulah yang dilakukan jawaban yang diterima. 1 ini membuang-buang waktu menghitung jumlah karakter utf8 dari jarum, dan di mana posisi karakter utf8 ke-9 dari tumpukan jerami adalah .. saya pikir, tanpa 100% pasti, ini hanya buang-buang cpu. dapatkah Anda membuat test case aktual di mana jawaban yang diterima gagal, dan ini tidak?
hanshenrik
2
@hanshenrik - itu bisa terjadi btw, dalam kasus yang sangat langka ketika Anda mencari string yang berisi byte yang sama dengan UTF8 tetapi dengan setengah dari karakter terakhir yang hilang. Seperti, Anda memiliki unicode C5 91 (huruf "ő") dan Anda mencari C5 (huruf "Å") yang seharusnya tidak cocok dengan Anda. Di sisi lain, tentu saja, mengapa Anda mencari tumpukan jerami utf untuk jarum non-utf ... Tetapi untuk pemeriksaan antipeluru, ini harus dianggap sebagai kemungkinan.
dkellner
Dalam startsWithitu harus$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen
2
@ThomasKekeisen Terima kasih, perbaiki.
Vahid Amiri
8

One-liner pendek dan mudah dipahami tanpa ekspresi reguler.

dimulai dengan () lurus ke depan.

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

endsWith () menggunakan strrev yang agak mewah dan lambat ():

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
Dan
sumber
@ FrancescoMM: strpos bukan "alat yang tepat" ... Mengapa? Apa "alat yang tepat" itu? EDIT: Saya membaca jawaban Anda di bawah ini. Saya pikir pemrograman seperti penemuan menggunakan sumber daya yang Anda miliki .. Jadi tidak ada benar atau salah ... hanya bekerja atau tidak berfungsi ... kinerja adalah sekunder.
Fr0zenFyr
"Karena ini adalah alat untuk mencari, bukan untuk membandingkan?" Cit. Aristoteles
FrancescoMM
7

Berfokus pada start dengan, jika Anda yakin string tidak kosong, menambahkan tes pada char pertama, sebelum perbandingan, strlen, dll., Mempercepat segalanya:

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

Itu entah bagaimana (20% -30%) lebih cepat. Menambahkan tes char lain, seperti $ haystack {1} === $ needle {1} tampaknya tidak mempercepat banyak hal, bahkan mungkin melambat.

===tampaknya lebih cepat dari == Operator Bersyarat (a)?b:ctampaknya lebih cepat dariif(a) b; else c;


Bagi mereka yang bertanya "mengapa tidak menggunakan strpos?" menyebut solusi lain "pekerjaan yang tidak perlu"


Strpos cepat, tetapi itu bukan alat yang tepat untuk pekerjaan ini.

Untuk memahami, ini adalah sedikit simulasi sebagai contoh:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

Apa yang dilakukan komputer "di dalam"?

    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

Dengan asumsi strlen tidak mengulangi seluruh string (tetapi bahkan dalam kasus itu) ini tidak nyaman sama sekali.

FrancescoMM
sumber
Hanya ada percepatan jika karakter pertama berbeda.
Ja͢ck
2
@ Jack ya, tentu saja, idenya adalah bahwa secara statistik itu terjadi, sehingga speedup umumnya 20% -30% dari seluruh set tes (termasuk kasus di mana tidak berbeda). Anda mendapatkan banyak ketika mereka berbeda dan sedikit sekali longgar ketika mereka tidak. Rata-rata Anda mendapatkan 30% (bervariasi tergantung pada set, tetapi sebagian besar Anda mendapatkan kecepatan pada tes besar)
FrancescoMM
"Tapi itu bukan alat yang tepat untuk pekerjaan ini" ... Ada kutipan?
Fr0zenFyr
1
WTF. Saya mencantumkan semua proses di bawah ini yang harus saya kutip, lebih dari itu? Apakah Anda menggunakan fungsi yang mencari sampai akhir string untuk memberi tahu Anda bahwa karakter pertama bukan 'a'? Apakah itu peduli? Ini bukan alat yang tepat karena ini adalah alat untuk mencari, bukan untuk membandingkan, tidak perlu mengutip Aristoteles untuk menyatakan yang jelas!
FrancescoMM
6

Saya harap jawaban di bawah ini bisa efisien dan juga sederhana:

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

Saya biasanya berakhir dengan perpustakaan seperti garis bawah-php hari ini.

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   

Perpustakaan penuh dengan fungsi praktis lainnya.

yuvilio
sumber
6

The jawabannya oleh mpen ini sangat menyeluruh, namun, sayangnya, patokan yang disediakan memiliki pengawasan yang sangat penting dan merugikan.

Karena setiap byte dalam jarum dan tumpukan jerami benar-benar acak, probabilitas bahwa pasangan jarum-tumpukan jerami akan berbeda pada byte pertama adalah 99,609375%, yang berarti bahwa, rata-rata, sekitar 99609 dari 100.000 pasangan akan berbeda pada byte pertama . Dengan kata lain, benchmark sangat bias terhadap startswithimplementasi yang memeriksa byte pertama secara eksplisit, seperti strncmp_startswith2halnya.

Jika loop menghasilkan tes diimplementasikan sebagai berikut:

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

hasil benchmark menceritakan kisah yang sedikit berbeda:

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

Tentu saja, tolok ukur ini mungkin masih tidak bias sempurna, tetapi ini menguji efisiensi algoritma ketika diberikan juga jarum yang cocok sebagian.

Veeno
sumber
5

pendeknya:

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

Hanya rekomendasi:

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

Baris tambahan itu, yang membandingkan karakter pertama dari string, dapat membuat case palsu segera kembali , sehingga membuat banyak perbandingan Anda jauh lebih cepat (7x lebih cepat ketika saya mengukur). Dalam kasus yang sebenarnya Anda membayar hampir tidak ada harga dalam kinerja untuk satu baris jadi saya pikir itu layak termasuk. (Juga, dalam praktiknya, saat Anda menguji banyak string untuk suatu starting starting tertentu, sebagian besar perbandingan akan gagal karena dalam kasus tipikal Anda sedang mencari sesuatu.)

dkellner
sumber
2
Bug dalam kode Anda: startsWith("123", "0")memberikantrue
Tino
Yup, buruk! $ Cek terjadi. Maaf! (Hanya ingin mengilustrasikan konsep di baris 3)
dkellner
4

The substrfungsi dapat kembali falsedalam banyak kasus khusus, jadi di sini adalah versi saya, yang berkaitan dengan masalah ini:

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
}

Tes ( trueberarti bagus):

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

Selain itu, substr_comparefungsinya juga layak dicari. http://www.php.net/manual/en/function.substr-compare.php

biziclop
sumber
4

Ini mungkin berhasil

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

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

pengguna507410
sumber
4

Saya akan melakukannya seperti ini

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

  function endWith($haystack,$needle){
              if(substr($haystack, -strlen($needle))===$needle)
              return true;
        }
Jelle Keizer
sumber
Lupa mengembalikan false jika tidak cocok. Errgo salah karena nilai pengembalian fungsi tidak boleh 'diasumsikan', tapi saya tahu apa yang Anda cari setidaknya dibandingkan dengan jawaban lain.
Spoo
3

Berdasarkan jawaban James Black, inilah ujungnya dengan versi:

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

}

Catatan: Saya telah menukar bagian if-else untuk fungsi James Black beginWith, karena strncasecmp sebenarnya adalah versi case-insensitive dari strncmp.

bobo
sumber
2
Perhatikan bahwa strrev()ini kreatif tetapi sangat mahal, terutama jika Anda memiliki string katakan ... 100Kb.
Alexis Wilke
Gunakan ===bukannya ==untuk memastikan. 0sama dengan banyak hal di PHP.
nawfal
3

Kenapa tidak mengikuti yang berikut?

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

Keluaran:

Nilai ditemukan di awal valuehaystack!

Perlu diingat, strposakan mengembalikan false jika jarum tidak ditemukan di tumpukan jerami, dan akan mengembalikan 0 jika, dan hanya jika, jarum ditemukan di indeks 0 (AKA awal).

Dan ini berakhir dengan:

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

Dalam skenario ini tidak perlu untuk fungsi beginWith () as

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

akan mengembalikan benar atau salah secara akurat.

Tampaknya aneh ini sesederhana ini dengan semua fungsi liar yang merajalela di sini.

Kade Hafen
sumber
3
Tampaknya aneh bahwa jika Anda mencari "xy" di dalam string "abcdefghijklmxyz" alih-alih hanya membandingkan "x" dengan "a" dan mengembalikan FALSE, Anda melihat setiap karakter dari "a" ke "m" lalu akhirnya menemukan "xy" di dalam string, dan akhirnya Anda mengembalikan FALSE karena posisi itu bukan nol! Inilah yang Anda lakukan, dan ini aneh dan lebih liar daripada fungsi merajalela lainnya di sini.
FrancescoMM
Kesederhanaan ada dalam pengetikan, bukan logika.
Kade Hafen
Ini bukan logika, tapi kemungkinan optimasi yang ditunjukkan oleh Francsco. Penggunaan strpos()akan lambat kecuali saat cocok. strncmp()akan jauh lebih baik dalam hal ini.
Alexis Wilke
Ketika Anda melakukan fungsi tingkat rendah seperti itu, Anda biasanya ingin mencari solusi yang paling cepat dioptimalkan, tidak peduli seberapa kompleksnya, karena ini akan disebut jutaan kali. Setiap mikrodetik yang Anda dapatkan atau kehilangan di sini akan membuat perbedaan yang sangat nyata. Jadi, lebih baik lakukan perubahan (dan lupakan kerumitannya, sekarang setelah Anda memiliki fungsinya), alih-alih mencari tampilannya dan kehilangan banyak waktu kemudian ketika Anda bahkan tidak tahu apa yang salah. Bayangkan memeriksa string 2GB yang tidak cocok.
dkellner
3

Banyak jawaban sebelumnya juga akan berfungsi. Namun, ini mungkin sesingkat yang Anda bisa dan lakukan apa yang Anda inginkan. Anda hanya menyatakan bahwa Anda ingin 'mengembalikan yang benar'. Jadi saya memasukkan solusi yang mengembalikan boolean true / false dan tekstual 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';
}
poros bidik
sumber
Benar. Namun, Peter meminta fungsi yang akan bekerja dengan karakter string. Meskipun demikian, saya telah memperbarui jawaban saya untuk menenangkan Anda.
wynshaft
Setelah diedit, solusi Anda sekarang benar-benar usang. Ia mengembalikan 'true'dan 'false'sebagai string, yang keduanya truedalam arti boolean. Ini adalah pola yang baik untuk sesuatu seperti underhanded.xcott.com ;)
Tino
Nah, Peter hanya menyatakan dia ingin mengembalikan 'benar'. Jadi saya pikir saya akan mengembalikan apa yang dia minta. Saya telah menambahkan kedua versi, untuk berjaga-jaga kalau bukan itu yang dia inginkan.
wynshaft
2

Anda juga dapat menggunakan ekspresi reguler:

function endsWith($haystack, $needle, $case=true) {
  return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
Warga kehormatan
sumber
3
$ jarum harus diloloskan dengan preg_quote($needle, '/').
Timo Tijhof
2

Tanpa-salin dan tanpa-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;
}
mazatwork
sumber
ini harusnya jauh lebih cepat daripada implementasi MrHus! saya mungkin membandingkannya
hanshenrik
1

Inilah solusi yang efisien untuk PHP 4. Anda bisa mendapatkan hasil yang lebih cepat jika menggunakan PHP 5 dengan menggunakan substr_comparealih-alih 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
sumber
0

Anda dapat menggunakan fungsi fnmatch untuk ini.

// Starts with.
fnmatch('prefix*', $haystack);
// Ends with.
fnmatch('*suffix', $haystack);
ya.teck
sumber
peringatan, tidak biner aman, dan bahkan tidak aman terhadap jarum yang berisi wildcard = /
hanshenrik