функцій startWith () і закінчуєWith () в PHP


1479

Як я можу записати дві функції, які братимуть рядок і повертатимуться, якщо він починається із зазначеного символу / рядка або закінчується ним?

Наприклад:

$str = '|apples}';

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

19
Дивіться клас Str на Laravel, починаючи з () і закінчуючиWith (), про добре перевірені методи. Зустрічалися випадкові випадки , тому широке використання цього коду є перевагою.
Подвійний Гра

1
Ви можете знайти s($str)->startsWith('|')і s($str)->endsWith('}')корисні, як це знайдено в цій самостійній бібліотеці .
каре

3
Попередження: більшість відповідей тут недостовірні в багатобайтових кодуваннях, таких як UTF-8.
Альваро Гонсалес

Дотримуючись мого вище коментаря, ви можете переконатися, що ви використовуєте останню версію (станом на сьогодні, 5.4 ). Зокрема, startWith () оптимізовано для великих струн сіна.
Двомісний гра

Відповіді:


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

Використовуйте це, якщо ви не хочете використовувати регулярний вираз.


16
+1 Це чистіше, ніж прийнята відповідь. Крім того, $lengthне потрібен в останньому рядку endsWith().
занадто багато php

13
Я б сказав, що endWith ('foo', '') == false - це правильна поведінка. Тому що foo не закінчується нічим. 'Foo' закінчується на 'o', 'oo' і 'Foo'.
MrHus

125
Закінчення можна написати набагато коротше:return substr($haystack, -strlen($needle))===$needle;
Rok Kralj

12
Ви можете уникнути ifзовсім, передавши в $lengthякості третьої параметра substr: return (substr($haystack, -$length, $length);. Це обробляє випадок $length == 0повернення порожнього рядка, а не цілого $haystack.
mxxk

20
@MrHus Я рекомендував би використовувати багатобайтові безпечні функції, наприклад mb_strlen та mb_substr
19Gerhard85

1024

Ви можете використовувати 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/…
Wim ten Brink

Зауважте, що коментарі @DavidWallace та @FrancescoMM стосуються старішої версії цієї відповіді. Поточна відповідь використовує те, strrposщо (слід) негайно вийти з ладу, якщо голка не відповідає початку стога сіна.
Салман

2
Я не розумію. На підставі php.net/manual/en/function.strrpos.php : "Якщо значення негативне, пошук замість цього почне починати з тих численних символів з кінця рядка, шукаючи назад." Це, мабуть, свідчить про те, що ми починаємо з символу 0 (через -strlength($haystack)) і шукаємо назад звідти? Це не означає, що ви нічого не шукаєте? Я також не розумію !== falseчастин цього. Я здогадуюсь, що це покладається на химерність PHP, де одні значення "тривожні", а інші "хибні", але як це працює в цьому випадку?
Вельбог

3
@Welbog: наприклад, стог сечі = xxxyyyголка = yyyі використання strrposпошуку починається з першого x. Зараз у нас немає успішного поєднання тут (знайдено x замість y), і ми вже не можемо повернутися назад (ми починаємо рядок) пошук не вдається негайно . Про використання !== false- strrposу наведеному вище прикладі повернеться 0 або false, а не інше значення. Так само strposу наведеному прикладі може повернутися $temp(очікувана позиція) або помилково. Я пішов з !== falseпослідовністю, але ви могли використовувати === 0і === $tempвідповідно функції.
Салман

8
@spoo вже встановлено, що strpos === 0 - це жахливе рішення, якщо стог сіна великий, а голки не існує.
Салман

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

startwith_benchmark.php


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
Дарн, я не знаю, що мені тоді пішло в голову. Пролічуйте недолік сну.
Jronny

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 голками та стріповими на 30% швидше. Зробіть свої орієнтири перед тим, як заявити, що щось швидше чи ні ...
wdev

7
Ви повинні серйозно розглянути з посиланням на голку , як strpos($haystack, "$needle", 0)якщо є якийсь - небудь шанс , що це не є рядком (наприклад, якщо це виходить від json_decode()). В іншому випадку поведінка за замовчуванням [непарне] strpos()може призвести до несподіваних результатів: " Якщо голка не є рядком, вона перетворюється на ціле число і застосовується як порядкове значення символу. "
тихість

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

Кредит :

Перевірте, чи закінчується рядок іншим рядком

Перевірте, чи починається рядок з іншого рядка


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

2
У php для рядкових операцій упорядкування параметрів - $ стог сіна, $ голка. ці функції зворотні і діють як функції масиву, де впорядкування насправді $ голка, $ копиця сіна.
Енді

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()), як показано вище.

Але знову ж таки - це не рішення для кожної справи, просто ця дуже специфічна.


вам не потрібно жорстко кодувати рядок. регулярний вираз може бути динамічним.
Райан

2
@ Сам правдивий, але якщо рядок не жорстко закодований, вам доведеться уникати цього. На даний момент є 2 відповіді на це питання, які це роблять. Це легко, але це лише трохи ускладнює код. Тож моя думка полягала в тому, що для дуже простих випадків, коли можливе жорстке кодування, ви можете зберегти це просто.
noamtm

1
Вам також не потрібно уникати косу рису, ви можете зафіксувати регулярний вираз в якомусь іншому символі, наприклад @, таким чином, щоб сліш ( /) не потрібно було уникати. Дивіться приклад №3 тут: php.net/manual/en/function.preg-match.php .
cjbarth

Дякую @cjbarth. Відповідно змінив мою відповідь. BTW, "#" - приклад, наведений у php.net/manual/en/regexp.reference.delimiters.php при роботі з косою рисою.
noamtm

23

Якщо швидкість важлива для вас, спробуйте це (я вважаю, що це найшвидший метод)

Працює лише для рядків, і якщо $ копиця сіна лише 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 та IMHO. Але endsWidthслід зробити return $needle==='' || substr_compare(... так це працює так, як очікувалося -strlen($needle)===0, і без виправлення, endsWith('a','')повертаєтьсяfalse
Тіно,

@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 до іншої :)
Крістоф Делінс

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

як би ви покінчили з цим?
квітня 11

@Mark - ви можете подивитися на прийняту відповідь, але я вважаю за краще використовувати strncmp головним чином тому, що вважаю, що це безпечніше.
Джеймс Блек

Я маю на увазі конкретно з strncmp. Не можна вказати зміщення. Це означає, що для вашої функції ENDWith доведеться повністю використовувати інший метод.
квітня 11

@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%, це просто марна трата процесора. чи можете ви придумати справжній тестовий випадок, коли прийнята відповідь не відповідає, а це не відбувається?
hanshenrik

2
@hanshenrik - це може статися btw, в дуже рідкісному випадку, коли ви шукаєте рядок, який містить ті самі байти, що і UTF8, але половина останнього символу відсутня. Мовляв, у вас є unicode C5 91 (літера "ő"), і ви шукаєте C5 (літера "Å"), він не повинен відповідати вам. З іншого боку, впевнено, навіщо ви шукати стовбур сіна для голки, що не є utf ... Але для куленепробивної перевірки це потрібно вважати можливим.
dkellner

У startsWithньому має бути$length = mb_strlen($needle, 'UTF-8');
Томас Кекейзен

2
@ThomasKekeisen Спасибі, виправив це.
Вахід Амірі

8

Короткі та зрозумілі однолінійки без регулярних виразів.

startWith () прямо вперед.

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

endWith () використовує злегка химерний і повільний strrev ():

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

@FrancescoMM: strpos не є "правильним інструментом" ... Чому? Які тоді "правильні інструменти"? EDIT: Я прочитав вашу відповідь нижче. Я думав, що програмування - це як винахід із використанням наявних у вас ресурсів. Тож немає правильного чи неправильного ... лише працюючи чи не працюючи ... продуктивність є другорядним.
Fr0zenFyr

"тому що це інструмент для пошуку, а не для порівняння?" Цитувати. Арістотель
FrancescoMM

7

Якщо зосередитись на startwith, якщо ви впевнені, що рядки не порожні, додавання тесту на перший знак, перед порівнянням, strlen тощо, трохи прискорює:

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

Це якось (на 20% -30%) швидше. Якщо додати ще один тест, наприклад, $ стог сіна {1} === $ голка {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 не повторює весь рядок (але навіть у цьому випадку), це зовсім не зручно.


Прискорити швидкість можна лише в тому випадку, якщо перші символи відрізняються.
Ja͢ck

2
@ Jaa так, звичайно, ідея полягає в тому, що статистично це відбувається, тому швидкість загалом становить 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';

6

Я зазвичай закінчую роботу з такою бібліотекою, як підкреслення-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   

Бібліотека повна інших зручних функцій.


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

Звичайно, цей еталон може все-таки не бути абсолютно неупередженим, але він перевіряє ефективність алгоритмів, коли даються також частково збігаючі голки.


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 разів швидше, коли я вимірював). У справжньому випадку ви платите практично не за ціну за цей єдиний рядок, тому я думаю, що це варто включити. (Крім того, на практиці, коли ви тестуєте багато рядків для конкретного початкового фрагменту, більшість порівнянь не вдасться, оскільки в типовому випадку ви щось шукаєте.)


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



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

Забувши повернути помилкове, якщо воно не відповідає. Errgo невірно, як і повернене значення функції, не слід «припускати», але я знаю, що ви збираєтеся принаймні порівняно з іншими відповідями.
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 на функцію beginWith Джеймса Блека, оскільки strncasecmp - це фактично нечутлива до регістру версія strncmp.


2
Зверніть увагу , що strrev()є творчим , але дуже дорого, особливо якщо у вас є рядки говорять ... 100 Кб.
Алексіс Вілке

Використовуйте ===замість того, ==щоб бути впевненим. 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 . "!";
}

Вихід:

Знайдене значення на початку valuehaystack!

Майте на увазі, strposповернеться помилково, якщо голка не була знайдена в копиці сіна, і поверне 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 . "!";
}

У цьому сценарії немає необхідності у запуску функціїWith () як

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

повернеться справжньою чи хибною точно.

Здається дивним, що це все просто, коли всі дикі функції працюють тут.


3
Здається дивним, що якщо ви шукаєте "xy" всередині рядка "abcdefghijklmxyz" замість того, щоб просто порівнювати "x" з "a" і повертати FALSE, ви переглядаєте кожного символу від "a" до "m", а потім знаходяться "xy" всередині рядка, і нарешті ви повертаєте FALSE, оскільки його позиція не дорівнює нулю! Це те, що ви робите, і це дивно і диво, ніж будь-яка інша розгульна функція тут.
FrancescoMM

Простота полягає в наборі тексту, а не в логіці.
Каде Хафен

Це не стільки логіка, скільки можлива оптимізація, на яку вказував Франкско. Використання strpos()буде повільним, за винятком випадків, коли воно відповідає. strncmp()було б набагато краще в цьому випадку.
Alexis Wilke

Виконуючи такі низькорівневі функції, ви, як правило, бажаєте вирішити найбільш оптимізовану швидкість рішення, незалежно від того, наскільки складно, оскільки це буде називатися мільйонами разів. Кожна мікросекунда, яку ви отримаєте або втратите тут, буде дуже реальною. Тож краще виправити це з пекла (а потім забудьте про складність, тепер, коли у вас є функція), замість того, щоб піти на вигляд і втратити жахливий час пізніше, коли ви навіть не знаєте, що пішло не так. Уявіть, що перевіряєте рядок 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

Після редагування рішення зараз остаточно застаріло. Він повертається 'true'і 'false'як рядки, які є обома trueв булевому значенні. Хороший зразок для чогось на кшталт underhanded.xcott.com, хоча;)
Тіно

Ну, Петро просто заявив, що хоче, щоб це повернулося "правдою". Тож я подумав, що поверну те, що він просив. Я додав обидві версії, на всякий випадок, якщо це не те, що він хотів.
wynshaft

2

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

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

3
$ голку слід уникнути 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;
}

це має бути набагато швидше, ніж впровадження 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);

попередження, не бінарний безпечний і навіть не безпечний проти голок, що містять символи = /
hanshenrik
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.