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


1478

我该如何编写两个函数,这些函数将接受字符串并以指定的字符/字符串开头或以指定的字符串结尾?

例如:

$str = '|apples}';

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

19
有关经过良好测试的方法,请参见Laravel的Str类 startsWith()和endsWith()。遇到了极端情况,因此该代码的广泛使用是一个优势。
Gras Double

1
正如在独立库中所发现的那样,您可能会发现s($str)->startsWith('|')s($str)->endsWith('}')有所帮助。
caw 2016年

3
警告:此处的大多数答案在多字节编码(例如UTF-8)中都是不可靠的。
阿尔瓦罗·冈萨雷斯

遵循我的上述评论,您可以确保使用最新版本(截止到今天,是5.4)。值得注意的是,startsWith()已针对大型干草堆字符串进行了优化。
Gras Double

Answers:


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

如果您不想使用正则表达式,请使用此选项。


16
+1比接受的答案干净。另外,$length在的最后一行也不需要endsWith()
太多的PHP

13
我会说endsWith('foo','')== false是正确的行为。因为foo并非一无所有。“ Foo”以“ o”,“ oo”和“ Foo”结尾。
MrHus

125
EndsWith可以写得更短一些:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj 2012年

12
您可以if通过将$length第三个参数传递给substr:来完全避免return (substr($haystack, -$length, $length);$length == 0通过返回一个空字符串而不是整个字符串来处理这种情况$haystack
mxxk 2015年

20
@MrHus我建议使用多字节安全功能,例如mb_strlen和mb_substr
19Gerhard85 '16

1024

您可以使用substr_compare函数检查开始于和结束于:

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对于“开始于”而言是一种更快的触摸,但无法检查“结束于”。


74
这个答案使它成为了每日WTF!:d见thedailywtf.com/articles/...
维姆10布林克

请注意,@ DavidWallace和@FrancescoMM注释适用于此答案的旧版本。strrpos如果针与干草堆的开头不匹配,当前答案将立即使用哪个(应该)失败。
Salman 2016年

2
我不明白 基于php.net/manual/en/function.strrpos.php:“如果该值为负,则搜索将从字符串末尾的那么多字符开始,向后搜索。” 这似乎表明我们从字符0开始(由于-strlength($haystack)),然后从那里开始向后搜索?这不是意味着您没有搜索任何内容吗?我也不了解其中的!== false部分。我猜这是基于PHP的古怪之处,其中某些值“真实”而另一些“虚假”,但是在这种情况下如何工作?
Welbog '16

3
@Welbog:例如haystack = xxxyyyneedle =,yyy并且使用strrpos搜索从第一个开始x。现在我们在这里没有成功的匹配项(找到了x而不是y),并且我们不能再返回了(我们在字符串的开头),搜索立即失败。关于使用!== false- strrpos在上面的示例中将返回0或false,而不返回其他值。同样,strpos在上面的示例中可以返回$temp(期望的位置)或false。我!== false为了保持一致性而去了,但是您可以分别在和函数中使用=== 0=== $temp
Salman'A

8
@spoo已经确定,如果干草堆很大并且不存在针,strpos === 0是一个糟糕的解决方案。
Salman A

243

更新2016年8月23日

功能

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
如果字符串不为空(如您的测试中所示),则速度实际上快了(20-30%):function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;}我在下面添加了答复。
FrancescoMM

3
@Jronny因为110小于133 ... ??
mpen 2014年

2
该死,我不知道那个时候我的脑子里发生了什么。完全缺乏睡眠。
Jronny

1
@mpen,我根本没注意到大象:(
Visman '17

1
这些测试不利于测试性能。您正在做的是使用随机弦作为针。在99.99%的情况下将没有匹配。匹配第一个字节后,大多数功能将退出。发现匹配的情况如何?哪个函数花费最少的时间来完成成功的匹配?如果99%的针头匹配但最后几个字节不匹配,该怎么办?哪个函数需要最少的时间来得出不匹配的结论?
Salman A

137

所有的答案似乎到目前为止做不必要的工作负载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
endsWith()函数有错误。它的第一行应该是(不包含-1): $expectedPosition = strlen($haystack) - strlen($needle);
Enrico Detoma 2010年

6
strlen()不是必需的。如果字符串不是以给定的针开始的,则您的代码将不必要地扫描整个干草堆。
AppleGrew 2011年

5
@Mark是的,仅检查开始时间要快很多,尤其是如果您正在执行类似检查MIME类型的操作(或其他任何字符串较大的地方)的时候
chacham15'9

2
@mark我使用1000个字符的干草堆和10或800个字符的针进行了一些基准测试,而strpos的速度提高了30%。在陈述某些东西更快或更慢之前做基准测试
wdev

7
你应该认真考虑引述针状strpos($haystack, "$needle", 0)如果有任何的机会,它是不是已经是一个字符串(例如,如果它是来自哪里json_decode())。否则,[odd]的默认行为strpos()可能会导致意外的结果:“ 如果needle不是字符串,则将其转换为整数并用作字符的序数值。
quietmint 2012年

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

贷给

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

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


1
strtolower不是使大小写不敏感的函数的最佳方法。在某些语言环境中,大小写不仅比上面和下面还要复杂。
桑德·赖肯

8
我看到抱怨并且没有解决方案...如果您要说这很糟糕,那么您应该举一个例子,说明它应该如何。
KdgDev

2
@WebDevHobo:这就是为什么我在您发表评论的前一天自己添加了一个答案。对于您的代码来说,strcasecmp确实是正确的选择。
桑德·里肯

29

上面的regex功能,但上面也建议进行其他调整:

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

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

2
在php中用于字符串操作的参数顺序为$ haystack,$ needle。这些函数是向后的,其作用类似于数组函数,其顺序实际上是$ needle,$ haystack。
安迪

29

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

检查字符串是否以“ 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()如上所示的函数。

但是,再次强调,这并不是针对每种情况的解决方案,而只是针对非常具体的情况。


您不需要对字符串进行硬编码。正则表达式可以是动态的。
瑞安

2
@self为true,但是如果未对字符串进行硬编码,则必须对其进行转义。目前,有2个答案可以解决此问题。这很容易,但是会使代码复杂一点。所以我的意思是,在非常简单的情况下,可以进行硬编码,您可以使其保持简单。
noamtm '16

1
您也不必转义斜杠,可以将正则表达式换成其他字符,例如@,这样/就不必转义斜杠()。请参阅此处的示例3:php.net/manual/en/function.preg-match.php
cjbarth

谢谢@cjbarth。相应地更改了我的答案。顺便说一句,“#”是在处理斜杠时在php.net/manual/en/regexp.reference.delimiters.php中给出的示例。
noamtm

23

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

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

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
这可能是最有效的答案,因为不使用任何函数作为额外的字符串,而只是使用普通的字符串...

它可能应该检查字符串是否至少有一个字符并交换了两个参数
a1an

1
有创意。包含干草堆的针。顺便说一句,有一些丑陋的减弱:endsWithChar('','x'),但结果是正确的
Tino

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
+1自PHP5.1和IMHO最佳答案以来一直有效。但endsWidth应该做return $needle==='' || substr_compare(...使其按预期工作-strlen($needle)===0,如果没有修复,它会endsWith('a','')返回false
Tino

@Tino谢谢...我觉得这是一个错误substr_compare()居然,所以我添加了一个PR修理好了:)
杰克

3
该调用endsWith('', 'foo')触发警告:“ substr_compare():起始位置不能超过初始字符串长度”。也许这是中的另一个错误substr_compare(),但是要避免出现此错误,您需要进行类似... || (strlen($needle) <= strlen($haystack) && substr_compare(... 的预检查……) === 0);
gx_

@gx_无需使用更多代码来降低速度。只需使用return $needle === '' || @substr_compare(..禁止显示此警告。
蒂诺

17

最快的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

3
+1是花时间比较不同的解决方案并实际对其进行基准测试!您还应该提到使用的PHP版本,因为随着语言的发展进行了优化!我已经看到了从一个PHP版本到另一个PHP版本的字符串比较函数的显着改进:)
Christophe Deliens

1
回显@ChristopheDeliens及其提供PHP版本的请求。我在7.3.2上运行了您的测试,并获得了类似的FWIW结果。
杰夫,

16

我知道已经完成了,但是您可能要看一下strncmp,因为它允许您输入要比较的字符串的长度,因此:

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

您将如何处理呢?
mpen 2011年

@Mark-您可以查看接受的答案,但是我更喜欢使用strncmp,主要是因为我认为它更安全。
詹姆斯·布莱克

我的意思是专门用strncmp。您不能指定偏移量。这意味着您的endsWith函数将必须完全使用其他方法。
mpen 2011年

@Mark-对于EndsWith我将只使用strrpos(php.net/manual/en/function.strrpos.php),但是,通常来说,任何时候使用strcmp strncmp都是一个更安全的选择。
詹姆斯·布莱克

11

您可以使用strposstrrpos

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

1
如果你在这里使用的三重等号strpos($sHaystack, $sNeedle) == 0这样strpos($sHaystack, $sNeedle) === 0false == 0评估为时,我看到一个错误true
Kalyan

11

这是已接受答案的多字节安全版本,适用于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
我很确定这只是浪费CPU。对于StarstWith和EndsWith,您需要检查的只是检查字节是否匹配,而这恰恰是公认的答案。此1浪费时间计算针的utf8字符的数量,以及大海捞针的第n个utf8字符的位置在哪里。.我想,如果不确定100%确定,那只是在浪费CPU。您能否提出一个实际的测试用例,使接受的答案失败,而事实并非如此?
hanshenrik '18

2
@hanshenrik-这很可能会发生,在极少数情况下,当您查找包含与UTF8相同字节但缺少最后一个字符一半的字符串时。像,您有unicode C5 91(字母“ő”),而您寻找的是C5(字母“Å”),它不应该给您匹配。另一方面,当然,您为什么要在utf干草堆中寻找非utf针头...但是对于防弹检查,必须考虑到这种可能性。
dkellner '18

startsWith它应该是$length = mb_strlen($needle, 'UTF-8');
托马斯Kekeisen

2
@ThomasKekeisen谢谢,修复它。
Vahid Amiri

8

简短且易于理解的单行代码,不带正则表达式。

startsWith()很简单。

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

EndsWith()使用稍微花哨且缓慢的strrev():

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

@FrancescoMM:strpos不是“正确的工具” ...为什么?那么什么是“正确的工具”? 编辑:我在下面阅读了您的答案。我认为编程就像利用您拥有的资源进行发明。.因此,没有对与错...只有工作或不工作...性能是次要的。
Fr0zenFyr

“因为它是搜索工具,而不是比较工具?” 哥 亚里士多德
FrancescoMM

7

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


如果前几个字符不同,只会加快速度。
杰克

2
@Jack是的,当然,这个想法是从统计学上讲是发生的,因此,在整个测试集中(包括没有差异的情况),提速通常为20%-30%。当它们不同时,您会收获很多;而当它们不相同时,您将收获很少。平均而言,您可以获得30%(取决于设置,但大多数情况下您可以在大型测试中获得速度)
FrancescoMM

“但是这不是完成这项工作的正确工具” …… 有人引用吗?
Fr0zenFyr

1
WTF。我在下面列出了所有流程,我还应该引用谁?您会使用一个搜索到字符串末尾的函数来告诉您拳头字符不是'a'吗?谁在乎吗?这不是正确的工具,因为它是搜索而不是比较的工具,没有必要引用亚里士多德陈述明显的事实!
FrancescoMM

6

我希望以下答案可能有效且简单:

$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

这些天,我通常最终会使用下划线-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   

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


6

答案通过MPEN是非常彻底的,但不幸的是,所提供的基准具有非常重要的和有害的监督。

因为针和干草堆中的每个字节都是完全随机的,所以针-干草堆对在第一个字节上发生差异的概率为99.609375%,这意味着,平均而言,在100000对中,大约99609个字节在第一个字节上会有所不同。换句话说,基准测试严重偏向于startswith像第一个那样显式检查第一个字节的strncmp_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

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


5

简而言之:

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

只是一个建议:

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倍)。在真实情况下,您几乎不需要为这条单线的性能付出任何代价,因此我认为值得考虑。(此外,实际上,当您针对特定的起始块测试许多字符串时,大多数比较都会失败,因为在典型情况下,您正在寻找某些东西。)


2
您代码中的错误:startsWith("123", "0")给出true
Tino,

是的,不好!$检查发生了。抱歉! (只是想在第3行中说明这个概念)
dkellner

4

substr函数可以false在许多特殊情况下返回,因此这是我的版本,它处理以下问题:

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

另外,该substr_compare功能也值得一看。 http://www.php.net/manual/zh/function.substr-compare.php



4

我会这样

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

如果不匹配,则忘记返回false。Errgo错误,因为函数的返回值不应该被假定,但是我知道至少与其他答案相比,您要做什么。
Spoo,

3

根据詹姆斯·布莱克(James Black)的回答,以下是endWith版本:

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
请注意,这strrev()很有创意,但成本很高,尤其是当您说的是... 100Kb时。
Alexis Wilke 2014年

使用===而不是==确定。0等于PHP中的很多东西。
nawfal

3

为什么不以下?

//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的开始发现价值!

请记住,strpos如果在大海捞针中未找到针,则将返回false;并且仅当在索引0(即开始)处找到针时,才会返回0。

结束于:

$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
似乎很奇怪,如果您要在字符串“ abcdefghijklmxyz”中搜索“ xy”,而不仅仅是将“ x”与“ a”进行比较并返回FALSE,那么您会查找从“ a”至“ m”的每个字符,然后最终找到“ xy”在字符串中,最后返回FALSE,因为它的位置不为零!这就是您要执行的操作,它比此处的任何其他猖ant功能都要奇怪和疯狂。
FrancescoMM 2014年

简单在于键入,而不是逻辑。
卡德·哈芬

与其说是逻辑,不如说是弗朗斯科指出的可能的优化。strpos()除非匹配,否则使用会很慢。strncmp()在这种情况下会更好。
亚历克西斯·威尔克

当您执行此类低级功能时,无论多么复杂,通常都希望寻求速度最优化的解决方案,因为这将被称为数百万次。您在此处获得或失去的每一微秒都会产生非常实际的变化。因此,最好从中进行调整(然后,就已经拥有了函数,然后再去考虑复杂性),而不是去寻找外观,并在以后甚至不知道出了什么问题的时候浪费了很多时间。想象一下检查一个不匹配的2GB字符串。
dkellner '18

3

先前的许多答案也将同样有效。但是,这可能会尽可能的短,让它按照自己的意愿进行。您只是说您希望它“返回真实”。因此,我包含了返回布尔值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';
}

真正。但是,Peter要求使用可与字符串一起使用的函数。尽管如此,我已经更新了答案以安抚您。
wynshaft

编辑后,您的解决方案现在完全过时了。它以布尔值形式返回'true''false'作为字符串true。对于诸如underhanded.xcott.com之类的东西来说,这是一个很好的模式;)
Tino 2014年

好吧,彼得只是说他希望它返回“ true”。所以我想我会归还他的要求。我已经添加了两个版本,以防万一这不是他想要的。
wynshaft 2014年

2

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

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

3
$ needle应该使用进行转义preg_quote($needle, '/')
Timo Tijhof 2012年

2

无复制和无内部循环:

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

这应该比MrHus的实施要快得多!我可能会对其进行基准测试
hanshenrik

1

这是针对PHP 4的有效解决方案。如果在PHP 5上使用substr_compare而不是,您可以获得更快的结果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

您可以为此使用fnmatch函数。

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

警告,不是二进制安全的,甚至不能安全地防止包含通配符的指针= /
hanshenrik
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.