ChatGPT解决这个技术问题 Extra ChatGPT

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

如果它以指定的字符/字符串开头或以指定的字符/字符串结尾,我如何编写两个接受字符串并返回的函数?

例如:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
请参阅 Laravel 的 Str class startsWith() 和 endsWith() 以了解 well-tested 方法。 Edge cases 已经遇到过,因此广泛使用此代码是一个优势。
this standalone library 中所示,您可能会发现 s($str)->startsWith('|')s($str)->endsWith('}') 很有帮助。
警告:这里的大多数答案在 UTF-8 等多字节编码中都不可靠。
按照我的上述评论,您可以确保使用最新版本(截至今天,5.4)。值得注意的是,startsWith() 已针对大型 haystack 字符串进行了优化。
PHP 8.0 为这项工作 str_starts_withstr_end_with 引入了新方法:stackoverflow.com/a/64160081/7082164

s
sh6210

PHP 8.0 及更高版本

从 PHP 8.0 开始,您可以使用

str_starts_with Manual

str_ends_with Manual

例子

echo str_starts_with($str, '|');

8.0 之前的 PHP

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

我会说 endsWith('foo', '') == false 是正确的行为。因为 foo 不会以任何形式结束。 “Foo”以“o”、“oo”和“Foo”结尾。
EndsWith 可以写得更短:return substr($haystack, -strlen($needle))===$needle;
@RokKralj 但前提是 $needle 不为空。
您可以通过将 $length 作为第三个参数传递给 substr 来完全避免 ifreturn (substr($haystack, -$length, $length);。这通过返回一个空字符串而不是整个 $haystack 来处理 $length == 0 的情况。
@MrHus 我建议使用多字节安全函数,例如 mb_strlen 和 mb_substr
S
Salman A

您可以使用 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 (benchmark script) 上最快的解决方案之一。针对 8KB 干草堆、各种长度的针以及完整、部分和不匹配的情况进行了测试。 strncmp 对starts-with 稍快一点,但它不能检查end-with。


请注意,@DavidWallace 和 @FrancescoMM 评论适用于此答案的旧版本。当前答案使用 strrpos 如果 needle 与 haystack 的开头不匹配,则(应该)立即失败。
我不明白。基于 php.net/manual/en/function.strrpos.php:“如果值为负,则搜索将从字符串末尾的那么多字符开始,向后搜索。”这似乎表明我们从字符 0 开始(由于 -strlength($haystack))并从那里向后搜索?这不意味着你没有搜索任何东西吗?我也不明白其中的 !== false 部分。我猜这依赖于 PHP 的一个怪癖,其中一些值是“真实的”而另一些是“虚假的”,但在这种情况下它是如何工作的?
@Welbog:例如 haystack = xxxyyy needle = yyy 并使用 strrpos 搜索从第一个 x 开始。现在我们在这里没有成功匹配(找到 x 而不是 y)并且我们不能再向后退(我们在字符串的开头)搜索失败立即。关于使用 !== false -- 上面示例中的 strrpos 将返回 0 或 false 而不是其他值。同样,上例中的 strpos 可以返回 $temp(预期位置)或 false。为了保持一致性,我选择了 !== false,但您可以分别在函数中使用 === 0=== $temp
在这方面做了很多不必要的工作。为什么不对startsWith 使用strpos === 0。反对使次优的编码响应复杂化。
@spoo 已经确定 strpos === 0 是一个糟糕的解决方案,如果 haystack 很大并且 needle 不存在。
А
Артур Курицын

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


如果字符串不为空,就像在您的测试中那样,这实际上会更快(20-30%):function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;} 我在下面添加了回复。
@Jronny 因为 110 小于 133 ...??
该死的,我不知道当时我脑子里发生了什么。 Prolly睡眠不足。
@mpen,我根本没有注意到大象:(
如果您不使用 isset 进行测试,$haystack[0] 将引发通知错误。针也是一样。但是如果你添加测试,它会降低它的性能
R
Ram Sharma

到目前为止,所有答案似乎都做了很多不必要的工作,strlen calculationsstring allocations (substr) 等。'strpos''stripos' 函数返回 $haystack 中第一次出现 $needle 的索引:

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

endsWith() 函数有错误。它的第一行应该是(没有 -1):$expectedPosition = strlen($haystack) - strlen($needle);
strlen() 事情不是不必要的。如果字符串不是以给定的针开头,那么您的代码将不必要地扫描整个干草堆。
@Mark 是的,只检查开头会快很多,特别是如果您正在执行诸如检查 MIME 类型(或任何其他字符串必然很大的地方)之类的事情
@mark 我用 1000 char haystack 和 10 或 800 char needle 做了一些基准测试,strpos 快了 30%。在说明某事是否更快之前,请先进行基准测试...
如果有任何可能它还不是字符串(例如,如果它来自 json_decode()),您应该强烈考虑像 strpos($haystack, "$needle", 0) 这样引用针。否则,strpos() 的 [odd] 默认行为可能会导致意外结果:“If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
R
Rubens Mariuzzo
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);
}

归功于:

Check if a string ends with another string

Check if a string begins with another string


strtolower 不是制作不区分大小写函数的最佳方法。在某些地区,套管比上层和下层更复杂。
我看到抱怨并且没有解决方案......如果你要说它很糟糕,那么你应该举一个例子来说明它应该如何。
@WebDevHobo:这就是为什么我在您发表评论的前一天自己添加了一个答案。对于您的代码 strcasecmp 确实是正确的做法。
F
Flimm

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 for this feature 提供了更多信息,并讨论了明显(和不那么明显)用户空间实现的优点和问题。


n
noamtm

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

检查字符串是否以 '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())。

但同样——这不是每个案例的解决方案,只是这个非常具体的解决方案。


您不需要对字符串进行硬编码。正则表达式可以是动态的。
@self 是的,但是如果字符串不是硬编码的,则必须对其进行转义。目前这个问题有2个答案可以做到。这很容易,但它使代码稍微复杂了一点。所以我的观点是,对于可以进行硬编码的非常简单的情况,您可以保持简单。
T
Timo Tijhof

上面的正则表达式函数,但上面还建议了其他调整:

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

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

在 php 中,字符串操作的参数顺序是 $haystack, $needle。这些函数是倒退的,就像数组函数,其中的顺序实际上是 $needle, $haystack。
L
Lucas Bustamante

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

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

如果速度对你很重要,试试这个。(我相信这是最快的方法)

仅适用于字符串,如果 $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

这可能是最有效的答案,因为不使用任何额外的函数,只是使用普通字符串......
它应该可能检查字符串是否至少有一个字符并且交换了两个参数
有创造力的。包含干草堆的针头。顺便说一句,有一些丑陋的减弱:endsWithChar('','x'),但结果是正确的
我喜欢你的回答,但这很有趣,......针和干草堆是相反的:)......即你会在干草堆中搜索针,因此,它应该是:return ($needle == = $干草堆[0]); ,但很好的答案,谢谢!
@HeiderSati:很好的观察!这就是@Tino 所说的Creative. Needles which contain haystacks....我没有给予足够的关注。谢谢!我修好了它。 :)
J
Ja͢ck

这里有两个不引入临时字符串的函数,这在针很大时可能很有用:

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

function endsWith($haystack, $needle)
{
    return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}

+1 从 PHP5.1 和 IMHO 最佳答案开始工作。但是 endsWidth 应该做 return $needle==='' || substr_compare(...所以它可以按预期对 -strlen($needle)===0 工作,如果没有修复,会使 endsWith('a','') 返回 false
@Tino 谢谢......我觉得这实际上是 substr_compare() 中的一个错误,所以我添加了一个 PR 来修复它:)
调用 endsWith('', 'foo') 触发警告:“substr_compare():起始位置不能超过初始字符串长度”。也许这是 substr_compare() 中的另一个错误,但为了避免它,您需要像 ...|| (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0); 这样的预检查
@gx_ 无需使用更多代码放慢速度。只需使用 return $needle === '' || @substr_compare(.. 来禁止此警告。
J
James Black

我意识到这已经完成,但您可能想查看 strncmp,因为它允许您放置要比较的字符串的长度,所以:

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

你会怎么做呢?
@Mark-您可以查看已接受的答案,但我更喜欢使用 strncmp 主要是因为我认为它更安全。
我的意思是 strncmp 具体。您不能指定偏移量。这意味着您的 endsWith 函数必须完全使用不同的方法。
@Mark-对于endsWith,我只会使用strrpos(php.net/manual/en/function.strrpos.php),但是通常,无论何时使用strcmp strncmp 都可能是更安全的选择。
V
Vahid Amiri

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

我很确定这只是对 CPU 的浪费。对于 StarstWith 和 EndsWith,您需要检查的只是检查字节是否匹配,而这正是公认的答案正在做的事情。这 1 浪费时间计算针的 utf8 字符数,以及大海捞针的第 n 个 utf8 字符的位置。我认为,没有 100% 确定,这只是浪费 cpu。你能想出一个实际的测试用例,其中接受的答案失败了,而这不是吗?
@hanshenrik - 顺便说一句,在极少数情况下,当您查找包含与 UTF8 相同字节但最后一个字符丢失一半的字符串时,它可能会发生。就像,你有 unicode C5 91(字母“ő”)并且你寻找 C5(字母“Å”)它不应该给你一个匹配。另一方面,当然,您为什么要在 utf 大海捞针中搜索非 utf 针……但是对于防弹检查,必须考虑这种可能性。
startsWith 中应该是 $length = mb_strlen($needle, 'UTF-8');
@ThomasKekeisen 谢谢,已修复。
被接受的(嗯,目前被接受的)解决方案已经是多字节安全的。它实际上是二进制安全的,这是一个更强大的保证。
B
Bhavik Shah

您可以使用 strposstrrpos

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

您是否应该在此处使用三等号 strpos($sHaystack, $sNeedle) == 0,例如 strpos($sHaystack, $sNeedle) === 0?当 false == 0 评估为 true 时,我看到了一个错误。
D
Dan

没有正则表达式的简短且易于理解的单行语句。

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 不是“正确的工具”......为什么?那么什么是“正确的工具”呢?编辑:我在下面阅读了您的答案。我认为编程就像使用你拥有的资源进行发明一样。所以没有对错……只有工作或不工作……性能是次要的。
“因为它是搜索的工具,而不是比较的工具?”引用。亚里士多德
F
FrancescoMM

关注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:c 似乎比 if(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 不迭代整个字符串(但即使在这种情况下),这根本不方便。


如果第一个字符不同,则只有加速。
@Jack 是的,当然,这个想法是统计上发生的,所以整个测试集的加速通常是 20%-30%(包括没有不同的情况)。当它们不同时,您会获得很多,而当它们不同时,您会获得很少。平均而言,您获得了 30%(取决于设置,但大多数情况下您会在大型测试中获得速度)
“但它不是这项工作的正确工具”......任何引用?
哇。我在下面列出了我应该引用谁的所有过程,除此之外?您会使用搜索到字符串末尾的函数来告诉您第一个字符不是“a”吗?谁在乎呢?这不是正确的工具,因为它是用于搜索的工具,而不是用于比较的工具,没有必要引用亚里士多德来陈述显而易见的事情!
S
Srinivasan.S

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

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

y
yuvilio

这些天我通常最终会使用像 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   

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


V
Veeno

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

因为 needles 和 haystacks 中的每个字节都是完全随机的,所以 needle-haystack 对在第一个字节上不同的概率是 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

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


V
Vincent Pazeller

简而言之:

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

function endsWith($str, $needle){
   $length = strlen($needle);
   return !$length || substr($str, - $length) === $needle;
}

d
dkellner

做得更快:

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) 为真),则需要进行一些类型转换:

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

我认为没有必要,但这是一个有趣的边缘案例,会引发诸如“布尔值是否以 at 开头?”之类的问题。 - 所以你决定,但要确保你决定好。


您的代码中的错误:startsWith("123", "0") 给出 true
是的,糟糕!$检查发生了。对不起! (只是想说明第 3 行中的概念)
@Tino 我想说我们现在可以删除这 2 条评论,你不同意吗?我的意思是,点了,它是固定的,已经 2 年了。
C
Community

这可能有效

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

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


b
biziclop

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/en/function.substr-compare.php


P
Peter Mortensen

为什么不是以下?

//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(AKA 开头)找到针时才会返回 0。

这里是endsWith:

$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。

看起来很奇怪,所有狂野的功能都在这里泛滥成灾,这么简单。


似乎很奇怪,如果您在字符串“abcdefghijklmxyz”中搜索“xy”,而不是仅将“x”与“a”进行比较并返回 FALSE,您会查看从“a”到“m”的每个字符,然后最终找到“xy”在字符串中,最后你返回 FALSE 因为它的位置不为零!这就是你正在做的事情,它比这里任何其他猖獗的功能都奇怪和狂野。
简单在于打字,而不是逻辑。
与其说是逻辑,不如说是弗朗斯科指出的可能的优化。除非匹配,否则使用 strpos() 会很慢。 strncmp() 在这种情况下会好得多。
当您执行此类低级功能时,您通常希望寻求速度最优化的解决方案,无论多么复杂,因为这将被调用数百万次。您在这里获得或失去的每一微秒都会产生非常真实的影响。所以最好把它改掉(然后忘记复杂性,既然你有这个功能),而不是追求外观,然后在你甚至不知道出了什么问题的时候浪费了可怕的时间。想象一下检查一个不匹配的 2GB 字符串。
J
Jelle Keizer

我会这样做

     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 不正确,因为函数的返回值不应该被“假设”,但至少与其他答案相比,我知道你在追求什么。
b
bobo

根据 James Black 的回答,这里是它的 endsWith 版本:

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 版本。


请注意,strrev() 具有创意,但成本很高,尤其是当您有诸如... 100Kb 的字符串时。
请务必使用 === 而不是 ==0 相当于 PHP 中的很多东西。
c
coddiwomplefrog

以前的许多答案也同样有效。但是,这可能会尽可能短,并让它做你想做的事。您只需声明您希望它“返回真实”。所以我已经包含了返回布尔真/假和文本真/假的解决方案。

// 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 要求的是一个可以处理字符串的函数。尽管如此,我已经更新了我的答案以安抚你。
编辑后,您的解决方案现在完全过时了。它将 'true''false' 作为字符串返回,它们都是布尔意义上的 true。不过,对于 underhanded.xcott.com 之类的东西来说,这是一个很好的模式;)
好吧,彼得只是说他希望它返回“真实”。所以我想我会退回他要求的东西。我已经添加了两个版本,以防万一这不是他想要的。
h
hanshenrik

无复制和无内部循环:

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 的实现要快得多!我可能会对其进行基准测试
P
Patrick Smith

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

F
Freeman

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

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

$needle 应该用 preg_quote($needle, '/') 转义。