PHP 中的 startsWith() 和 endsWith() 函数

startsWith() and endsWith() functions in PHP

提问人:Ali 提问时间:5/7/2009 最后编辑:IvarAli 更新时间:10/15/2023 访问量:985070

问:

我怎样才能编写两个函数,如果它以指定的字符/字符串开头或以它结尾,它们将接受一个字符串并返回?

例如:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
PHP 字符串

评论

26赞 Gras Double 12/7/2014
参见 Laravel 的 Str 类 startsWith() 和 endsWith() 以获取经过充分测试的方法。已经遇到过边缘情况,因此广泛使用此代码是一个优势。
1赞 caw 7/27/2016
您可能会发现 s($str)->startsWith('|') 和 s($str)->endsWith('}') 很有帮助,正如这个独立库中所找到的那样。
5赞 Álvaro González 5/15/2017
警告:这里的大多数答案在多字节编码(如 UTF-8)中都是不可靠的。
0赞 Gras Double 5/23/2017
根据我的上述评论,您可以确保使用最新版本(截至今天,5.4)。值得注意的是,startsWith() 已针对大型干草堆字符串进行了优化。
10赞 Jsowa 10/2/2020
PHP 8.0 为这项工作引入了新的方法,并且: stackoverflow.com/a/64160081/7082164str_starts_withstr_end_with

答:

53赞 KdgDev 5/7/2009 #1
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);
}

归功于

检查字符串是否以另一个字符串结尾

检查字符串是否以另一个字符串开头

评论

2赞 Sander Rijken 5/14/2009
strtolower 不是使函数不区分大小写的最佳方法。在某些地区,外壳比上层和下层更复杂。
9赞 KdgDev 5/14/2009
我看到抱怨,没有解决方案......如果你要说它很糟糕,那么你也应该举一个例子来说明它应该如何。
2赞 Sander Rijken 8/6/2010
@WebDevHobo:这就是为什么我在您发表评论的前一天自己添加了一个答案。对于您的代码,strcasecmp 确实是正确的做法。
1857赞 MrHus 5/7/2009 #2

PHP 8.0 及更高版本

从 PHP 8.0 开始,你可以使用

str_starts_with 手动

str_ends_with 手动

echo str_starts_with($str, '|');

PHP 8.0 之前的版本

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

评论

20赞 MrHus 4/13/2012
我会说 endsWith('foo', '') == false 是正确的行为。因为 foo 不会一无所获。“Foo”以“o”、“oo”和“Foo”结尾。
153赞 Rok Kralj 6/11/2012
EndsWith可以写得更短:return substr($haystack, -strlen($needle))===$needle;
10赞 Ja͢ck 2/21/2014
@RokKralj 但前提是不为空。$needle
15赞 mxxk 1/23/2015
您可以通过将第三个参数作为第三个参数传递给 : 来完全避免这种情况。这通过返回一个空字符串而不是整个 .if$lengthsubstrreturn (substr($haystack, -$length, $length);$length == 0$haystack
22赞 19Gerhard85 4/25/2016
@MrHus我建议使用多字节安全函数,例如 mb_strlen 和 mb_substr
147赞 Sander Rijken 5/14/2009 #3

到目前为止,所有答案似乎都做了大量不必要的工作,,等等。和函数返回 in 中第一次出现的索引:strlen calculationsstring allocations (substr)'strpos''stripos'$needle$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赞 Enrico Detoma 8/6/2010
endsWith()函数有错误。它的第一行应该是(不带 -1):$expectedPosition = strlen($haystack) - strlen($needle);
7赞 AppleGrew 1/4/2011
strlen() 的事情并非没有必要。如果字符串不是以给定的针开头,那么您的代码将不必要地扫描整个大海捞针。
6赞 chacham15 9/26/2011
@Mark是的,只检查开头会快得多,特别是当你正在做一些事情,比如检查MIME类型(或任何其他字符串肯定会很大的地方)
2赞 wdev 8/6/2012
@mark我用 1000 个焦炭干草堆和 10 或 800 个焦炭针做了一些基准测试,strpos 的速度提高了 30%。在声明某些东西更快或不更快之前做你的基准测试......
8赞 quietmint 12/3/2012
您应该强烈考虑引用针头,例如它是否可能还不是字符串(例如,如果它来自 )。否则,[odd] 默认行为可能会导致意外结果:“如果 needle 不是字符串,则将其转换为整数并应用为字符的序号值。strpos($haystack, "$needle", 0)json_decode()strpos()"
39赞 tridian 6/20/2009 #4

上面的正则表达式功能,但上面也建议了其他调整:

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

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

评论

2赞 Andrew 12/2/2014
在 PHP 中,对于字符串操作,参数的顺序是 $haystack、$needle。这些函数是向后的,就像数组函数一样,其中的排序实际上是$needle的,$haystack。
19赞 James Black 9/17/2009 #5

我意识到这已经完成了,但你可能想看看strncmp,因为它允许你把字符串的长度进行比较,所以:

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

评论

0赞 mpen 8/26/2011
你会如何做到这一点?
0赞 James Black 8/27/2011
@Mark - 你可以看看公认的答案,但我更喜欢使用 strncmp,主要是因为我认为它更安全。
0赞 mpen 8/27/2011
我的意思是特别是strncmp。不能指定偏移量。这意味着您的 endsWith 函数将不得不使用完全不同的方法。
0赞 James Black 8/27/2011
@Mark - 为了达到目的,我只会使用 strrpos (php.net/manual/en/function.strrpos.php),但是,一般来说,任何时候你去使用 strcmp strncmp 可能是一个更安全的选择。
3赞 bobo 10/30/2009 #6

根据詹姆斯·布莱克的回答,以下是它的结尾版本:

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

}

注意:我已将 if-else 部分换成 James Black 的 startsWith 函数,因为 strncasecmp 实际上是 strncmp 的不区分大小写的版本。

评论

2赞 Alexis Wilke 6/25/2014
请注意,这很有创意,但成本很高,特别是如果你有一串说......100Kb的。strrev()
0赞 nawfal 1/29/2016
使用而不是确定。 相当于PHP中的很多东西。=====0
29赞 lepe 12/15/2009 #7

如果速度对您很重要,请尝试一下。(我相信这是最快的方法)

仅适用于字符串,并且$haystack只有 1 个字符

function startsWithChar($needle, $haystack)
{
   return ($needle === $haystack[0]);
}

function endsWithChar($needle, $haystack)
{
   return ($needle === $haystack[strlen($haystack) - 1]);
}

$str='|apples}';
echo startsWithChar('|',$str); //Returns true
echo endsWithChar('}',$str); //Returns true
echo startsWithChar('=',$str); //Returns false
echo endsWithChar('#',$str); //Returns false

评论

1赞 8/1/2013
这可能是最有效的答案,因为不使用任何函数作为额外的功能,只是通常的字符串......
0赞 a1an 5/15/2018
它可能会检查字符串是否至少有一个字符,并且交换了两个参数
2赞 Tino 7/20/2018
创造性。含有干草堆的针头。顺便说一句,有一些丑陋的减弱:,但结果是正确的endsWithChar('','x')
1赞 Heider Sati 8/17/2020
我喜欢你的回答,但很有趣,...针和大海捞针是相反的:)......也就是说,你会在大海捞针中搜索,因此,它应该是:return ($needle === $haystack[0]);,但很好的回答,谢谢!
1赞 lepe 8/17/2020
@HeiderSati:观察力很强!这就是@Tino所说的......我没有给予足够的关注。谢谢!我修好了。:)Creative. Needles which contain haystacks.
1赞 Freeman 12/22/2010 #8

您还可以使用正则表达式:

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

评论

3赞 Timo Tijhof 11/16/2012
$needle 应使用 .preg_quote($needle, '/')
2赞 Patrick Smith 4/19/2011 #9

这是 PHP 4 的有效解决方案。如果在 PHP 5 上使用而不是 .substr_comparestrcasecmp(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;
}
9赞 Dan 6/29/2011 #10

简短易懂的单行代码,没有正则表达式。

startsWith() 是直截了当的。

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

endsWith() 使用略显花哨和缓慢的 strrev():

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

评论

0赞 Fr0zenFyr 3/24/2018
@FrancescoMM:strpos 不是“正确的工具”......为什么?那么什么是“正确的工具”呢?编辑:我在下面读了你的回答。我以为编程就像是利用你所拥有的资源进行发明。所以没有对错之分......只工作或不工作......性能是次要的。
0赞 FrancescoMM 3/24/2018
“因为它是搜索的工具,而不是比较的工具?” 同前。 亚里士多德
260赞 mpen 8/24/2011 #11

已更新 23-Aug-2016

功能

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

测试

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

结果 (PHP 7.0.9)

(从快到慢排序)

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

结果 (PHP 5.3.29)

(从快到慢排序)

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赞 FrancescoMM 7/28/2013
如果字符串不是空的,就像在你的测试中一样,这实际上会以某种方式(20-30%)更快:我在下面添加了一个回复。function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}
3赞 mpen 12/3/2014
@Jronny 因为 110 小于 133...??
2赞 Jronny 12/18/2014
的,我不知道那次我脑子里想的是什么。导致睡眠不足。
1赞 Visman 11/21/2017
@mpen,我根本没有注意到大象:(
3赞 Thanh Trung 4/16/2018
$haystack[0]如果不使用 isset 对其进行测试,将抛出通知错误。针头也是如此。但是,如果添加测试,则会降低其性能
4赞 biziclop 2/21/2012 #12

该函数可以在许多特殊情况下返回,所以这是我的版本,它处理这些问题:substrfalse

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
}

测试(表示良好):true

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

此外,该功能也值得一看。http://www.php.net/manual/en/function.substr-compare.phpsubstr_compare

8赞 Vincent Pazeller 4/20/2012 #13

总之:

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

function endsWith($str, $needle){
   $length = strlen($needle);
   return !$length || substr($str, - $length) === $needle;
}
1104赞 Salman A 5/7/2012 #14

您可以使用substr_compare函数来检查 start-with 和 ends-with:

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

这应该是 PHP 7(基准测试脚本)上最快的解决方案之一。针对 8KB 干草堆、各种长度的针以及完整、部分和无匹配情况进行了测试。strncmp 的 starts-with 速度更快,但它不能检查 ends-with。

评论

0赞 Salman A 4/21/2016
请注意,@DavidWallace和@FrancescoMM评论适用于此答案的旧版本。当前答案使用如果针与大海捞针的开头不匹配,则(应该)立即失败。strrpos
2赞 Welbog 4/21/2016
我不明白。基于 php.net/manual/en/function.strrpos.php:“如果值为负数,则搜索将从字符串末尾的多个字符开始,向后搜索。这似乎表明我们从字符 0 开始(由于 )并从那里向后搜索?这难道不意味着你没有搜索任何东西吗?我也不明白其中的部分。我猜这依赖于 PHP 的一个怪癖,其中一些值是“真实的”,而另一些是“虚假的”,但在这种情况下它是如何工作的?-strlength($haystack)!== false
3赞 Salman A 4/21/2016
@Welbog:例如,haystack = needle =,并且使用搜索从第一个开始。现在我们在这里没有成功的匹配(找到 x 而不是 y),我们不能再倒退了(我们在字符串的开头),搜索立即失败。关于使用 -- 在上面的例子中将返回 0 或 false,而不是其他值。同样,在上面的示例中可以返回(预期位置)或 false。我选择了一致性,但您可以分别在函数中使用 和。xxxyyyyyystrrposx!== falsestrrposstrpos$temp!== false=== 0=== $temp
0赞 Spoo 10/8/2016
这方面有很多不必要的工作。为什么不使用 strpos === 0 作为 startsWith。Down投票赞成将次优编码响应复杂化。
13赞 Salman A 10/8/2016
@spoo已经确定,如果大海捞针很大并且不存在针,则 strpos === 0 是一个可怕的解决方案。
4赞 Bill Effin Murray 6/30/2012 #15

为什么不是以下?

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

输出:

在valuehaystack的开头发现了价值!

请记住,如果在大海捞针中未找到针,则返回 false,当且仅当在索引 0(又名开头)找到针时,将返回 0。strpos

这里结束了:

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

在这种情况下,不需要函数 startsWith() 作为

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

将准确返回 true 或 false。

这似乎很奇怪,它就是这么简单,所有狂野的功能都在这里猖獗。

评论

3赞 FrancescoMM 1/23/2014
看起来很奇怪,如果你在字符串“abcdefghijklmxyz”中搜索“xy”,而不仅仅是将“x”与“a”进行比较并返回 FALSE,你查看从“a”到“m”的每个字符,然后最终在字符串中找到“xy”,最后你返回 FALSE,因为它的位置不为零!这就是你正在做的事情,它比这里的任何其他猖獗功能都奇怪和狂野。
0赞 Bill Effin Murray 1/24/2014
简单在于打字,而不是逻辑。
0赞 Alexis Wilke 7/2/2014
与其说是逻辑,不如说是 Francsco 指出的可能的优化。使用速度会很慢,除非匹配。 在这种情况下会好得多。strpos()strncmp()
0赞 dkellner 9/3/2018
当您执行此类低级功能时,您通常希望选择速度最优化的解决方案,无论多么复杂,因为这将被调用数百万次。你在这里获得或失去的每一微秒都会产生非常实际的差异。因此,最好调整一下它(然后忘记复杂性,现在你有了这个功能),而不是追求外观,并在你甚至不知道出了什么问题时浪费可怕的时间。想象一下,检查一个不匹配的 2GB 字符串。
5赞 user507410 6/4/2013 #16

这可能有效

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

来源: https://stackoverflow.com/a/4419658

10赞 FrancescoMM 7/28/2013 #17

专注于 startswith,如果您确定字符串不为空,则在第一个字符、比较之前、strlen 等处添加测试,可以加快速度:

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

它以某种方式(20%-30%)快。添加另一个字符测试,如 $haystack{1}===$needle{1} 似乎不会加快速度,甚至可能减慢速度。

===似乎比条件运算符更快==(a)?b:cif(a) b; else c;


对于那些问“为什么不使用strpos?”的人,称其他解决方案是“不必要的工作”


strpos 速度很快,但它不是这项工作的正确工具。

为了理解,这里有一个小小的模拟作为例子:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

计算机“内部”做什么?

    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 不迭代整个字符串(但即使在这种情况下),这也不方便。

评论

0赞 Ja͢ck 8/2/2013
只有当第一个字符不同时,才会有速度。
3赞 FrancescoMM 9/18/2013
@Jack是的,当然,这个想法是统计学上会发生这种情况,因此在整个测试集中(包括没有差异的情况)的加速通常为 20%-30%。当它们不同时,你会收获很多,而当它们不同时,你会失去很少。平均而言,您获得 30%(因设置而异,但大多数情况下,您在大型测试中会提高速度)
0赞 Fr0zenFyr 3/24/2018
“但它不是这项工作的正确工具”......有引文吗?
2赞 FrancescoMM 3/24/2018
跆拳道。我在下面列出了我应该引用的所有过程,更多?你会使用一个搜索到字符串末尾的函数来告诉你第一个字符不是“a”吗?谁在乎呢?这不是正确的工具,因为它是搜索工具,而不是比较工具,没有必要引用亚里士多德来陈述显而易见的事情!
23赞 Ja͢ck 8/2/2013 #18

下面是两个不引入临时字符串的函数,当针非常大时,这可能很有用:

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赞 Tino 2/21/2014
+1 自 PHP5.1 和恕我直言最佳答案以来有效。但应该做......因此,它按预期工作,如果没有修复程序,则返回endsWidthreturn $needle==='' || substr_compare(-strlen($needle)===0endsWith('a','')false
0赞 Ja͢ck 2/21/2014
@Tino谢谢......我觉得这实际上是一个错误,所以我添加了一个 PR 来修复该:)substr_compare()
3赞 gx_ 8/10/2015
调用会触发警告:“substr_compare():起始位置不能超过初始字符串长度”。也许这是另一个错误,但为了避免它,你需要一个预检查,比如......endsWith('', 'foo')substr_compare()|| (strlen($needle) <= strlen($haystack) && substr_compare() === 0);
0赞 Tino 7/20/2018
@gx_ 无需因代码过多而放慢速度。只需使用 ..以禁止显示此警告。return $needle === '' || @substr_compare(
8赞 Srinivasan.S 10/18/2013 #19

我希望下面的答案可能是有效的,也是简单的:

$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';
3赞 womplefrog 12/7/2013 #20

前面的许多答案也同样有效。但是,这可能尽可能短,并让它做你想做的事。你只是声明你希望它“返回真实”。因此,我包含了返回布尔值 true/false 和文本 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';
}

评论

0赞 womplefrog 10/24/2014
真。但是,Peter 要求一个可以处理字符串的函数。尽管如此,我已经更新了我的答案来安抚你。
0赞 Tino 10/26/2014
编辑后,您的解决方案现在已完全过时。它以字符串形式返回 和,这两者都是布尔意义上的。对于像 underhanded.xcott.com 这样的东西来说,这是一个很好的模式;)'true''false'true
0赞 womplefrog 10/27/2014
好吧,彼得只是说他希望它返回“真实”。所以我想我会归还他要求的东西。我已经添加了两个版本,以防万一这不是他想要的。
4赞 Jelle Keizer 10/7/2014 #21

我会这样做

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

评论

0赞 Spoo 10/8/2016
如果不匹配,则忘记返回 false。Errgo 不正确,因为函数的返回值不应该被“假设”,但我知道你至少在追求什么,至少与其他答案相比。
8赞 yuvilio 1/23/2015 #22

这些天我通常最终会使用像 underscore-php 这样的库。

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   

该库充满了其他方便的功能。

12赞 Lex Podgorny 5/12/2015 #23

您可以使用 strposstrrpos

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

评论

2赞 Kalyan 6/23/2017
你应该像这样在这里使用三等号吗?我看到一个错误,当计算到 .strpos($sHaystack, $sNeedle) == 0strpos($sHaystack, $sNeedle) === 0false == 0true
-1赞 ymakux 8/26/2015 #24
$ends_with = strrchr($text, '.'); // Ends with dot
$start_with = (0 === strpos($text, '.')); // Starts with dot

评论

5赞 Sam Bull 7/5/2016
根据文档,strrchr() 将返回从最后一次出现 '.' 到最后的字符串,这意味着如果 '.' 位于 $text 中的任何位置,则您的 $ends_with 将为真。因此ends_with应该是:('.' === strrchr($text, '.'))
6赞 Sam Bull 3/26/2017
由于您的答案是错误的,并且没有按照它声称的那样做,并且您拒绝接受我的编辑来修复您的答案,因此我对这个答案投了反对票,因为它“危险地不正确”。
47赞 noamtm 1/6/2016 #25

这个问题已经有很多答案,但在某些情况下,你可以满足于比所有答案更简单的东西。 如果您要查找的字符串是已知的(硬编码),则可以使用正则表达式而不使用任何引号等。

检查字符串是否以“ABC”开头:

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

以“ABC”结尾:

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

在我的简单案例中,我想检查字符串是否以斜杠结尾:

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

优点:由于它非常简短和简单,因此您不必定义如上所示的函数(例如 )。endsWith()

但同样,这并不是针对所有情况的解决方案,只是针对这个非常具体的情况。

评论

0赞 Ryan 5/15/2016
无需对字符串进行硬编码。正则表达式可以是动态的。
2赞 noamtm 5/16/2016
@self true,但如果字符串不是硬编码的,则必须对其进行转义。目前,这个问题有 2 个答案可以做到这一点。这很容易,但它会使代码稍微复杂一点。所以我的观点是,对于非常简单的情况,在硬编码的情况下,你可以保持简单。
7赞 dkellner 7/27/2016 #26

做得更快:

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

额外的一行,比较字符串的第一个字符,可以使 错误大小写会立即返回,因此会进行许多比较 速度快得多(我测量时快 7 倍)。在真实的情况下,您几乎不需要为这条线付出任何性能代价,所以我认为它值得一提。(此外,在实践中,当您为特定的起始块测试多个字符串时,大多数比较都会失败,因为在典型情况下,您正在寻找某些东西。

注意:下面 @Tino 评论中的错误已修复

至于字符串与整数

如果要强制字符串比较(即,您期望 startsWith(“1234”,12) 为 true),则需要进行一些类型转换:

function startsWith($haystack,$needle) {
    if($needle==="") return true;
    $haystack = (string)$haystack;
    $needle   = (string)$needle;
    if($haystack[0]<>$needle[0]) return false; // ------------------------- speed boost!
    return (0===substr_compare($haystack,$needle,0,strlen($needle)));
}

我不认为这是必要的,但这是一个有趣的边缘情况,导致诸如“布尔值是否以t开头?”这样的问题 - 所以你决定,但要确保你决定永远。

评论

2赞 Tino 7/20/2018
代码中的错误:给出startsWith("123", "0")true
0赞 dkellner 7/20/2018
是的,糟糕!$checking发生了。不好意思!(只是想说明第 3 行中的概念)
0赞 dkellner 12/17/2020
@Tino我想说我们现在可以删除这 2 条评论,你不同意吗?我的意思是,这一点,它已经修好了,已经 2 年了。
8赞 Veeno 9/21/2016 #27

mpen 的答案非常彻底,但不幸的是,提供的基准有一个非常重要且有害的疏忽。

因为针和干草堆中的每个字节都是完全随机的,所以针-干草堆对在第一个字节上不同的概率是 99.609375%,这意味着平均而言,100000 对中约有 99609 个在第一个字节上会不同。换句话说,基准测试严重偏向于显式检查第一个字节的实现,就像一样。startswithstrncmp_startswith2

如果测试生成循环按如下方式实现:

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

基准测试结果讲述了一个略有不同的故事:

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

当然,这个基准测试可能仍然不是完全无偏的,但它在给定部分匹配的针头时也测试了算法的效率。

15赞 Vahid Amiri 11/10/2017 #28

这是已接受答案的多字节安全版本,它适用于 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);
}

评论

3赞 hanshenrik 5/12/2018
我很确定这只是浪费 CPU。对于 StarstWith 和 EndsWith,您需要检查的只是检查字节是否匹配,而这正是接受的答案正在做的事情。这 1 浪费时间计算针的 UTF8 字符数,以及大海捞针的第 n 个 UTF8 字符的位置。我认为,在没有 100% 确定的情况下,这只是浪费 CPU。你能想出一个实际的测试用例,其中接受的答案失败了,而这并没有?
2赞 dkellner 9/8/2018
@hanshenrik - 顺便说一句,在极少数情况下,当您查找包含与 UTF8 相同字节但缺少最后一个字符一半的字符串时,可能会发生这种情况。比如,你有 unicode C5 91(字母“ő”),你寻找 C5(字母“Å”),它不应该给你匹配。另一方面,当然,你为什么要在 utf 大海捞针中搜索非 utf 针......但是对于防弹检查,必须考虑这是一种可能性。
0赞 Thomas Kekeisen 4/17/2019
在它应该是startsWith$length = mb_strlen($needle, 'UTF-8');
2赞 Vahid Amiri 4/18/2019
@ThomasKekeisen谢谢,修复了它。
0赞 Jon 10/30/2020
公认的(嗯,目前公认的)解决方案已经是多字节安全的。它实际上是二进制安全的,这是一个更强大的保证。
32赞 Lucas Bustamante 7/24/2018 #29

最快的 endsWith() 解决方案:

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

基准:

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

基准测试结果:

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

评论

5赞 Christophe Deliens 10/3/2018
+1 花时间比较不同的解决方案并实际对它们进行基准测试!您还应该提及您使用的 PHP 版本,因为优化是随着语言的发展而完成的!我已经看到从一个PHP版本到另一个PHP版本的字符串比较函数有了显着的改进:)
2赞 Jeff 2/20/2019
呼应@ChristopheDeliens和他提供PHP版本的请求。我在 7.3.2 上运行了您的测试,并得到了类似的结果 FWIW。
3赞 mazatwork 8/28/2019 #30

无复制和无实习循环:

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

评论

0赞 hanshenrik 5/11/2020
这应该比 MrHus 的实现快得多!我可能会对它进行基准测试
0赞 ya.teck 4/3/2020 #31

为此,您可以使用 fnmatch 函数。

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

评论

2赞 hanshenrik 5/11/2020
警告,不是二进制安全的,甚至对包含通配符的针头也不安全 =/
55赞 Jon 6/3/2020 #32

PHP 8 更新

PHP 8 包含新的 str_starts_withstr_ends_with 函数,最终为此问题提供了高效且方便的解决方案:

$str = "beginningMiddleEnd";
if (str_starts_with($str, "beg")) echo "printed\n";
if (str_starts_with($str, "Beg")) echo "not printed\n";
if (str_ends_with($str, "End")) echo "printed\n";
if (str_ends_with($str, "end")) echo "not printed\n";

此功能的 RFC 提供了更多信息,并讨论了明显(和不那么明显)的用户空间实现的优点和问题。

0赞 JSON 6/23/2022 #33
function startsWith($haystack, $needle)
{
    $length = mb_strlen($needle);
    return mb_substr($haystack, 0, $length) === $needle;
}

function endsWith($haystack, $needle)
{
    $length = mb_strlen($needle);
    if(!$length)
    {
     return true;
    }
    
    return mb_substr($haystack, -$length) === $needle;
}

字符串长度和字符定位是多字节与单字节可以发挥作用的两个示例。PHP 中的标准字符串是 char 数组,就像在 C 中一样,它们始终是单字节。因此,如果 UTF-16,普通 strlen 字符串 () 中的第三个字符实际上是第二个字符的开头。如果在字符串的开头寻找字符串是否可以是 JSON,这没什么大不了的,因为这是一个有效的单字节字符,例如在 ASCII 编码中,但如果检查字符串末尾的 emogi 👍 vs 😲 判断反应,那就另当别论了,因为它们是多字节字符。str[2]{{

-1赞 Toby Allen 9/29/2022 #34

拉拉维尔 9.0

如果你正在使用 Laravel,那么你可以执行以下操作(如果你没有使用 Laravel,你真的应该这样做)。

Str::of('a long string')->startsWith('a');
Str::of('a long string')->endsWith('string');

//true
//true
0赞 Maxim Rysevets 10/15/2023 #35

我的任务是为打开的CMS EffCore创建最近似的str_starts_with()polyfill,结果是以下代码:

namespace {
    if (!function_exists('str_starts_with')) {
        function str_starts_with($haystack, $needle) {
            if (is_null    ($haystack)) trigger_error('str_starts_with(): Passing null to parameter #1 ($haystack) of type string is deprecated in '.__FILE__, E_USER_DEPRECATED);
            if (is_null    ($needle)  ) trigger_error('str_starts_with(): Passing null to parameter #2 ($needle) of type string is deprecated in '.__FILE__, E_USER_DEPRECATED);
            if (is_array   ($haystack)) throw new TypeError('str_starts_with(): Argument #1 ($haystack) must be of type string, array given');
            if (is_object  ($haystack)) throw new TypeError('str_starts_with(): Argument #1 ($haystack) must be of type string, object given');
            if (is_resource($haystack)) throw new TypeError('str_starts_with(): Argument #1 ($haystack) must be of type string, resource given');
            if (is_array   ($needle)  ) throw new TypeError('str_starts_with(): Argument #2 ($needle) must be of type string, array given');
            if (is_object  ($needle)  ) throw new TypeError('str_starts_with(): Argument #2 ($needle) must be of type string, object given');
            if (is_resource($needle)  ) throw new TypeError('str_starts_with(): Argument #2 ($needle) must be of type string, resource given');
            if ((string)$needle === '') return true;
            return strpos((string)$haystack, (string)$needle) === 0;
        }
    }
}

测试数据:

str_starts_with('100', '')               # true
str_starts_with('100', '1')              # true
str_starts_with('100', '0')              # false
str_starts_with('100', 0)                # false
str_starts_with('100', 1)                # true
str_starts_with('100', 0.0)              # false
str_starts_with('100', 1.0)              # true
str_starts_with('100', true)             # true / (string)true  === '1'
str_starts_with('100', false)            # true / (string)false === ''
str_starts_with('100', null)             # Warning
str_starts_with('100', [])               # Exception
str_starts_with('100', [0])              # Exception
str_starts_with('100', [1])              # Exception
str_starts_with('100', new stdCLass)     # Exception
str_starts_with('100', new SomeClass)    # Exception
str_starts_with('100', fopen('resource') # Exception

str_starts_with('010', '')               # true
str_starts_with('010', '1')              # false
str_starts_with('010', '0')              # true
str_starts_with('010', 0)                # true
str_starts_with('010', 1)                # false
str_starts_with('010', 0.0)              # true
str_starts_with('010', 1.0)              # false
str_starts_with('010', true)             # false / (string)true  === '1'
str_starts_with('010', false)            # true  / (string)false === ''
str_starts_with('010', null)             # Warning
str_starts_with('010', [])               # Exception
str_starts_with('010', [0])              # Exception
str_starts_with('010', [1])              # Exception
str_starts_with('010', new stdCLass)     # Exception
str_starts_with('010', new SomeClass)    # Exception
str_starts_with('010', fopen('resource') # Exception


str_starts_with('',                  '1') # false
str_starts_with('100',               '1') # true
str_starts_with('010',               '1') # false
str_starts_with('001',               '1') # false
str_starts_with(0,                   '1') # false
str_starts_with(1,                   '1') # true
str_starts_with(0.0,                 '1') # false
str_starts_with(1.0,                 '1') # true
str_starts_with(true,                '1') # true  / (string)true  === '1'
str_starts_with(false,               '1') # false / (string)false === ''
str_starts_with(null,                '1') # Warning
str_starts_with([],                  '1') # Exception
str_starts_with([0],                 '1') # Exception
str_starts_with([1],                 '1') # Exception
str_starts_with(new stdCLass,        '1') # Exception
str_starts_with(new SomeClass,       '1') # Exception
str_starts_with(fofopen('resource'), '1') # Exception