функции functionsWith () и окончание () в PHP - PullRequest
1349 голосов
/ 07 мая 2009

Как я могу написать две функции, которые будут принимать строку и возвращать ее, если она начинается с указанного символа / строки или заканчивается ею?

Например:

$str = '|apples}';

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

Ответы [ 30 ]

12 голосов
/ 24 июля 2018

Решение быстрых концов с ():

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

Benchmark:

# This answer
function endsWith($haystack, $needle) {
    return substr($haystack,-strlen($needle))===$needle;
}

# Accepted answer
function endsWith2($haystack, $needle) {
    $length = strlen($needle);

    return $length === 0 ||
    (substr($haystack, -$length) === $needle);
}

# Second most-voted answer
function endsWith3($haystack, $needle) {
    // search forward starting from end minus needle length characters
    if ($needle === '') {
        return true;
    }
    $diff = \strlen($haystack) - \strlen($needle);
    return $diff >= 0 && strpos($haystack, $needle, $diff) !== false;
}

# Regex answer
function endsWith4($haystack, $needle) {
    return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
}

function timedebug() {
    $test = 10000000;

    $time1 = microtime(true);
    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith('TestShortcode', 'Shortcode');
    }
    $time2 = microtime(true);
    $result1 = $time2 - $time1;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith2('TestShortcode', 'Shortcode');
    }
    $time3 = microtime(true);
    $result2 = $time3 - $time2;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith3('TestShortcode', 'Shortcode');
    }
    $time4 = microtime(true);
    $result3 = $time4 - $time3;

    for ($i=0; $i < $test; $i++) {
        $tmp = endsWith4('TestShortcode', 'Shortcode');
    }
    $time5 = microtime(true);
    $result4 = $time5 - $time4;

    echo $test.'x endsWith: '.$result1.' seconds # This answer<br>';
    echo $test.'x endsWith2: '.$result4.' seconds # Accepted answer<br>';
    echo $test.'x endsWith3: '.$result2.' seconds # Second most voted answer<br>';
    echo $test.'x endsWith4: '.$result3.' seconds # Regex answer<br>';
    exit;
}
timedebug();

Результаты тестов:

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
11 голосов
/ 12 мая 2015

Вы можете использовать strpos и strrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
8 голосов
/ 10 ноября 2017

Вот многобайтовая безопасная версия принятого ответа, она отлично работает для строк 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);
}
8 голосов
/ 29 июня 2011

Короткие и понятные однострочные без регулярных выражений.

начинается с () прямо вперед.

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

окончание с () использует слегка причудливую и медленную строчку ():

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
7 голосов
/ 28 июля 2013

Сосредоточение на запуске с, если вы уверены, что строки не пусты, добавление теста на первый символ, перед сравнением, 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 не выполняет итерацию всей строки (но даже в этом случае), это совсем не удобно.

6 голосов
/ 18 октября 2013

Я надеюсь, что приведенный ниже ответ может быть эффективным и простым:

$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 голосов
/ 21 сентября 2016

Ответ от mpen невероятно тщательный, но, к сожалению, предоставленный тест имеет очень важный и вредный упущение.

Поскольку каждый байт в иголках и стогах сена является совершенно случайным, вероятность того, что пара иголка-стог будет отличаться в самом первом байте, составляет 99.609375%, что означает, что в среднем около 99609 из 100000 пар будут отличаться в самый первый байт. Другими словами, эталонный тест сильно смещен в сторону 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

Конечно, этот эталонный тест все еще не может быть абсолютно беспристрастным, но он также проверяет эффективность алгоритмов при наличии частично совпадающих игл.

6 голосов
/ 23 января 2015

Я обычно заканчиваю тем, что хожу с библиотекой типа 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   

Библиотека полна других полезных функций.

4 голосов
/ 20 апреля 2012

короче:

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

function endsWith($str, $needle){
   $length = strlen($needle);
   return !$length || substr($str, - $length) === $needle;
}
4 голосов
/ 30 июня 2012

Почему не следующее?

//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 (AKA начало).

А вот и кончается:

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

В этом сценарии нет необходимости в функции startWith (), так как

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

вернет истину или ложь точно.

Кажется странным, что это так просто, когда все дикие функции работают безудержно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...