PHP에서 startsWith () 및 endsWith () 함수


1479

문자열을 가져 와서 지정된 문자 / 문자열로 시작하거나 끝나는 경우 반환하는 두 가지 함수를 작성하는 방법은 무엇입니까?

예를 들면 다음과 같습니다.

$str = '|apples}';

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

19
잘 테스트 된 메소드는 Laravel의 Str 클래스 startsWith () 및 endsWith ()를 참조하십시오 . 에지 사례 가 발생 했으므로이 코드를 널리 사용하는 것이 유리합니다.
Gras Double


3
경고 : 여기의 대부분의 답변은 UTF-8과 같은 멀티 바이트 인코딩에서는 신뢰할 수 없습니다.
Álvaro González

위의 의견에 이어 최신 버전 (오늘 5.4 기준 ) 을 사용해야합니다 . 특히 startsWith ()는 큰 건초 더미 문자열에 최적화되었습니다.
Gras Double

답변:


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
endsWith ( 'foo', '') == false가 올바른 동작이라고 말하고 싶습니다. foo는 아무것도 끝나지 않기 때문입니다. 'Foo'는 'o', 'oo'및 'Foo'로 끝납니다.
MrHus

125
EndsWith 많이 짧은 쓸 수있다 :return substr($haystack, -strlen($needle))===$needle;
한국 Kralj에게

12
세 번째 매개 변수로 다음 if을 전달 하여 완전히 피할 수 있습니다 . 이것은 전체가 아닌 빈 문자열을 반환 하여 경우를 처리합니다 . $lengthsubstrreturn (substr($haystack, -$length, $length);$length == 0$haystack
mxxk

20
나는 예를 들어 mb_strlen 및 mb_substr, 멀티 바이트 안전 기능을 사용하는 것이 좋습니다 것입니다 @MrHus
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에서 가장 빠른 솔루션 중 하나 여야합니다 ( 벤치 마크 스크립트 ). 8KB 건초 더미, 다양한 길이의 바늘 및 전체, 부분 및 일치 사례에 대해 테스트되었습니다. strncmp로 시작하는 것이 더 빠르지 만 종료를 확인할 수는 없습니다.


74
이 답변은 Daily WTF에 전달되었습니다! : D dailywtf.com/articles/…
Wim ten Brink

@DavidWallace 및 @FrancescoMM 의견은이 답변의 이전 버전에 적용됩니다. 현재 답변은 strrpos바늘이 건초 더미의 시작과 일치하지 않으면 즉시 실패 해야하는 것을 사용합니다 .
Salman A

2
나는 그것을 얻지 못한다. php.net/manual/en/function.strrpos.php를 기반으로 : "값이 음수이면 문자열의 끝에서부터 많은 문자부터 검색이 시작됩니다." 이것은 우리가 (인해 문자 0에서 시작하고 있음을 나타내는 것 -strlength($haystack)) 및 검색 뒤로 거기에서? 그것은 당신이 아무것도 검색하지 않는다는 것을 의미하지 않습니까? 나는 또한 !== false이것 의 일부를 이해하지 못한다 . 나는 이것이 일부 값이 "truthy"하고 다른 값이 "false"인 PHP의 기발한 말에 의존하고 있지만이 경우 어떻게 작동합니까?
Welbog

3
@Welbog : 예를 들어 haystack = xxxyyyneedle = yyystrrpos검색 사용 은 처음부터 시작합니다 x. 이제 우리는 여기에 성공적으로 일치하지 않으며 (y 대신 x를 찾았습니다) 더 이상 뒤로 갈 수 없습니다 (문자열이 시작됩니다) 즉시 검색이 실패 합니다 . 위 예제에서 !== false- strrpos를 사용 하면 0 또는 false를 반환하지만 다른 값은 반환하지 않습니다. 마찬가지로 strpos위의 예에서 $temp(예상 위치)를 반환 하거나 false를 반환 할 수 있습니다 . !== false일관성 을 위해 함께 갔지만 기능 === 0=== $temp기능을 각각 사용할 수 있습니다 .
살만 A

8
@spoo 건초 더미가 크고 바늘이 존재하지 않으면 strpos === 0이 끔찍한 해결책이라는 것이 이미 확립되었습니다.
살만 A

243

2016 년 8 월 23 일 업데이트

기능

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


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 December

2
Darn, 나는 그때 내 머리에 무슨 일이 있었는지 모르겠다. 수면 부족.
Jronny

1
@mpen, 나는 코끼리가 전혀 보이지 않았다 :(
Visman

1
이러한 테스트는 성능 테스트에 좋지 않습니다. 당신이하고있는 일은 무작위 문자열을 바늘로 사용하는 것입니다. 99.99 %의 경우 일치하는 항목이 없습니다. 대부분의 함수는 첫 번째 바이트와 일치 한 후에 종료됩니다. 일치하는 경우는 어떻습니까? 성공적인 경기를 마치는 데 가장 적은 시간이 걸리는 기능은 무엇입니까? 99 %의 바늘이 일치하지만 마지막 몇 바이트는 일치하지 않는 경우는 어떻습니까? 어떤 기능이 일치하지 않는 데 가장 적은 시간이 걸립니까?
Salman A

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);
Enrico Detoma

6
strlen () 것은 불필요하지 않습니다. 문자열이 주어진 바늘로 시작하지 않으면 ur 코드는 불필요하게 전체 건초 더미를 스캔합니다.
AppleGrew

5
@Mark yea, 시작 부분 만 확인하는 것이 훨씬 빠릅니다. 특히 MIME 유형 (또는 문자열이 큰 다른 곳)을 확인하는 것과 같은 일을하는 경우
chacham15

2
@mark 나는 1000 숯 건초 더미와 10 또는 800 숯 바늘로 벤치 마크를했으며 strpos는 30 % 더 빠릅니다. 어떤 것이 더 빠르거나 그렇지
않다고 말하기

7
당신은 강하게처럼 바늘을 인용 고려해야 strpos($haystack, "$needle", 0)가 있다면 어떤 (그것은에서오고있는 경우, 예를 들어, 기회는 이미 문자열이 아니다 json_decode()). 그렇지 않으면 [odd] 기본 동작으로 strpos()예기치 않은 결과가 발생할 수 있습니다. " needle이 문자열이 아닌 경우 정수로 변환되어 문자의 서수 값으로 적용됩니다. "
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);
}

신용 :

문자열이 다른 문자열로 끝나는 지 확인

문자열이 다른 문자열로 시작하는지 확인


1
strtolower는 대소 문자를 구분하지 않는 함수를 만드는 가장 좋은 방법은 아닙니다. 일부 로케일에서 케이싱은 단지 위와 아래보다 더 복잡합니다.
Sander Rijken

8
나는 불평하고 해결책이 없다고 생각한다 ... 그것이 나쁘다고 말한다면, 그것이 어떻게되어야하는지에 대한 예를 제시해야한다.
KdgDev

2
@WebDevHobo : 그렇기 때문에 댓글 하루 전에 답변을 직접 추가했습니다. 귀하의 코드에 대해 strcasecmp는 실제로 옳은 일이었습니다.
Sander Rijken

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에서 매개 변수의 순서는 $ haystack, $ needle입니다. 이 함수는 거꾸로되어 있으며 순서가 $ needle, $ haystack 인 배열 함수처럼 작동합니다.
Andy

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()하므로 위에 표시된 대로 함수 (예 :)를 정의 할 필요가 없습니다 .

그러나 다시 말하지만 이것은 모든 경우에 대한 해결책이 아니라 매우 구체적인 것입니다.


문자열을 하드 코딩 할 필요가 없습니다. 정규식 동적 일 있습니다.
Ryan

2
@self true이지만 문자열이 하드 코딩되지 않은 경우 이스케이프해야합니다. 현재이 질문에 대한 두 가지 답변이 있습니다. 이것은 쉽지만 코드를 조금 복잡하게 만듭니다. 제 요점은 하드 코딩이 가능한 매우 간단한 경우에는 간단하게 유지할 수 있다는 것입니다.
noamtm

1
슬래시를 이스케이프 처리하지 않아도됩니다. 정규식을와 같은 다른 문자로 감싸서 @슬래시 ( /)를 이스케이프 처리하지 않아도됩니다. php.net/manual/en/function.preg-match.php의 예제 # 3을 참조하십시오 .
cjbarth

감사합니다 @cjbarth. 이에 따라 내 대답을 변경했습니다. BTW, "#"는 슬래시를 다룰 때 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
창조적. 건초 더미를 포함하는 바늘. BTW :와 함께 추악한 약탈이 endsWithChar('','x')있지만 결과는 정확합니다
Tino

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

@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(이 경고를 표시하지 않으 려면 ..을 사용하십시오 .
Tino

17

가장 빠른 endsWith () 솔루션 :

# 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 버전에서 문자열 비교 함수에 극적인 개선을 본 적이
크리스토프 Deliens

1
@ChristopheDeliens와 그의 PHP 버전 제공 요청 에코. 7.3.2에서 테스트를 실행했으며 유사한 결과를 FWIW로 얻었습니다.
Jeff

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를 의미합니다. 오프셋을 지정할 수 없습니다. 그것은 endsWith 함수가 다른 방법을 완전히 사용해야한다는 것을 의미합니다.
mpen

@ Mark-끝을 위해 strrpos ( php.net/manual/en/function.strrpos.php )를 사용하지만 일반적으로 strcmp를 사용하려고 할 때 strncmp가 더 안전한 옵션 일 것입니다.
James Black

11

당신이 사용할 수있는 strposstrrpos

$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.
Kalyan 2016 년

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
나는 이것이 단지 CPU 낭비라고 확신합니다. StarstWith와 EndsWith에 대해 확인해야 할 것은 바이트가 일치하는지 확인하는 것입니다. 그리고 그것이 받아 들인 대답이 정확히 수행하는 것입니다. 이 1은 바늘의 utf8 문자 수를 계산하는 데 시간을 낭비하고 건초 더미의 n 번째 utf8 문자 위치는 100 % 확실하지 않으면 CPU의 낭비 일뿐이라고 생각합니다. 수락 된 답변이 실패하는 실제 테스트 사례를 생각해 낼 수 있습니까?
hanshenrik 0시 6 분

2
@hanshenrik-UTF8과 동일한 바이트를 포함하지만 마지막 문자의 절반이 누락 된 문자열을 찾을 때 매우 드물게 발생할 수 있습니다. 마찬가지로 유니 코드 C5 91 (문자 "ő")이 있고 C5 (문자 "Å")를 찾으면 일치하지 않아야합니다. 반면에, utf 건초 더미에서 utf가 아닌 바늘을 찾는 이유는 무엇입니까? 그러나 방탄 검사의 경우 이것이 가능성으로 간주되어야합니다.
dkellner

에서에게 startsWith이 있어야한다$length = mb_strlen($needle, 'UTF-8');
토마스 Kekeisen

2
@ThomasKekeisen 감사합니다.
Vahid Amiri

8

정규식이없는 짧고 이해하기 쉬운 라이너.

startsWith ()는 간단합니다.

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

endsWith ()는 약간 공상적이고 느린 strrev ()를 사용합니다.

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

@FrancescoMM : strpos가 "올바른 도구"가 아닙니다 ... 왜? 그렇다면 "올바른 도구"는 무엇입니까? 편집 : 아래 답변을 읽었습니다. 나는 프로그래밍이 당신이 가진 자원을 사용하는 발명과 같다고 생각했다. 따라서 옳고 그른 것은 없다.
Fr0zenFyr

"비교가 아닌 검색 도구이기 때문에?" Cit. 아리스토텔레스
FrancescoMM 8

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이 전체 문자열을 반복하지 않는다고 가정하지만 (이 경우에도) 이것은 전혀 편리하지 않습니다.


첫 번째 문자가 다르면 속도가 향상됩니다.
Ja͢ck

2
@Jack 예, 물론 아이디어는 통계적으로 발생하므로 전체 테스트 세트 (일반적으로 다르지 않은 경우 포함)에서 속도가 일반적으로 20 % -30 %입니다. 그들이 다르면 많이 얻습니다. 그렇지 않으면 아주 느슨합니다. 평균적으로 30 %를 얻습니다 (세트에 따라 다르지만 대부분 대규모 테스트에서 속도를 얻습니다)
FrancescoMM

"그러나 그것은이 일을위한 올바른 도구가 아니다" ... 어떤 인용?
Fr0zenFyr

1
WTF. 나는 아래에 누구보다 인용해야하는 모든 과정을 열거 했습니까? 주먹 끝이 'a'가 아님을 나타 내기 위해 문자열 끝까지 검색하는 함수를 사용 하시겠습니까? 누가 신경 쓰나요? 검색을위한 도구이기 때문에 비교하기위한 도구가 아니므로 Aristoteles를 인용 할 필요가 없습니다.
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

나는 일반적으로 요즘 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   

라이브러리에는 다른 편리한 기능이 가득합니다.


6

대답 하여 mpen는 불행하게도, 제공된 벤치 마크는 매우 중요하고 해로운 감독이 믿을 수 없을만큼 철저하게,하지만.

바늘과 건초 더미의 모든 바이트가 완전히 임의적이기 때문에 첫 번째 바이트에서 바늘-건초 더미 쌍이 다를 확률은 99.609375 %입니다. 즉, 평균적으로 100000 쌍 중 약 99609가 첫 번째 바이트에서 다름을 의미합니다. . 다시 말해, 벤치 마크는 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
Tino

예, 나쁜! $ 확인이 발생했습니다. 죄송합니다! (3
열의

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

일치하지 않으면 false를 반환하는 것을 잊었습니다. Errgo는 함수의 반환 값이 '가정'되어서는 안되므로 잘못되었지만 적어도 다른 답변과 비교하여 당신이 겪고있는 것을 알고 있습니다.
Spoo

3

James Black의 답변을 바탕으로 끝은 다음과 같습니다.

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

}

참고 : strncasecmp는 실제로 대소 문자를 구분하지 않는 strncmp 버전이므로 James Black의 startsWith 함수에 if-else 부분을 바꿨습니다.


2
(가) 있습니다 strrev()이며 창조적 인 당신이 ... 100KB를 말의 문자열을 가지고 특히,하지만 매우 비용이 많이 드는.
Alexis Wilke

===대신에 사용 ==하십시오. 0PHP에서 많은 것들과 같습니다.
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바늘이 건초 더미에서 발견되지 않은 경우 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 . "!";
}

이 시나리오에서는 startsWith () 함수가 필요하지 않습니다.

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

true 또는 false를 정확하게 반환합니다.

여기서 모든 야생 기능이 만연해있는 것이 이상하지 않은 것 같습니다.


3
"x"를 "a"와 비교하고 FALSE를 반환하는 대신 문자열 "abcdefghijklmxyz"에서 "xy"를 검색하는 경우 모든 문자를 "a"에서 "m"으로 찾은 다음 "xy"를 찾는 것이 이상해 보입니다. 문자열 내부에서 마지막으로 위치가 0이 아니기 때문에 FALSE를 반환합니다! 이것은 당신이하고있는 일이며, 여기 다른 어떤 기능보다 이상하고 거칠습니다.
FrancescoMM

단순성은 논리가 아니라 타이핑에 있습니다.
Kade Hafen

그다지 논리는 아니지만 Francsco가 지적한 가능한 최적화입니다. strpos()일치하지 않는 한 사용 이 느려집니다. strncmp()이 경우 훨씬 나을 것입니다.
Alexis Wilke

이러한 저수준 기능을 수행 할 때는 일반적으로 수백만 번 호출되므로 복잡하지 않더라도 가장 속도가 최적화 된 솔루션을 원합니다. 여기에서 얻거나 잃을 때마다 매우 큰 차이가 생길 것입니다. 따라서 외모를 바꾸고 나중에 무엇이 잘못되었는지 알지 못할 때 끔찍한 시간을 잃는 대신 지옥을 더 잘 조정하십시오 (그런 다음 기능에 대한 복잡성을 잊어 버립니다). 일치하지 않는 2GB 문자열을 확인한다고 상상해보십시오.
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';
}

진실. 그러나 Peter는 문자열에서 작동하는 함수를 요청했습니다. 그럼에도 불구하고, 나는 당신을 달래기 위해 나의 대답을 업데이트했습니다.
wynshaft

편집 후 솔루션은 이제 완전히 사용되지 않습니다. 그것은 반환 'true'하고 'false'모두 문자열로 true부울 의미에서. underhanded.xcott.com 과 같은 경우에는 좋은 패턴입니다 .)
Tino

피터는 방금 '참'을 돌려 주길 원한다고 말했습니다. 그래서 나는 그가 요청한 것을 돌려 줄 것이라고 생각했습니다. 그가 원하지 않는 경우를 대비하여 두 버전을 모두 추가했습니다.
wynshaft

2

정규식을 사용할 수도 있습니다.

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

3
$ needle은로 이스케이프되어야합니다 preg_quote($needle, '/').
Timo Tijhof

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의 구현보다 훨씬 빠릅니다! 나는 그것을 벤치마킹 할 수있다
hanshenrik

1

다음은 PHP 4를위한 효율적인 솔루션 substr_compare입니다 strcasecmp(substr(...)). PHP 5를 사용하는 경우 대신을 사용하여 더 빠른 결과를 얻을 수 있습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.