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

Например:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
1635
Ali 7 Май 2009 в 16:14
24
См. класс Str, начинающийся с () и endWith () для хорошо протестированных методов. Встречались крайние случаи, поэтому широкое использование этого кода является преимуществом.
 – 
Gras Double
7 Дек 2014 в 02:32
1
 – 
caw
27 Июл 2016 в 02:46
4
Предупреждение: большинство ответов здесь ненадежны в многобайтовых кодировках, таких как UTF-8.
 – 
Álvaro González
15 Май 2017 в 17:07
Следуя моему вышеупомянутому комментарию, вы можете использовать последнюю версию (на сегодняшний день 5.4). Примечательно, что startWith () оптимизирован для больших строк стога сена.
 – 
Gras Double
22 Май 2017 в 21:51
7
В PHP 8.0 представлены новые методы для этого задания str_starts_with и str_end_with: stackoverflow.com/a/64160081/7082164 < / а>
 – 
Jsowa
1 Окт 2020 в 20:06

29 ответов

Вы можете использовать функцию 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 (тестовый скрипт). Протестировано на стогах сена 8 КБ, иглами различной длины, а также на полных, частичных и несоответствующих случаях. strncmp на касание быстрее для начала, но не может проверить окончание.

1081
Salman A 24 Апр 2019 в 11:24
84
Этот ответ попал в Daily WTF! : D См. thedailywtf.com/articles/…
 – 
Wim ten Brink
21 Апр 2016 в 14:36
Обратите внимание, что комментарии @DavidWallace и @FrancescoMM относятся к более старой версии этого ответа. В текущем ответе используется strrpos, который (должен) немедленно завершиться неудачей, если игла не совпадает с началом стога сена.
 – 
Salman A
21 Апр 2016 в 17:00
2
Я не понимаю. На основе php.net/manual/en/function.strrpos.php: «Если значение отрицательное, поиск вместо этого начнется с такого количества символов от конца строки, поиск в обратном направлении». Похоже, это означает, что мы начинаем с символа 0 (из-за -strlength($haystack)) и ищем оттуда назад ? Разве это не значит, что вы ничего не ищете? Я также не понимаю !== false части этого. Я предполагаю, что это полагается на причуду PHP, где одни значения «правдивы», а другие «ложны», но как это работает в этом случае?
 – 
Welbog
21 Апр 2016 в 17:44
3
@Welbog: например, стог сена = xxxyyy Needle = yyy и при использовании strrpos поиск начинается с первого x. Теперь у нас нет успешного совпадения (найдено x вместо y), и мы больше не можем вернуться назад (мы в начале строки), поиск завершается неудачно немедленно . Об использовании !== false - strrpos в приведенном выше примере вернет 0 или false, а не другое значение. Точно так же strpos в приведенном выше примере может возвращать $temp (ожидаемое положение) или false. Я выбрал !== false для единообразия, но вы можете использовать === 0 и === $temp в функциях соответственно.
 – 
Salman A
21 Апр 2016 в 18:01
10
Уже было установлено, что strpos === 0 - ужасное решение, если стог сена большой, а иголки нет.
 – 
Salman A
8 Окт 2016 в 09:22

Обновлено 23 августа 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

startwith_benchmark.php

257
Артур Курицын 28 Июн 2017 в 22:46
3
Если строки не пустые, как в ваших тестах, это на самом деле как-то (на 20-30%) быстрее: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;} Я добавил ответ ниже.
 – 
FrancescoMM
28 Июл 2013 в 19:38
3
Потому что 110 меньше 133 ... ??
 – 
mpen
2 Дек 2014 в 19:41
2
Черт, я не знаю, что пришло мне в голову в тот раз. Возможно недосыпание.
 – 
Jronny
18 Дек 2014 в 06:08
1
@mpen, я вообще не слона заметил :(
 – 
Visman
21 Ноя 2017 в 05:15
2
$haystack[0] выдаст сообщение об ошибке, если вы не протестируете его с помощью isset. То же и с иглами. Но если вы добавите тесты, это снизит его производительность
 – 
Thanh Trung
15 Апр 2018 в 21:12

Все ответы на данный момент, похоже, выполняют кучу ненужной работы, strlen calculations, string 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;
}
144
Ram Sharma 4 Авг 2015 в 18:13
2
Ошибка функции endsWith(). Его первая строка должна быть (без -1): $expectedPosition = strlen($haystack) - strlen($needle);
 – 
Enrico Detoma
5 Авг 2010 в 21:16
6
В strlen () нет необходимости. Если строка не начинается с данной иглы, код ur будет без необходимости сканировать весь стог сена.
 – 
AppleGrew
4 Янв 2011 в 18:46
5
Да, проверка только начала выполняется НАМНОГО быстрее, особенно если вы делаете что-то вроде проверки типов MIME (или в любом другом месте, где строка должна быть большой)
 – 
chacham15
26 Сен 2011 в 19:39
2
Я провел несколько тестов со стогом сена на 1000 знаков и иглой на 10 или 800 знаков, и strpos был на 30% быстрее. Сделайте свои тесты, прежде чем заявлять, что что-то работает быстрее или нет ...
 – 
wdev
6 Авг 2012 в 04:39
7
Вам следует серьезно подумать о цитировании стрелки, например strpos($haystack, "$needle", 0), если есть хоть какой шанс, что это еще не строка (например, если она исходит от json_decode()). В противном случае [нечетное] поведение по умолчанию для strpos() может привести к неожиданным результатам: "Если игла не является строкой, она преобразуется в целое число и применяется как порядковое значение символа. "
 – 
quietmint
3 Дек 2012 в 07:47
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);
}

Кредит для :

Проверить, заканчивается ли строка другой строкой

Проверить, начинается ли строка с другой строки

49
Rubens Mariuzzo 1 Авг 2013 в 19:46
2
Strtolower - не лучший способ сделать функции нечувствительными к регистру. В некоторых регионах корпус более сложный, чем просто верхний и нижний.
 – 
Sander Rijken
14 Май 2009 в 01:25
8
Я вижу жалобы, но решения нет ... Если ты хочешь сказать, что это плохо, то ты тоже должен привести пример, как это должно быть.
 – 
KdgDev
14 Май 2009 в 15:06
2
@WebDevHobo: поэтому я сам добавил ответ за день до вашего комментария. Для вашего кода strcasecmp действительно был правильным решением.
 – 
Sander Rijken
6 Авг 2010 в 11:34

Обновление PHP 8

PHP 8 включает новые str_starts_with и < функции href = "https://www.php.net/manual/en/function.str-ends-with.php" rel = "noreferrer"> str_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 для этой функции предоставляет дополнительную информацию, а также обсуждение достоинств и проблем очевидных ( и не столь очевидные) реализации пользовательского пространства.

50
Flimm 3 Янв 2021 в 02:27

На этот вопрос уже есть много ответов, но в некоторых случаях вы можете согласиться на что-то попроще, чем все они. Если искомая строка известна (жестко запрограммирована), вы можете использовать регулярные выражения без каких-либо кавычек и т. Д.

Проверьте, начинается ли строка с "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()), как показано выше.

Но опять же - это решение не для каждого случая, просто в этом конкретном случае.

44
noamtm 30 Окт 2018 в 22:55
Вам не нужно жестко кодировать строку. регулярное выражение может быть динамическим.
 – 
Ryan
15 Май 2016 в 06:02
2
Истина, но если строка жестко не запрограммирована, вы должны ее избежать. На данный момент есть 2 ответа на этот вопрос. Это легко, но немного усложняет код. Я хотел сказать, что для очень простых случаев, когда возможно жесткое кодирование, вы можете оставить его простым.
 – 
noamtm
15 Май 2016 в 22:13

Вышеуказанные функции регулярного выражения, но с другими настройками, также предложенными выше:

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

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
36
Timo Tijhof 16 Ноя 2012 в 19:14
2
В php для строковых операций порядок параметров следующий: $ haystack, $ Needle. эти функции работают в обратном порядке и действуют как функции массива, где на самом деле упорядочивается $ Need, $ haystack.
 – 
Andrew
2 Дек 2014 в 04:59

Если для вас важна скорость, попробуйте это (я считаю, что это самый быстрый метод).

Работает только для строк и если $ 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
27
lepe 17 Авг 2020 в 13:09
1
Это, вероятно, самый эффективный ответ, потому что не используется какая-либо дополнительная функция, просто обычная строка ...
 – 
user1646111
1 Авг 2013 в 14:29
Вероятно, он должен проверить, есть ли в строке хотя бы один символ и два параметра поменяны местами
 – 
a1an
15 Май 2018 в 17:51
2
Творческий. Иголки со стогами сена. Кстати, есть некрасивое угасание с: endsWithChar('','x'), но результат правильный
 – 
Tino
20 Июл 2018 в 14:28
1
Мне нравится ваш ответ, но он довольно забавный, ... иголка и стог сена - наоборот :) ... то есть вы искали бы иголку в стоге сена, следовательно, это должно быть: return ($ Need == = $ стог сена [0]); , но хороший ответ, спасибо!
 – 
Heider Sati
17 Авг 2020 в 12:24
1
@HeiderSati: Отличное наблюдение! Это то, о чем говорил @Tino Creative. Needles which contain haystacks. ... Я не обратил на это достаточно внимания. Спасибо! Я починил это. :)
 – 
lepe
17 Авг 2020 в 13:11

Самый быстрый результат с решением ():

# 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
29
Lucas Bustamante 15 Окт 2020 в 18:36
4
+1 за то, что нашли время, чтобы сравнить различные решения и протестировать их! вы также должны указать, какую версию PHP вы использовали, поскольку оптимизация выполняется по мере развития языка! Я видел кардинальные улучшения функций сравнения строк от одной версии PHP к другой :)
 – 
Christophe Deliens
3 Окт 2018 в 16:51
2
Повторяя @ChristopheDeliens и его просьбу предоставить версию PHP. Я запустил ваш тест на 7.3.2 и получил аналогичные результаты FWIW.
 – 
Jeff
20 Фев 2019 в 00:50

Вот две функции, которые не вводят временную строку, что может быть полезно, когда иглы достаточно большие:

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

function endsWith($haystack, $needle)
{
    return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
20
Ja͢ck 21 Фев 2014 в 06:56
2
+1 Работает с PHP5.1 и лучший ответ IMHO. Но endsWidth должен делать return $needle==='' || substr_compare( ... чтобы он работал должным образом для -strlen($needle)===0, что без исправления возвращает endsWith('a','') возврат false
 – 
Tino
21 Фев 2014 в 01:53
Спасибо ... Мне кажется, что это ошибка в substr_compare(), поэтому я добавил PR, чтобы исправить это :)
 – 
Ja͢ck
21 Фев 2014 в 06:55
3
Вызов endsWith('', 'foo') вызывает предупреждение: «substr_compare (): начальная позиция не может превышать длину начальной строки». Возможно, это еще одна ошибка в substr_compare(), но чтобы ее избежать, вам нужна предварительная проверка, например ... || (strlen($needle) <= strlen($haystack) && substr_compare( ... ) === 0);
 – 
gx_
9 Авг 2015 в 22:47
Не нужно замедляться с большим количеством кода. Просто используйте return $needle === '' || @substr_compare( .., чтобы подавить это предупреждение.
 – 
Tino
20 Июл 2018 в 14:35

Я понимаю, что это уже закончено, но вы можете посмотреть strncmp поскольку он позволяет вам указать длину строки для сравнения, поэтому:

function startsWith($haystack, $needle, $case=true) {
    if ($case)
        return strncasecmp($haystack, $needle, strlen($needle)) == 0;
    else
        return strncmp($haystack, $needle, strlen($needle)) == 0;
}    
16
James Black 17 Сен 2009 в 06:50
Как бы вы с этим справились?
 – 
mpen
26 Авг 2011 в 19:20
- вы можете посмотреть принятый ответ, но я предпочитаю использовать strncmp в основном потому, что считаю его более безопасным.
 – 
James Black
26 Авг 2011 в 20:45
Я имею в виду конкретно strncmp. Вы не можете указать смещение. Это означало бы, что ваша функцияndsWith должна будет использовать совершенно другой метод.
 – 
mpen
26 Авг 2011 в 22:50
- Для endWith я бы просто использовал strrpos (php.net/manual/en/function .strrpos.php), но, как правило, в любое время, когда вы собираетесь использовать strcmp, strncmp, вероятно, является более безопасным вариантом.
 – 
James Black
27 Авг 2011 в 04:15

Вот многобайтовая безопасная версия принятого ответа, она отлично работает для строк 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);
}
13
Vahid Amiri 17 Апр 2019 в 20:08
3
Я почти уверен, что это просто пустая трата процессора. все, что вам нужно проверить для StarstWith и EndsWith, - это просто проверить совпадение байтов, и это именно то, что делает принятый ответ. это 1 тратит время на вычисление количества символов utf8 на игле, и где позиция n-го символа utf8 стога сена ... я думаю, не будучи на 100% уверенным, это просто трата процессора. Можете ли вы придумать реальный тестовый пример, когда принятый ответ терпит неудачу, а это нет?
 – 
hanshenrik
12 Май 2018 в 03:06
2
- это могло произойти, кстати, в очень редком случае, когда вы ищете строку, которая содержит те же байты, что и UTF8, но с отсутствующей половиной последнего символа. Например, у вас есть код юникода C5 91 (буква «ő»), и вы ищете C5 (буква «Å»), он не должен дать вам совпадения. С другой стороны, конечно, зачем вам искать в стоге сена utf иглу, отличную от utf ... Но для пуленепробиваемых проверок это следует рассматривать как возможность.
 – 
dkellner
8 Сен 2018 в 13:43
В startsWith должно быть $length = mb_strlen($needle, 'UTF-8');
 – 
Thomas Kekeisen
16 Апр 2019 в 19:02
2
Спасибо, исправили это.
 – 
Vahid Amiri
17 Апр 2019 в 20:08
Принятое (в настоящее время принятое) решение уже является многобайтовым. На самом деле он безопасен для двоичного кода, что является еще более надежной гарантией.
 – 
Jon
30 Окт 2020 в 17:32

Вы можете использовать strpos и < a href = "http://php.net/manual/en/function.strrpos.php" rel = "noreferrer"> strrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
11
Bhavik Shah 29 Июл 2016 в 10:11
2
Следует ли вам использовать тройное равенство здесь strpos($sHaystack, $sNeedle) == 0, как это strpos($sHaystack, $sNeedle) === 0? Я вижу ошибку, когда false == 0 оценивается как true.
 – 
Kalyan
23 Июн 2017 в 17:37

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

StartWith () прямолинейный.

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

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

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
8
Dan 29 Июн 2011 в 02:40
@FrancescoMM: strpos - не "правильный инструмент" ... Почему? Какие тогда «правильные инструменты»? ИЗМЕНИТЬ: Я прочитал ваш ответ ниже. Я думал, что программирование похоже на изобретение с использованием имеющихся у вас ресурсов ... Итак, нет правильного или неправильного ... только работа или не работа ... производительность вторична.
 – 
Fr0zenFyr
24 Мар 2018 в 11:22
"потому что это инструмент для поиска, а не для сравнения?" Cit. Аристотель
 – 
FrancescoMM
24 Мар 2018 в 11:42

Сосредоточение внимания на startwith, если вы уверены, что строки не пустые, добавление теста к первому символу, перед сравнением, strlen и т. Д., Немного ускоряет работу:

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

Это как-то (на 20% -30%) быстрее. Добавление еще одного теста char, такого как $ haystack {1} === $ Need {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 не выполняет итерацию по всей строке (но даже в этом случае), это совсем не удобно.

8
FrancescoMM 28 Июл 2013 в 19:34
Ускорение есть только в том случае, если первые символы разные.
 – 
Ja͢ck
2 Авг 2013 в 11:51
3
Да, конечно, идея в том, что это происходит статистически, поэтому ускорение обычно составляет 20% -30% по всему набору тестов (включая случаи, когда оно не отличается). Вы получаете много, когда они разные, и очень мало теряете, когда они не такие. В среднем вы получаете эти 30% (зависит от набора, но в основном вы набираете скорость на больших тестах)
 – 
FrancescoMM
18 Сен 2013 в 12:42
"но это не тот инструмент для этой работы" ... Есть ссылки?
 – 
Fr0zenFyr
24 Мар 2018 в 11:29
2
Черт возьми. Я перечислил весь процесс ниже, кого я должен цитировать, более того? Вы бы использовали функцию, которая ищет до конца строки, чтобы сказать вам, что первый символ не является «а»? Кого это волнует? Это неподходящий инструмент, потому что это инструмент для поиска, а не для сравнения, нет необходимости цитировать Аристотеля, чтобы констатировать очевидное!
 – 
FrancescoMM
24 Мар 2018 в 11:40

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

$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';
7
Srinivasan.S 18 Окт 2013 в 17:01

В наши дни я обычно использую такую ​​библиотеку, как 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   

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

7
yuvilio 23 Янв 2015 в 06:53

Короче говоря:

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

function endsWith($str, $needle){
   $length = strlen($needle);
   return !$length || substr($str, - $length) === $needle;
}
6
Vincent Pazeller 20 Апр 2012 в 01:39

ответ от 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

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

7
Veeno 21 Сен 2016 в 17:31

PHP 8.0

Начиная с PHP 8.0 реализовано два новых метода:

str_starts_with(string $haystack, string $needle): bool

Документация str_starts_with()

str_ends_with(string $haystack, string $needle): bool

Документация str_ends_with()

Однако они чувствительны к регистру. Функции возвращают истину или ложь.

$str = 'apples';

var_dump(str_starts_with($str, 'a')); // bool(true)
var_dump(str_starts_with($str, 'A')); // bool(false)

var_dump(str_ends_with($str, 's')); // bool(true)
var_dump(str_ends_with($str, 'S')); // bool(false)
9
Jsowa 4 Окт 2021 в 13:20

Сделайте это быстрее:

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

Эта дополнительная строка, сравнивая первый символ строк, может сделать false case return немедленно , поэтому многие из ваших сравнений намного быстрее (в 7 раз быстрее, чем я измерял). В действительности, вы практически не платите за производительность для этой единственной линии, поэтому я думаю, что это стоит включить. (Кроме того, на практике, когда вы тестируете много строк для определенного начального фрагмента, большинство сравнений не удастся, поскольку в типичном случае вы что-то ищете.)

ПРИМЕЧАНИЕ: ошибка в комментарии @ Tino ниже уже исправлена ​​

Что касается строк и целых чисел

Если вы хотите принудительно выполнить сравнение строк (то есть вы ожидаете, что startWith ("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)));
}

Я не думаю, что это необходимо, но это интересный крайний случай, ведущий к таким вопросам, как "логическое истина начинается с t?" - так что вы решаете, но убедитесь, что вы решили навсегда.

6
dkellner 17 Дек 2020 в 11:22
2
Ошибка в вашем коде: startsWith("123", "0") дает true
 – 
Tino
20 Июл 2018 в 14:10
Ага, плохо! $ Проверка произошла. Извините! (Просто хотел проиллюстрировать концепцию в строке 3)
 – 
dkellner
20 Июл 2018 в 17:43
Я бы сказал, что мы могли бы удалить эти 2 комментария прямо сейчас, вы не согласны? То есть, точка взята, это исправлено, и прошло 2 года.
 – 
dkellner
17 Дек 2020 в 11:21

Это может сработать

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

Источник: https://stackoverflow.com/a/4419658

5
Community 23 Май 2017 в 15:10

Функция 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

4
biziclop 21 Фев 2012 в 15:52

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

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

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

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

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

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

4
Peter Mortensen 12 Апр 2014 в 23:17
3
Кажется странным, что если вы ищете «xy» внутри строки «abcdefghijklmxyz» вместо того, чтобы просто сравнивать «x» с «a» и возвращать FALSE, вы просматриваете каждый символ от «a» до «m», а затем находите «xy» внутри строки, и, наконец, вы возвращаете FALSE, потому что ее позиция не равна нулю! Это то, что вы делаете, и это страннее и дико, чем любая другая безудержная функция здесь.
 – 
FrancescoMM
23 Янв 2014 в 19:05
Простота заключается в наборе текста, а не в логике.
 – 
Bill Effin Murray
23 Янв 2014 в 21:34
Дело не столько в логике, сколько в возможной оптимизации, на которую указывал Франко. Использование strpos() будет медленным, кроме случаев, когда оно действительно совпадает. strncmp() в этом случае было бы намного лучше.
 – 
Alexis Wilke
2 Июл 2014 в 04:33
Когда вы выполняете такие низкоуровневые функции, вы обычно хотите выбрать наиболее оптимизированное по скорости решение, каким бы сложным оно ни было, поскольку оно будет вызываться миллионы раз. Каждая микросекунда, которую вы здесь выиграете или проиграете, будет иметь очень большое значение. Так что лучше подправить его, черт возьми (а затем забыть о сложности, теперь, когда у вас есть функция), вместо того, чтобы искать взгляды и терять ужасающее количество времени позже, когда вы даже не знаете, что пошло не так. Представьте, что вы проверяете строку размером 2 ГБ, которая не совпадает.
 – 
dkellner
3 Сен 2018 в 16:09

Я бы сделал это так

     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;
        }
4
Jelle Keizer 7 Окт 2014 в 17:27
Забыть вернуть false, если оно не совпадает. Errgo неверно, так как возвращаемое значение функции не должно «предполагаться», но я знаю, к чему вы стремитесь, по крайней мере, по сравнению с другими ответами.
 – 
Spoo
7 Окт 2016 в 22:58

Обновление от 2021 г., PHP> 8.0

PHP 8.0 представил str_starts_with и str_ends_with.

str_starts_with( string $haystack , string $needle ) : bool
str_ends_with( string $haystack , string $needle ) : bool

В вашем примере у нас будет:

$str = '|apples}';
echo str_starts_with( $str, '|' ); //... true || 1
echo str_ends_with( $str, '}' ); //... true || 1
5
amarinediary 15 Мар 2021 в 18:59

Основываясь на ответе Джеймса Блэка, вот его конец с версией:

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 на функцию startWith Джеймса Блэка, потому что strncasecmp на самом деле является нечувствительной к регистру версией strncmp.

3
bobo 30 Окт 2009 в 03:17
2
Обратите внимание, что strrev() является творческим , но очень дорогостоящим, особенно если у вас есть строки, скажем ... 100 КБ.
 – 
Alexis Wilke
25 Июн 2014 в 06:45
Для уверенности используйте === вместо ==. 0 - это много чего в PHP.
 – 
nawfal
29 Янв 2016 в 14:38

Многие из предыдущих ответов будут работать так же. Тем не менее, это возможно настолько коротко, насколько вы можете сделать это и заставить его делать то, что вы хотите. Вы просто заявляете, что хотите, чтобы он «вернул истину». Поэтому я включил решения, которые возвращают логическое значение 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';
}
3
ThatOneCoder 27 Окт 2014 в 16:03
Правда. Однако Питер просил функцию, которая работала бы со строками символов. Тем не менее, я обновил свой ответ, чтобы вас успокоить.
 – 
ThatOneCoder
24 Окт 2014 в 01:02
После редактирования ваше решение полностью устарело. Он возвращает 'true' и 'false' как строки, которые оба являются true в логическом смысле. Хотя это хороший шаблон для чего-то вроде underhanded.xcott.com;)
 – 
Tino
26 Окт 2014 в 14:08
Ну, Питер только что заявил, что хочет, чтобы оно вернулось как «истина». Так что я решил, что верну то, о чем он просил. Я добавил обе версии, на всякий случай, если это не то, что он хотел.
 – 
ThatOneCoder
27 Окт 2014 в 16:04

Без копирования и без внутреннего цикла:

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;
}
3
hanshenrik 11 Май 2020 в 01:46
Это должно быть намного быстрее, чем реализация MrHus! я мог бы сравнить это
 – 
hanshenrik
11 Май 2020 в 01:46