функции functionsWith () и окончание () в PHP

1483

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

Например:

$str = '|apples}';

echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Нажмите Upvote
источник
19
Посмотрите класс Str Laravel startWith () и endWith () для хорошо протестированных методов. Пограничные случаи встречались, поэтому широкое использование этого кода является преимуществом.
Гра Дабл
1
Вы можете найти s($str)->startsWith('|')и s($str)->endsWith('}')помочь, как найти в этой автономной библиотеке .
кар
3
Предупреждение: большинство ответов здесь ненадежны в многобайтовых кодировках, таких как UTF-8.
Альваро Гонсалес
Следуя моим комментариям, вы можете использовать последнюю версию (на сегодня 5.4 ). Примечательно, что launchWith () был оптимизирован для больших строк сена.
Двойной

Ответы:

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

Используйте это, если вы не хотите использовать регулярные выражения.

MrHus
источник
16
+1 Это чище, чем принятый ответ. Кроме того, $lengthне требуется в последней строке endsWith().
слишком много php
13
Я бы сказал, что конец с ('foo', '') == false - это правильное поведение. Потому что foo не заканчивается ничем. «Foo» оканчивается на «o», «oo» и «Foo».
MrHus
126
Концы могут быть написаны намного короче:return substr($haystack, -strlen($needle))===$needle;
Рок Краль
12
Вы можете избежать ifвовсе, передав в $lengthкачестве третьего параметра substr: return (substr($haystack, -$length, $length);. Это обрабатывает случай $length == 0, возвращая пустую строку, а не всю $haystack.
mxxk
20
@MrHus Я бы порекомендовал использовать многобайтовые безопасные функции, например, mb_strlen и mb_substr
19Gerhard85
1025

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

Салман А
источник
74
Этот ответ добрался до Daily WTF! : D См. Thedailywtf.com/articles/…
Вим тен Бринк
Обратите внимание, что комментарии @DavidWallace и @FrancescoMM относятся к более старой версии этого ответа. Текущий ответ использует, strrposкоторый (должен) немедленно завершиться неудачей, если стрелка не соответствует началу стога сена.
Салман A
2
Я не понимаю Основано на php.net/manual/en/function.strrpos.php : «Если значение отрицательное, вместо этого поиск будет начинаться с такого количества символов с конца строки, поиск в обратном направлении». Кажется, это указывает на то, что мы начинаем с символа 0 (из-за -strlength($haystack)) и ищем оттуда назад ? Разве это не значит, что вы ничего не ищете? Я также не понимаю !== falseчасти этого. Я предполагаю, что это опирается на причуду PHP, где некоторые значения являются «правдивыми», а другие «ложными», но как это работает в этом случае?
Welbog
3
@Welbog: например, haystack = xxxyyyneedle = yyyи strrposпоиск начинается с первого x. Теперь у нас нет успешного совпадения (найдено x вместо y), и мы больше не можем идти назад (мы находимся в начале строки), поиск немедленно завершается неудачей . Об использовании !== false- strrposв приведенном выше примере будет возвращено 0 или false, а не другое значение. Аналогично, strposв приведенном выше примере может возвращаться $temp(ожидаемая позиция) или false. Я !== falseсогласился, но вы можете использовать === 0и === $tempфункции соответственно.
Салман A
8
@ spoo уже было установлено, что strpos === 0 - ужасное решение, если стог сена большой, а иголка не существует.
Салман A
243

Обновлено 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

startswith_benchmark.php

mpen
источник
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
2
Черт, я не знаю, что пришло мне в голову в то время. Полностью отсутствие сна.
Джронни
1
@mpen, я вообще не заметил слона :(
Висман
1
Эти тесты не годятся для тестирования производительности. То, что вы делаете, использует случайную строку в качестве иглы. В 99,99% случаев совпадения не будет. Большинство функций завершится после сопоставления первого байта. А как насчет случаев, когда совпадение найдено? Какая функция занимает меньше времени, чтобы завершить успешное совпадение? Как насчет случаев, когда 99% иглы совпадают, но не последние несколько байтов? Какая функция занимает наименьшее количество времени, чтобы завершить сопоставление?
Салман А
137

Все ответы до сих пор , кажется, делают кучу ненужной работы, 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;
}
Сандер Рийкен
источник
2
endsWith()Функция имеет ошибку. Его первая строка должна быть (без -1): $expectedPosition = strlen($haystack) - strlen($needle);
Энрико Детома
6
Функция strlen () не является ненужной. Если строка не начинается с указанной иглы, тогда ваш код будет сканировать весь стог сена без необходимости.
AppleGrew
5
@ Отметьте, да, проверка только начала - ОЧЕНЬ быстрее, особенно если вы делаете что-то вроде проверки типов MIME (или любого другого места, где строка должна быть большой)
chacham15
2
@ mark Я провел несколько тестов с 1000 стогом сена и 10 или 800 иголками, и strpos был на 30% быстрее. Сделайте свои тесты, прежде чем заявить, что что-то быстрее или нет ...
wdev
7
Вы должны серьезно рассмотреть со ссылкой на иглу , как strpos($haystack, "$needle", 0)если есть какой - нибудь шанс , что это не является строкой (например, если это исходит от json_decode()). В противном случае поведение по умолчанию [нечетное] strpos()может привести к неожиданным результатам: « Если игла не является строкой, она преобразуется в целое число и применяется в качестве порядкового значения символа. »
quietmint
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);
}

Кредит для :

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

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

KdgDev
источник
1
strtolower - не лучший способ сделать регистронезависимыми функции. В некоторых регионах обсадная колонна является более сложной, чем просто верхняя и нижняя.
Сандер Рийкен
8
Я вижу жалобы и никакого решения ... Если вы скажете, что это плохо, то вам следует привести пример того, как это должно быть.
KdgDev
2
@WebDevHobo: вот почему я добавил ответ сам за день до вашего комментария. Для вашего кода strcasecmp был действительно правильным решением.
Сандер Рийкен
29

Регулярное выражение работает выше, но с другими настройками, также предложенными выше:

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

 function endsWith($needle, $haystack) {
     return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
tridian
источник
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()), как показано выше.

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

noamtm
источник
вам не нужно жестко кодировать строку. регулярное выражение может быть динамическим.
Райан
2
@self верно, но если строка не является жестко закодированной, вы должны ее избежать. В настоящее время есть 2 ответа на этот вопрос, которые делают это. Это легко, но немного усложняет код. Поэтому я хотел сказать, что для очень простых случаев, когда возможно жесткое кодирование, вы можете сделать его простым.
noamtm
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')но результат верный
Тино
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 и ИМХО лучший ответ. Но endsWidthдолжен сделать return $needle==='' || substr_compare(... так что он работает, как ожидалось, для -strlen($needle)===0которого, без исправления, делает endsWith('a','')возвратfalse
Tino
@Tino Спасибо ... Я чувствую, что это на substr_compare()самом деле ошибка , поэтому я добавил PR, чтобы исправить это :)
Ja͢ck
3
Вызов endsWith('', 'foo')вызывает предупреждение: «substr_compare (): начальная позиция не может превышать начальную длину строки». Возможно, это еще одна ошибка substr_compare(), но чтобы ее избежать, вам нужна предварительная проверка, например ... || (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
gx_
@ gx_ Не нужно замедляться с большим количеством кода. Просто используйте return $needle === '' || @substr_compare(.., чтобы подавить это предупреждение.
Тино
17

Самое быстрое решение заканчивается с ():

# 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 в другую :)
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
@Mark - вы можете посмотреть на принятый ответ, но я предпочитаю использовать strncmp в основном потому, что считаю его более безопасным.
Джеймс Блэк
Я имею в виду именно с strncmp. Вы не можете указать смещение. Это будет означать, что вашей функции EndWith придется использовать совершенно другой метод.
mpen
@Mark - для концов. Я бы просто использовал strrpos ( php.net/manual/en/function.strrpos.php ), но, как правило, всякий раз, когда вы используете strcmp, strncmp, вероятно, является более безопасным вариантом.
Джеймс Блэк
11

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

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
закон
источник
1
Должны ли вы использовать тройные равные здесь, strpos($sHaystack, $sNeedle) == 0как это strpos($sHaystack, $sNeedle) === 0? Я вижу ошибку, когда false == 0оценивает true.
Калян
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
Я уверен, что это просто трата процессора. все, что вам нужно проверить, для StarstWith и EndsWith, это просто проверить, что байты совпадают, и это именно то, что делает принятый ответ. эта 1 тратит впустую время, вычисляя количество символов иглы utf8, и где положение n-ого символа utf8 стога сена ... я думаю, не будучи уверенным на 100%, это просто пустая трата процессора. Можете ли вы придумать реальный тестовый случай, когда принятый ответ не получается, а это не так?
Ханшенрик
2
@hanshenrik - это может произойти, между прочим, в очень редком случае, когда вы ищете строку, которая содержит те же байты, что и UTF8, но с отсутствующей половиной последнего символа. Например, у вас есть Unicode C5 91 (буква «ő»), и вы ищете C5 (буква «Å»), он не должен совпадать. С другой стороны, конечно, зачем вам искать в стоге сена utf иглу без утфеля ... Но для пуленепробиваемых проверок это должно считаться возможным.
dkellner
В startsWithнем должно быть$length = mb_strlen($needle, 'UTF-8');
Томас Кекейсен
2
@ThomasKekeisen Спасибо, исправил это.
Вахид Амири
8

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

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

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

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

function endsWith($haystack, $needle) {
   return (strpos(strrev($haystack), strrev($needle)) === 0);
}
Дэн
источник
@FrancescoMM: strpos не "правильный инструмент" ... Почему? Каковы "правильные инструменты" тогда? РЕДАКТИРОВАТЬ: я прочитал ваш ответ ниже. Я думал, что программирование - это как изобретение, использующее имеющиеся у вас ресурсы. Так что нет правильного или неправильного ... только работает или не работает ... производительность вторична.
Fr0zenFyr
"потому что это инструмент для поиска, а не для сравнения?" Соч. Аристотель
FrancescoMM
7

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

FrancescoMM
источник
Там только ускорение, если первые символы разные.
Ja͢ck
2
@ Джек, да, конечно, идея в том, что статистически это происходит, поэтому ускорение обычно составляет 20% -30% по всему тестовому набору (включая случаи, когда он не отличается). Вы получаете много, когда они разные, и очень мало, когда нет. В среднем вы получаете 30% (варьируется в зависимости от набора, но в основном вы набираете скорость на больших тестах)
FrancescoMM
"но это не правильный инструмент для этой работы" ... Любая цитата?
Fr0zenFyr
1
WTF. Я перечислил весь процесс ниже, кого я должен процитировать, более того? Вы бы использовали функцию, которая ищет до конца строки, чтобы сказать вам, что первый символ не является «а»? Делай это, кого это волнует? Это не правильный инструмент, потому что это инструмент для поиска, а не для сравнения, нет необходимости цитировать Аристотеля, чтобы утверждать очевидное!
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';
Srinivasan.S
источник
6

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

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

yuvilio
источник
6

Ответ на 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

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

Veeno
источник
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 раз быстрее, когда я измерял). В истинном случае вы практически не платите за производительность этой единственной линии, так что я думаю, что это стоит того. (Кроме того, на практике, когда вы тестируете много строк для определенного начального блока, большинство сравнений не пройдут, поскольку в типичном случае вы что-то ищете.)

dkellner
источник
2
Ошибка в вашем коде: startsWith("123", "0")даетtrue
Тино
Да, плохо! Произошла проверка. Сожалею! (Просто хотел проиллюстрировать концепцию в строке 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/en/function.substr-compare.php

biziclop
источник
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, если он не совпадает. Ошибка неправильная, так как возвращаемое значение функции не следует «предполагать», но я знаю, что вы собираетесь, по крайней мере, по сравнению с другими ответами.
Spoo
3

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

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 для функции JamesW Black StartSith, потому что strncasecmp на самом деле является нечувствительной к регистру версией strncmp.

бобо
источник
2
Обратите внимание, что strrev()это креативно, но очень дорого, особенно если у вас есть, скажем, 100Kb.
Алексис Уилке
Используйте ===вместо того, ==чтобы быть уверенным. 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 . "!";
}

Вывод:

Найдено значение в начале стоимостного стога!

Имейте в виду, 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 . "!";
}

В этом сценарии нет необходимости использовать функцию launchWith () как

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

вернет true или false точно.

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

Kade Hafen
источник
3
Кажется странным, что если вы ищете «xy» внутри строки «abcdefghijklmxyz» вместо того, чтобы просто сравнивать «x» с «a» и возвращать FALSE, вы просматриваете каждый символ от «a» до «m», а затем в конечном итоге находите «xy» внутри строки, и, наконец, вы возвращаете FALSE, потому что его позиция не равна нулю! Это то, что вы делаете, и это странно и дико, чем любая другая безудержная функция здесь.
FrancescoMM
Простота в наборе, а не в логике.
Kade Hafen
Это не столько логика, сколько возможная оптимизация, на которую указывал Франко. Использование strpos()будет медленным, кроме случаев, когда оно совпадает. strncmp()было бы намного лучше в этом случае.
Алексис Уилк
Когда вы выполняете такие низкоуровневые функции, вы, как правило, хотите выбрать наиболее оптимизированное по скорости решение, независимо от того, насколько оно сложное, поскольку это будет вызываться миллионы раз. Каждая микросекунда, которую вы получаете или теряете, будет иметь реальное значение. Так что лучше избавиться от этого (а затем забыть о сложности, теперь, когда у вас есть функция), вместо того, чтобы искать внешний вид и потерять ужасное количество времени спустя, когда вы даже не знаете, что пошло не так. Представьте, что вы проверяете строку размером 2 ГБ, которая не соответствует.
dkellner
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';
}
wynshaft
источник
Правда. Однако Питер запрашивал функцию, которая бы работала со строками символов. Тем не менее, я обновил свой ответ, чтобы успокоить вас.
Wynshaft
После редактирования ваше решение теперь полностью устарело. Он возвращает 'true'и в 'false'виде строк, которые оба trueимеют логическое значение. Это хороший пример для чего-то вроде underhanded.xcott.com ;)
Тино
Ну, Питер только что заявил, что хочет, чтобы это вернуло «истину». Так что я решил вернуть то, что он просил. Я добавил обе версии, на случай, если это не то, что он хотел.
wynshaft
2

Вы также можете использовать регулярные выражения:

function endsWith($haystack, $needle, $case=true) {
  return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
почетный гражданин
источник
3
$ needle должен быть экранирован preg_quote($needle, '/').
Тимо Тихгоф
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;
}
mazatwork
источник
это должно быть намного быстрее, чем реализация MrHus! я мог бы
сравнить
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);
ya.teck
источник
предупреждение, не бинарный безопасный и даже не безопасный от игл, содержащих подстановочные знаки = /
hanshenrik