функции startsWith () и endsWith () в PHP
Как я могу написать две функции, которые будут принимать строку и возвращать, если она начинается с указанного символа / строки или заканчивается ею?
например:
$str = '|apples}';
echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
30 ответов
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);
}
использовать это, если вы не хотите использовать регулярное выражение.
можно использовать strrpos
и strpos
чтобы проверить старт-с, а концы-с соответственно.
обратите внимание, что с помощью strrpos
для проверки начинается с and strpos
для проверки концов с вернется как можно скорее, вместо того, чтобы проверять всю строку до конца. Кроме того, это решение не создает временную строку. Рассмотрите возможность объяснения причины Перед downvoting. Просто потому, что f-wit в DWTF не понимает, как это функция работает или думает, что есть только одно решение, не означает, что этот ответ неправильный.
function startsWith($haystack, $needle) {
// search backwards starting from haystack length characters from the end
return $needle === ''
|| strrpos($haystack, $needle, -strlen($haystack)) !== false;
}
function endsWith($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;
}
тесты и результаты (сравнение с этой):
startsWith('abcdef', 'ab') -> true
startsWith('abcdef', 'cd') -> false
startsWith('abcdef', 'ef') -> false
startsWith('abcdef', '') -> true
startsWith('', 'abcdef') -> false
endsWith('abcdef', 'ab') -> false
endsWith('abcdef', 'cd') -> false
endsWith('abcdef', 'ef') -> true
endsWith('abcdef', '') -> true
endsWith('', 'abcdef') -> false
Примечание:strncmp
и substr_compare
функции будут превосходить эту функцию.
Обновлено 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
все ответы до сих пор, похоже, делают массу ненужной работы,strlen calculations
, string allocations (substr)
, etc. The '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;
}
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);
}
Кредит:
функции regex выше, но с другими настройками, также предложенными выше:
function startsWith($needle, $haystack) {
return preg_match('/^' . preg_quote($needle, '/') . '/', $haystack);
}
function endsWith($needle, $haystack) {
return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
}
Если скорость важна для вас, попробуйте это.(Я считаю, что это самый быстрый способ)
работает только для строк, и если $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
Я понимаю, что это было закончено, но вы можете посмотреть на strncmp как это позволяет поставить длину строки для сравнения, так:
function startsWith($haystack, $needle, $case=true) {
if ($case)
return strncasecmp($haystack, $needle, strlen($needle)) == 0;
else
return strncmp($haystack, $needle, strlen($needle)) == 0;
}
вот две функции, которые не вводят временную строку, которая может быть полезна, когда иглы существенно большие:
function startsWith($haystack, $needle)
{
return strncmp($haystack, $needle, strlen($needle)) === 0;
}
function endsWith($haystack, $needle)
{
return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
на этот вопрос уже есть много ответов, но в некоторых случаях вы можете согласиться на что-то более простое, чем все из них. Если строка, которую вы ищете, известна (жестко закодирована), вы можете использовать регулярные выражения без цитирования и т. д.
Проверьте, начинается ли строка с 'ABC':
preg_match('/^ABC/', $myString); // "^" here means beginning of string
заканчивается на "ABC":
preg_match('/ABC$/', $myString); // "$" here means end of string
в моем простом случае, я хотел проверить, если строка заканчивается Слэшем:
preg_match('/\/$/', $myPath); // slash has to be quoted
преимущество: в виду того что оно очень короток и просто, вам не нужно определять функцию (например,endsWith()
), как показано выше.
но опять же -- это не решение для каждого случая, просто это очень специфичная.
короткие и простые для понимания однострочные без регулярных выражений.
startsWith () прямо вперед.
function startsWith($haystack, $needle) {
return (strpos($haystack, $needle) === 0);
}
endsWith () использует слегка причудливый и медленный strrev ():
function endsWith($haystack, $needle) {
return (strpos(strrev($haystack), strrev($needle)) === 0);
}
фокусировка на startswith, если вы уверены, что строки не пусты, добавление теста на первом символе, перед сравнением, strlen и т. д., ускоряет вещи немного:
function startswith5b($haystack, $needle) {
return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;
}
это как-то (20%-30%) быстрее. Добавление еще одного теста char, например $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 не повторяет всю строку (но даже в этом случае), это совсем не удобно.
Я обычно в конечном итоге происходит с библиотекой, как подчеркивание-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
библиотека полна других удобных функций.
на ответ by 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
конечно, этот тест все еще может быть не совсем беспристрастным, но он проверяет эффективность алгоритмов при частично совпадающих иглах.
вот многобайтовая безопасная версия принятого ответа, она отлично работает для строк UTF-8:
function startsWith($haystack, $needle)
{
$length = mb_substr($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);
}
Я надеюсь, что приведенный ниже Ответ может быть эффективным, а также простым:
$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';
на 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'));
и
короче:
function startsWith($str, $needle){
return substr($str, 0, strlen($needle)) === $needle;
}
function endsWith($str, $needle){
$length = strlen($needle);
return !$length || substr($str, - $length) === $needle;
}
самый быстрый endsWith () решение:
# 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
Это может работать
function startsWith($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) == $needle;
}
источник:https://stackoverflow.com/a/4419658
Почему не на следующей?
//How to check if a string begins with another string
$haystack = "valuehaystack";
$needle = "value";
if (strpos($haystack, $needle) === 0){
echo "Found " . $needle . " at the beginning of " . $haystack . "!";
}
выход:
найдено значение в начале valuehaystack!
имейте в виду, strpos
вернет false, если игла не была найдена в стоге сена, и вернет 0, если и только если игла была найдена в индексе 0 (он же начало).
и вот эндсвит:
$haystack = "valuehaystack";
$needle = "haystack";
//If index of the needle plus the length of the needle is the same length as the entire haystack.
if (strpos($haystack, $needle) + strlen($needle) === strlen($haystack)){
echo "Found " . $needle . " at the end of " . $haystack . "!";
}
в этом сценарии нет необходимости в функции startsWith() as
(strpos($stringToSearch, $doesItStartWithThis) === 0)
вернет true или false точно.
кажется странным, что это так просто со всеми дикими функциями, запущенными здесь.
Я бы сделал так
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;
}
просто рекомендация:
function startsWith($haystack,$needle) {
if($needle==="") return true;
if($haystack[0]<>$needle[0]) return false;
if(substr_compare($haystack,$needle,0,strlen($needle))==0) return true;
return false;
}
эта дополнительная строка, сравнивая первый символ строк, может сделать false case return тут, поэтому многие из ваших сравнений намного быстрее (7X быстрее, когда я измерял). В истинном случае вы не платите практически никакой цены за эту единственную строку, поэтому я думаю, что ее стоит включить. (Кроме того, на практике, когда вы тестируете много строк для определенного начального куска, большинство сравнений не удастся, так как в типичный случай, когда ты что-то ищешь.)
основываясь на ответе Джеймса Блэка, вот его версия 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 для функции startsWith Джеймса Блэка, потому что strncasecmp на самом деле является нечувствительной к регистру версией strncmp.
вы также можете использовать регулярные выражения:
function endsWith($haystack, $needle, $case=true) {
return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
многие из предыдущих ответов будут работать так же хорошо. Однако это, возможно, так коротко, как вы можете сделать это и сделать то, что вы хотите. Вы просто заявляете, что хотели бы, чтобы это "вернуло истину". Поэтому я включил решения, которые возвращают логическое 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';
}
вот эффективное решение для 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;
}
$ends_with = strrchr($text, '.'); // Ends with dot
$start_with = (0 === strpos($text, '.')); // Starts with dot
Не уверен, почему это так трудно для людей. Substr выполняет отличную работу и эффективен, так как вам не нужно искать всю строку, если она не соответствует.
кроме того, поскольку я не проверяю целочисленные значения, но сравниваю строки, мне не обязательно беспокоиться о строгом === случае. Однако это хорошая привычка.
function startsWith($haystack,$needle) {
substring($haystack,0,strlen($needle)) == $needle) { return true; }
return false;
}
function endsWith($haystack,$needle) {
if(substring($haystack,-strlen($needle)) == $needle) { return true; }
return false;
}
или даже лучше оптимизирован.
function startsWith($haystack,$needle) {
return substring($haystack,0,strlen($needle)) == $needle);
}
function endsWith($haystack,$needle) {
return substring($haystack,-strlen($needle)) == $needle);
}