Các hàm startedWith () và endWith () trong PHP


1480

Làm thế nào tôi có thể viết hai hàm sẽ lấy một chuỗi và trả về nếu nó bắt đầu bằng ký tự / chuỗi được chỉ định hoặc kết thúc bằng nó?

Ví dụ:

$str = '|apples}';

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

19
Xem lớp Str của Laravel bắt đầuWith () và endWith () để biết các phương thức được kiểm tra tốt . Các trường hợp cạnh đã gặp phải, vì vậy việc sử dụng rộng rãi mã này là một lợi thế.
Gras đôi

1
Bạn có thể tìm thấy s($str)->startsWith('|')s($str)->endsWith('}')hữu ích, như được tìm thấy trong thư viện độc lập này .
caw

3
Cảnh báo: hầu hết các câu trả lời ở đây không đáng tin cậy trong các bảng mã nhiều byte như UTF-8.
Álvaro González

Theo nhận xét trên của tôi, bạn có thể chắc chắn sử dụng phiên bản mới nhất (tính đến ngày hôm nay, 5.4 ). Đáng chú ý, startedWith () đã được tối ưu hóa cho các chuỗi haystack lớn.
Đôi Gras

Câu trả lời:


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

Sử dụng cái này nếu bạn không muốn sử dụng regex.


16
+1 Điều này sạch hơn câu trả lời được chấp nhận. Ngoài ra, $lengthkhông cần thiết trong dòng cuối cùng của endsWith().
quá nhiều php

13
Tôi muốn nói endWith ('foo', '') == false là hành vi đúng. Bởi vì foo không kết thúc mà không có gì. 'Foo' kết thúc bằng 'o', 'oo' và 'Foo'.
MrHus

125
EndsWith có thể được viết ngắn hơn rất nhiều:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj

12
Bạn có thể tránh ifhoàn toàn bằng cách chuyển $lengththam số thứ ba sang substr: return (substr($haystack, -$length, $length);. Điều này xử lý trường hợp $length == 0bằng cách trả về một chuỗi rỗng chứ không phải toàn bộ $haystack.
mxxk

20
@MrHus Tôi khuyên bạn nên sử dụng các hàm an toàn nhiều byte, ví dụ: mb_strlen và mb_substr
19Gerhard85

1024

Bạn có thể sử dụng substr_comparechức năng để kiểm tra bắt đầu và kết thúc bằng:

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

Đây phải là một trong những giải pháp nhanh nhất trên PHP 7 ( tập lệnh chuẩn ). Đã thử nghiệm với đống cỏ 8KB, kim dài khác nhau và các trường hợp đầy đủ, một phần và không khớp. strncmplà một liên lạc nhanh hơn để bắt đầu với nhưng nó không thể kiểm tra kết thúc với.


74
Câu trả lời này đã được đưa lên WTF hàng ngày! : D Xem thed Dailywtf.com/articles/
Wim ten Brink

Xin lưu ý rằng các nhận xét @DavidWallace và @FrancescoMM áp dụng cho phiên bản cũ hơn của câu trả lời này. Câu trả lời hiện tại sử dụng strrposcái nào (nên) thất bại ngay lập tức nếu kim không khớp với đầu của đống cỏ khô.
Salman A

2
Tôi không hiểu Dựa trên php.net/manual/en/feft.strrpos.php : "Nếu giá trị là âm, thay vào đó, tìm kiếm sẽ bắt đầu từ nhiều ký tự từ cuối chuỗi, tìm kiếm ngược lại." Điều này dường như cho thấy rằng chúng ta bắt đầu từ ký tự 0 (do -strlength($haystack)) và tìm kiếm ngược từ đó? Điều đó không có nghĩa là bạn không tìm kiếm gì sao? Tôi cũng không hiểu những !== falsephần này. Tôi đoán điều này dựa vào một sự châm biếm về PHP trong đó một số giá trị là "sự thật" và những cái khác là "giả" nhưng làm thế nào nó hoạt động trong trường hợp này?
Welbog

3
@Welbog: ví dụ haystack = xxxyyykim = yyyvà việc sử dụng strrpostìm kiếm bắt đầu từ lần đầu tiên x. Bây giờ chúng tôi không có một trận đấu thành công ở đây (tìm thấy x thay vì y) và chúng tôi không thể lùi lại được nữa (chúng tôi bắt đầu chuỗi) việc tìm kiếm thất bại ngay lập tức . Về việc sử dụng !== false- strrpostrong ví dụ trên sẽ trả về 0 hoặc sai và không phải giá trị khác. Tương tự như vậy, strpostrong ví dụ trên có thể trả về $temp(vị trí dự kiến) hoặc sai. Tôi đã đi với !== falsesự nhất quán nhưng bạn có thể sử dụng === 0=== $temptrong các chức năng tương ứng.
Salman A

8
@spoo nó đã được thiết lập rằng strpose === 0 là một giải pháp khủng khiếp nếu haystack lớn và kim không tồn tại.
Salman A

243

Cập nhật ngày 23 tháng 8 năm 2016

Chức năng

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

Xét nghiệm

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

Kết quả (PHP 7.0.9)

(Sắp xếp nhanh nhất đến chậm nhất)

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

Kết quả (PHP 5.3,29)

(Sắp xếp nhanh nhất đến chậm nhất)

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

startedwith_benchmark.php


3
Nếu các chuỗi không trống, như trong các thử nghiệm của bạn, điều này thực sự nhanh hơn (20-30%): function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}Tôi đã thêm một câu trả lời bên dưới.
FrancescoMM

3
@Jronny Vì 110 nhỏ hơn 133 ... ??
mở

2
Chết tiệt, tôi không biết những gì đã đi vào đầu tôi lúc đó. Hoàn toàn thiếu ngủ.
Jronny

1
@mpen, tôi nhận thấy không phải con voi nào cả :(
Visman

1
Những bài kiểm tra này không tốt trong việc kiểm tra hiệu suất. Những gì bạn đang làm là sử dụng chuỗi ngẫu nhiên như kim. Trong 99,99% trường hợp sẽ KHÔNG có kết quả khớp. Hầu hết các hàm sẽ thoát sau khi khớp byte đầu tiên. Còn những trường hợp khi một trận đấu được tìm thấy thì sao? Hàm nào mất ít thời gian nhất để kết thúc trận đấu thành công? Còn trường hợp 99% kim khớp với nhau nhưng không phải là vài byte cuối cùng thì sao? Hàm nào mất ít thời gian nhất để kết luận không khớp?
Salman A

137

Tất cả các câu trả lời dường như cho đến nay để làm vô số việc không cần thiết, strlen calculations, string allocations (substr), vv 'strpos''stripos'chức năng trả lại chỉ số của sự xuất hiện đầu tiên của $needlenăm $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()chức năng có lỗi. Dòng đầu tiên của nó phải là (không có -1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma

6
Điều strlen () không cần thiết. Trong trường hợp chuỗi không bắt đầu với kim đã cho thì mã ur sẽ không cần thiết quét toàn bộ haystack.
AppleGrew

5
@Mark yea, chỉ kiểm tra phần đầu nhanh hơn rất nhiều, đặc biệt nếu bạn đang làm gì đó như kiểm tra các loại MIME (hoặc bất kỳ nơi nào khác mà chuỗi bị ràng buộc là lớn)
chacham15

2
@mark Tôi đã thực hiện một số điểm chuẩn với 1000 char haystack và 10 hoặc 800 char kim và strpose nhanh hơn 30%. Làm điểm chuẩn của bạn trước khi nói rằng một cái gì đó nhanh hơn hay không ...
wdev

7
Bạn nên cân nhắc việc trích dẫn kim như strpos($haystack, "$needle", 0)thể có bất kỳ cơ hội nào không phải là một chuỗi (ví dụ: nếu nó đến từ json_decode()). Mặt khác, hành vi mặc định [lẻ] strpos()có thể gây ra kết quả không mong muốn: " Nếu kim không phải là chuỗi, nó được chuyển đổi thành số nguyên và được áp dụng làm giá trị thứ tự của một ký tự. "
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);
}

Tín dụng vào :

Kiểm tra xem một chuỗi kết thúc bằng một chuỗi khác

Kiểm tra nếu một chuỗi bắt đầu bằng một chuỗi khác


1
strtolower không phải là cách tốt nhất để tạo ra các hàm không phân biệt chữ hoa chữ thường. Trong một số vỏ địa phương phức tạp hơn chỉ trên và dưới.
Sander Rijken

8
Tôi thấy phàn nàn và không có giải pháp nào ... Nếu bạn sẽ nói nó tệ, thì bạn nên đưa ra một ví dụ về việc nó sẽ như thế nào.
KdgDev

2
@WebDevHobo: đó là lý do tại sao tôi đã thêm một câu trả lời một ngày trước khi nhận xét của bạn. Đối với mã của bạn, strcasecmp thực sự là điều đúng đắn.
Sander Rijken

29

Các hàm regex ở trên, nhưng với các chỉnh sửa khác cũng được đề xuất ở trên:

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

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

2
trong php cho các hoạt động chuỗi, thứ tự của các tham số là $ haystack, $ kim. các hàm này ngược và hoạt động giống như các hàm mảng trong đó thứ tự thực sự là $ kim, $ haystack.
Andy

29

Câu hỏi này đã có nhiều câu trả lời, nhưng trong một số trường hợp, bạn có thể giải quyết cho một cái gì đó đơn giản hơn tất cả chúng. Nếu chuỗi bạn đang tìm kiếm được biết đến (mã hóa cứng), bạn có thể sử dụng các biểu thức thông thường mà không cần trích dẫn, v.v.

Kiểm tra xem một chuỗi bắt đầu bằng 'ABC':

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

kết thúc bằng 'ABC':

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

Trong trường hợp đơn giản của tôi, tôi muốn kiểm tra xem một chuỗi kết thúc bằng dấu gạch chéo:

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

Ưu điểm: vì nó rất ngắn và đơn giản, bạn không phải xác định hàm (chẳng hạn như endsWith()) như được hiển thị ở trên.

Nhưng một lần nữa - đây không phải là một giải pháp cho mọi trường hợp, chỉ là điều này rất cụ thể.


bạn không cần phải cứng mã chuỗi. regex có thể là động.
Ryan

2
@ chính nó, nhưng nếu chuỗi không được mã hóa cứng, bạn phải thoát nó. Hiện tại có 2 câu trả lời cho câu hỏi này. Điều này là dễ dàng, nhưng nó làm phức tạp mã chỉ một chút. Vì vậy, quan điểm của tôi là đối với các trường hợp rất đơn giản, nơi có thể mã hóa cứng, bạn có thể giữ nó đơn giản.
noamtm

1
Bạn cũng không phải thoát dấu gạch chéo, bạn có thể bọc regex trong một số ký tự khác, như @vậy, để dấu gạch chéo ( /) không phải thoát. Xem ví dụ # 3 tại đây: php.net/manual/en/feft.preg-match.php .
cjbarth

Cảm ơn @cjbarth. Thay đổi câu trả lời của tôi cho phù hợp. BTW, "#" là ví dụ được đưa ra trong php.net/manual/en/regapi.reference.d006wr.php khi xử lý dấu gạch chéo.
noamtm

23

Nếu tốc độ là quan trọng đối với bạn, hãy thử điều này. (Tôi tin rằng đó là phương pháp nhanh nhất)

Chỉ hoạt động cho chuỗi và nếu $ haystack chỉ có 1 ký tự

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
đây có lẽ là câu trả lời hiệu quả nhất vì không sử dụng bất kỳ chức năng nào như phụ, chỉ là chuỗi thông thường ...

Nó có thể kiểm tra xem chuỗi có ít nhất một ký tự không và có hai tham số được hoán đổi
a1an

1
Sáng tạo. Kim tiêm có chứa cỏ khô. BTW có một số suy yếu xấu xí với: endsWithChar('','x')nhưng kết quả là chính xác
Tino

18

Đây là hai hàm không giới thiệu một chuỗi tạm thời, có thể hữu ích khi kim rất lớn:

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 Hoạt động kể từ PHP5.1 và IMHO trả lời tốt nhất. Nhưng endsWidthnên làm return $needle==='' || substr_compare(... để nó hoạt động như mong đợi -strlen($needle)===0, mà không cần sửa, sẽ endsWith('a','')quay trở lạifalse
Tino

@Tino Cảm ơn ... Tôi thực sự cảm thấy đó là một lỗi substr_compare(), vì vậy tôi đã thêm một PR để khắc phục điều đó :)
Ja͢ck

3
Cuộc gọi endsWith('', 'foo')sẽ kích hoạt Cảnh báo: Thời gian cơ sở (): Vị trí bắt đầu không thể vượt quá độ dài chuỗi ban đầu. Có thể đó là một lỗi khác substr_compare(), nhưng để tránh nó, bạn cần kiểm tra trước như ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_

@gx_ Không cần làm chậm với nhiều mã hơn. Chỉ cần sử dụng return $needle === '' || @substr_compare(.. để ngăn chặn cảnh báo này.
Tino

17

Kết thúc nhanh nhất với giải pháp With ():

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

Điểm chuẩn:

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

Kết quả điểm chuẩn:

10000000x endsWith: 1.5760900974274 seconds # This answer
10000000x endsWith2: 3.7102129459381 seconds # Accepted answer
10000000x endsWith3: 1.8731069564819 seconds # Second most voted answer
10000000x endsWith4: 2.1521229743958 seconds # Regex answer

3
+1 để dành thời gian để so sánh các giải pháp khác nhau và thực sự đánh giá chúng! bạn cũng nên đề cập đến phiên bản PHP nào bạn đã sử dụng, vì việc tối ưu hóa được thực hiện khi ngôn ngữ phát triển! Tôi đã thấy những cải tiến đáng kể về các chức năng so sánh chuỗi từ phiên bản PHP này sang phiên bản khác :)
Barshe Deliens

1
lặp lại @ChristopheDeliens và yêu cầu của anh ấy để cung cấp phiên bản PHP. Tôi đã chạy thử nghiệm vào ngày 7.3.2 và nhận được kết quả tương tự FWIW.
Jeff

16

Tôi nhận ra điều này đã được hoàn thành, nhưng bạn có thể muốn xem strncmp vì nó cho phép bạn đặt độ dài của chuỗi để so sánh với, vì vậy:

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

Làm thế nào bạn sẽ làm endswith với điều này?
mở

@Mark - bạn có thể xem câu trả lời được chấp nhận, nhưng tôi thích sử dụng strncmp chủ yếu vì tôi nghĩ nó an toàn hơn.
James Black

Ý tôi là với strncmp cụ thể. Bạn không thể chỉ định một phần bù. Điều đó có nghĩa là hàm endWith của bạn sẽ phải sử dụng một phương thức hoàn toàn khác.
mở

@ Mark - Đối với endsWith tôi sẽ chỉ cần sử dụng strrpos ( php.net/manual/en/function.strrpos.php ), nhưng, nói chung, bất cứ lúc nào bạn đi đến sử dụng strncmp strcmp có lẽ là một lựa chọn an toàn hơn.
James Black

11

Bạn có thể sử dụng strposstrrpos

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

1
Bạn có nên sử dụng ba bằng ở đây strpos($sHaystack, $sNeedle) == 0như thế này strpos($sHaystack, $sNeedle) === 0? Tôi thấy một lỗi, khi false == 0đánh giá true.
Kalyan

11

Đây là phiên bản an toàn nhiều byte của câu trả lời được chấp nhận, nó hoạt động tốt cho các chuỗi UTF-8:

function startsWith($haystack, $needle)
{
    $length = mb_strlen($needle, 'UTF-8');
    return (mb_substr($haystack, 0, $length, 'UTF-8') === $needle);
}

function endsWith($haystack, $needle)
{
    $length = mb_strlen($needle, 'UTF-8');
    return $length === 0 ||
        (mb_substr($haystack, -$length, $length, 'UTF-8') === $needle);
}

2
Tôi khá chắc chắn rằng đây chỉ là một sự lãng phí CPU. tất cả những gì bạn cần kiểm tra, đối với StarstWith và EndsWith, chỉ là kiểm tra xem các byte có khớp không, và đó chính xác là những gì câu trả lời được chấp nhận đang làm. Điều này làm lãng phí thời gian tính toán số ký tự utf8 của kim và vị trí của ký tự utf8 thứ n của đống cỏ khô là .. tôi nghĩ, không chắc chắn 100%, đây chỉ là một sự lãng phí của cpu. bạn có thể đưa ra một trường hợp thử nghiệm thực tế khi câu trả lời được chấp nhận không thành công, và điều này không?
hanshenrik

2
@hanshenrik - nó có thể xảy ra btw, trong trường hợp rất hiếm khi bạn tìm kiếm một chuỗi chứa các byte giống như UTF8 nhưng thiếu một nửa ký tự cuối cùng. Giống như, bạn có unicode C5 91 (chữ "") và bạn tìm kiếm C5 (chữ "") nó không nên cho bạn khớp. Mặt khác, chắc chắn, tại sao bạn sẽ tìm kiếm một hayfack utf cho một kim không utf ... Nhưng đối với kiểm tra chống đạn, điều này phải được coi là một khả năng.
dkellner

Trong startsWithđó nên là$length = mb_strlen($needle, 'UTF-8');
Thomas Kekeisen

2
@ThomasKekeisen Cảm ơn, đã sửa nó.
Vahid Amiri

8

Ngắn và dễ hiểu một lớp lót mà không cần biểu thức thông thường.

startedWith () là thẳng về phía trước.

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

endWith () sử dụng strrev () hơi lạ mắt và chậm:

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

@FrancescoMM: strpose không phải là "công cụ phù hợp" ... Tại sao? "Công cụ phù hợp" sau đó là gì? EDIT: Tôi đọc câu trả lời của bạn dưới đây. Tôi nghĩ lập trình giống như phát minh sử dụng các tài nguyên bạn có .. Vì vậy, không có đúng hay sai ... chỉ hoạt động hoặc không hoạt động ... hiệu suất là thứ yếu.
Fr0zenFyr

"bởi vì nó là một công cụ để tìm kiếm, không phải để so sánh?" Công dân. Aristoteles
FrancescoMM

7

Tập trung vào startwith, nếu bạn chắc chắn các chuỗi không trống, hãy thêm một bài kiểm tra vào char đầu tiên, trước khi so sánh, strlen, v.v., tăng tốc mọi thứ lên một chút:

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

Đó là bằng cách nào đó (20% -30%) nhanh hơn. Thêm một thử nghiệm char khác, như $ haystack {1} === $ kim {1} dường như không tăng tốc mọi thứ nhiều, thậm chí có thể chậm lại.

===có vẻ nhanh hơn == toán tử (a)?b:ccó điều kiện dường như nhanh hơnif(a) b; else c;


Đối với những người hỏi "tại sao không sử dụng strpose?" gọi các giải pháp khác là "công việc không cần thiết"


strpose là nhanh, nhưng nó không phải là công cụ phù hợp cho công việc này.

Để hiểu, đây là một mô phỏng nhỏ làm ví dụ:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

Những gì máy tính làm "bên trong"?

    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

Giả sử strlen không lặp lại toàn bộ chuỗi (nhưng ngay cả trong trường hợp đó) thì điều này không thuận tiện chút nào.


Chỉ tăng tốc nếu các ký tự đầu tiên khác nhau.
Ja͢ck

2
@Jack vâng, tất nhiên, ý tưởng là về mặt thống kê đã xảy ra, do đó, tốc độ tăng tổng thể là 20% -30% so với toàn bộ thử nghiệm (bao gồm cả các trường hợp không khác nhau). Bạn đạt được rất nhiều khi chúng khác biệt và mất rất ít khi chúng không. Trung bình bạn đạt được 30% (thay đổi tùy theo bộ, nhưng chủ yếu là bạn đạt được tốc độ trong các bài kiểm tra lớn)
FrancescoMM

"Nhưng nó không phải là công cụ phù hợp cho công việc này" ... Bất kỳ trích dẫn nào?
Fr0zenFyr

1
WTF. Tôi liệt kê tất cả các quy trình dưới đây tôi nên trích dẫn ai, nhiều hơn thế? Bạn có sử dụng hàm tìm kiếm cho đến hết chuỗi để cho bạn biết rằng ký tự nắm tay không phải là 'a' không? Làm ai quan tâm? Đây không phải là công cụ phù hợp bởi vì nó là một công cụ để tìm kiếm, không phải để so sánh, không cần trích dẫn Aristoteles để nêu rõ ràng!
FrancescoMM

6

Tôi hy vọng rằng câu trả lời dưới đây có thể hiệu quả và cũng đơn giản:

$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

Tôi thường kết thúc với một thư viện như undererscore-php ngày nay.

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   

Thư viện có đầy đủ các chức năng tiện dụng khác.


6

Câu trả lời của mpen là vô cùng kỹ lưỡng, nhưng, thật không may, điểm chuẩn được cung cấp có một sự giám sát rất quan trọng và bất lợi.

Bởi vì mỗi byte trong kim và đống cỏ là hoàn toàn ngẫu nhiên, xác suất một cặp kim-haystack sẽ khác nhau trên byte đầu tiên là 99,609375%, có nghĩa là, trung bình, khoảng 99609 trong số 100000 cặp sẽ khác nhau ở byte đầu tiên . Nói cách khác, điểm chuẩn rất thiên về các startswithtriển khai kiểm tra byte đầu tiên một cách rõ ràng, nhưstrncmp_startswith2 .

Nếu vòng lặp tạo thử nghiệm được thực hiện như sau:

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ết quả điểm chuẩn cho một câu chuyện hơi khác:

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

Tất nhiên, điểm chuẩn này có thể vẫn không hoàn toàn thiên vị, nhưng nó cũng kiểm tra hiệu quả của các thuật toán khi được cung cấp một phần kim phù hợp.


5

Nói ngắn gọn:

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

Chỉ là một đề nghị:

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

Dòng bổ sung đó, so sánh ký tự đầu tiên của chuỗi, có thể khiến trường hợp sai trở lại ngay lập tức , do đó làm cho nhiều so sánh của bạn nhanh hơn rất nhiều (nhanh hơn 7 lần khi tôi đo). Trong trường hợp thực tế, bạn hầu như không phải trả giá về hiệu suất cho dòng đơn đó vì vậy tôi nghĩ rằng nó đáng để bao gồm. (Ngoài ra, trong thực tế, khi bạn kiểm tra nhiều chuỗi cho một đoạn bắt đầu cụ thể, hầu hết các so sánh sẽ thất bại vì trong trường hợp điển hình là bạn đang tìm kiếm thứ gì đó.)


2
Lỗi trong mã của bạn: startsWith("123", "0")chotrue
Tino

Yup, xấu! $ Kiểm tra đã xảy ra. Lấy làm tiếc! (Chỉ muốn minh họa khái niệm trong dòng 3)
dkellner 20/07/18

4

Các substrchức năng có thể trở lại falsetrong nhiều trường hợp đặc biệt, vì vậy đây là phiên bản của tôi, trong đó giao dịch với những vấn đề này:

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
}

Các xét nghiệm ( truecó nghĩa là tốt):

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

Ngoài ra, substr_comparechức năng cũng đáng tìm kiếm. http://www.php.net/manual/en/feft.substr-compare.php



4

Tôi sẽ làm nó như thế này

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

Quên trả lại sai nếu nó không khớp. Errgo không chính xác vì giá trị trả về của hàm không nên là 'giả định', nhưng tôi biết bạn sẽ làm gì sau ít nhất so với các câu trả lời khác.
Spoo

3

Dựa trên câu trả lời của James Black, đây là phiên bản kết thúc của nó:

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

}

Lưu ý: Tôi đã hoán đổi phần if-other cho hàm startWith của James Black, bởi vì strncasecmp thực sự là phiên bản không phân biệt chữ hoa chữ thường của strncmp.


2
Lưu ý rằng đó strrev()sáng tạo nhưng rất tốn kém, đặc biệt là nếu bạn có chuỗi nói ... 100Kb.
Alexis Wilke

Sử dụng ===thay vì ==để chắc chắn. 0tương đương với rất nhiều thứ trong PHP.
nawfal

3

Tại sao không phải là sau đây?

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

Đầu ra:

Tìm thấy giá trị khi bắt đầu valuehaystack!

Hãy ghi nhớ, strpossẽ trả về false nếu không tìm thấy kim trong đống cỏ khô và sẽ trả về 0 nếu và chỉ khi tìm thấy kim ở chỉ số 0 (AKA bắt đầu).

Và đây là kết thúc với:

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

Trong kịch bản này, không cần hàm startWith () như

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

sẽ trả lại đúng hoặc sai chính xác.

Có vẻ kỳ lạ, nó đơn giản với tất cả các chức năng hoang dã đang lan tràn ở đây.


3
Có vẻ kỳ lạ là nếu bạn đang tìm kiếm "xy" bên trong chuỗi "abcdefghijklmxyz" thay vì chỉ so sánh "x" với "a" và trả về FALSE, bạn sẽ tìm mọi ký tự từ "a" đến "m" rồi cuối cùng tìm thấy "xy" bên trong chuỗi và cuối cùng bạn trả về SAI vì vị trí của nó không phải là số không! Đây là những gì bạn đang làm, và nó kỳ quặc và hoang dã hơn bất kỳ chức năng tràn lan nào khác ở đây.
FrancescoMM

Sự đơn giản là trong cách gõ chứ không phải logic.
Kade Hafen

Đó không phải là logic nhiều, đó là sự tối ưu hóa có thể có mà Francsco đã chỉ ra. Sử dụng strpos()sẽ chậm trừ khi nó phù hợp. strncmp()sẽ tốt hơn nhiều trong trường hợp này.
Alexis Wilke

Khi bạn đang thực hiện các chức năng cấp thấp như vậy, bạn thường muốn tìm giải pháp tối ưu hóa tốc độ nhất, bất kể phức tạp đến đâu, vì điều này sẽ được gọi là hàng triệu lần. Mỗi micro giây bạn được hoặc mất ở đây sẽ tạo ra sự khác biệt rất thực. Vì vậy, tốt hơn hết hãy loại bỏ nó (và sau đó quên đi sự phức tạp, bây giờ bạn có chức năng), thay vì tìm kiếm ngoại hình và mất thời gian khủng khiếp sau đó khi bạn thậm chí không biết điều gì đã xảy ra. Hãy tưởng tượng kiểm tra một chuỗi 2GB không khớp.
dkellner

3

Nhiều câu trả lời trước cũng sẽ hoạt động tốt. Tuy nhiên, điều này có thể ngắn như bạn có thể làm cho nó và để nó làm những gì bạn mong muốn. Bạn chỉ cần nói rằng bạn muốn nó 'trở về đúng'. Vì vậy, tôi đã bao gồm các giải pháp trả về boolean đúng / sai và văn bản đúng / sai.

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

Thật. Tuy nhiên, Peter đã yêu cầu một chức năng sẽ hoạt động với các chuỗi ký tự. Tuy nhiên, tôi đã cập nhật câu trả lời của mình để xoa dịu bạn.
wynshaft

Sau khi chỉnh sửa, giải pháp của bạn bây giờ đã hoàn toàn lỗi thời. Nó trả về 'true''false'dưới dạng chuỗi, cả hai đều truetheo nghĩa boolean. Mặc dù đó là một mô hình tốt cho một cái gì đó như underhanded.xcott.com ;)
Tino

Chà, Peter chỉ nói rằng anh ấy muốn nó trở lại 'đúng'. Vì vậy, tôi nghĩ rằng tôi sẽ trả lại những gì anh ấy yêu cầu. Tôi đã thêm cả hai phiên bản, chỉ trong trường hợp đó không phải là thứ anh ấy muốn.
wynshaft

2

Bạn cũng có thể sử dụng các biểu thức thông thường:

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

3
$ kim nên được thoát với preg_quote($needle, '/').
Timo Tijhof

2

Không sao chép và không thực hiện vòng lặp:

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

việc này sẽ nhanh hơn nhiều so với việc thực hiện của MrHus! tôi có thể điểm chuẩn nó
hanshenrik

1

Đây là một giải pháp hiệu quả cho PHP 4. Bạn có thể nhận được kết quả nhanh hơn nếu trên PHP 5 bằng cách sử dụng substr_comparethay vì 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

Bạn có thể sử dụng chức năng fnmatch cho việc này.

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

cảnh báo, không an toàn nhị phân và thậm chí không an toàn đối với kim có chứa ký tự đại diện = /
hanshenrik
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.