str_replace를 사용하여 첫 번째 경기에서만 작동합니까?


답변:


346

preg_replace 로 수행 할 수 있습니다 :

function str_replace_first($from, $to, $content)
{
    $from = '/'.preg_quote($from, '/').'/';

    return preg_replace($from, $to, $content, 1);
}

echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); 
// outputs '123def abcdef abcdef'

마술은 선택 사항 인 네 번째 매개 변수 [Limit]에 있습니다. 설명서에서 :

[제한]-각 주제 문자열에서 각 패턴에 대한 가능한 최대 교체입니다. 기본값은 -1입니다 (제한 없음).


그러나 보다 효율적인 방법에 대해서는 zombat의 답변 을 참조하십시오 (대략 3-4 배 빠름).


39
이 방법의 단점은 정규 표현식의 성능 저하입니다.
zombat

27
또 다른 단점은 "바늘"에 preg_quote ()를 사용하고 대체에서 메타 문자 $ 및 \를 이스케이프해야한다는 것입니다.
Josh Davis

32
이스케이프 문제로 인해 일반적인 솔루션으로 실패합니다.
Jeremy Kauffman

2
너무 자주 정규 표현식이 '성능'으로 인해 해제됩니다. 성능이 주요 관심사라면 PHP를 작성하지 않을 것입니다! '/'이외의 다른 것을 사용하여 패턴을 줄 바꿈 할 수 있습니다. 아마도 '~'는 이스케이프 문제를 어느 정도 피할 수 있습니다. 데이터가 무엇이며 어디에서 왔는지에 따라 다릅니다.
ThomasRedstone

1
성능 문제는 제쳐두고-이스케이프 문제에 대해 불평하는 사람들은 잠재적 인 버그 외에도 특별한 것을 염두에두고 preg_quote있습니까? 예를 들어 @ThomasRedstone은 구분 기호 /가에 표시되면 위험 할 수 있다고 걱정하지만 불행히도 $from그렇지 않습니다. preg_quote두 번째 매개 변수 때문에 올바르게 이스케이프 처리 됩니다 (쉽게 테스트 할 수 있음). 특정 문제 (내 책의 심각한 PCRE 보안 버그 일 수 있음)에 대해 듣고 싶습니다.
MvanGeest

610

그것의 버전은 없지만 해결책은 전혀 해킹되지 않습니다.

$pos = strpos($haystack, $needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
}

매우 쉽고 정규 표현식의 성능 저하를 줄입니다.


보너스 : 마지막 발생 을 바꾸려면 strrpos대신에 사용하십시오 strpos.


17
정규식보다 훨씬 빠르며 적은 메모리를 사용합니다. 누군가가 그 아래로 투표를 왜 아무 생각이 ...
조쉬 데이비스

12
이 방법이 마음에 들지만 코드에 오류가 있습니다. substr_replace 호출의 마지막 매개 변수는 strlen ($ replace) 대신 strlen ($ needle)이어야합니다.
넬슨

무슨 일이 일어나고 있는지 알아내는 데 상당히 많은 시간이 걸린다는 점에서 "해킹"입니다. 또한 명확한 코드라면 코드에 오류가 있다고 언급되지 않았을 것입니다. 그런 작은 발췌문에서 실수를 할 수 있다면 이미 너무 해키입니다.
Camilo Martin

9
나는 줄 수와 실수 가능성과 관련하여 @CamiloMartin에 동의하지 않습니다. substr_replace모든 매개 변수로 인해 사용하기가 다소 다루기 어려운 함수 이지만 실제 문제는 숫자로 문자열 조작을 수행하는 것이 까다로울 수 있다는 것입니다. 함수에 올바른 변수 / 오프셋을 전달해야합니다. 나는 실제로 위의 코드가 가장 간단하고 나에게 논리적 인 접근 방식이라고 말하기까지 했어요.
Alex

1
훌륭한 접근. 정규 표현식 문자를 예약 한 변수 값을 바꿀 때 완벽하게 작동합니다 (따라서 preg_replace는 곰입니다). 이것은 간단하고 우아합니다.
Praesagus

96

편집 : 두 답변이 모두 업데이트되었으며 이제 정확합니다. 함수 타이밍이 여전히 유용하기 때문에 답을 남겨 두겠습니다.

'zombat'과 '너무 많은 PHP'의 답변은 불행히도 정확하지 않습니다. 이것은 zombat가 게시 한 답변에 대한 개정입니다 (댓글을 게시 할만 큼 평판이 좋지 않기 때문에).

$pos = strpos($haystack,$needle);
if ($pos !== false) {
    $newstring = substr_replace($haystack,$replace,$pos,strlen($needle));
}

strlen ($ replace) 대신 strlen ($ needle)에 유의하십시오. Zombat의 예제는 바늘과 교체 길이가 동일한 경우에만 올바르게 작동합니다.

PHP 자체의 str_replace와 동일한 서명을 가진 함수의 기능은 다음과 같습니다.

function str_replace_first($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos !== false) {
        return substr_replace($subject, $replace, $pos, strlen($search));
    }
    return $subject;
}

이것은 '너무 많은 PHP'의 수정 된 답변입니다.

implode($replace, explode($search, $subject, 2));

끝에 1 대신 2를 기록하십시오. 또는 함수 형식으로 :

function str_replace_first($search, $replace, $subject) {
    return implode($replace, explode($search, $subject, 2));
}

두 기능의 시간을 정했으며 일치하는 항목이 없으면 첫 번째 기능이 두 배 빠릅니다. 일치하는 것이 발견 될 때 동일한 속도입니다.


str_replace_flexible (mixed $ s, mixed $ r, int $ offset, int $ limit)와 같이 일반화하지 않는 이유는 함수가 $ offset (nth) 일치에서 시작하여 $ limit 발생을 대체합니다.
Adam Friedman

이 방법은 대소 문자를 구분하는 교체에만 적용됩니다.
andreszs

4
@ 앤드류 stripos()구출 :-)
Gras Double

76

어느 것이 가장 빠른지 궁금해서 모두 테스트했습니다.

아래에서 찾을 수 있습니다 :

  • 이 페이지에 기여한 모든 기능의 포괄적 인 목록
  • 각 구성에 대한 벤치 마크 테스트 (1 만회 이상의 평균 실행 시간)
  • 각 답변에 대한 링크 (전체 코드)

모든 기능은 동일한 설정으로 테스트되었습니다.

$string = 'OOO.OOO.OOO.S';
$search = 'OOO'; 
$replace = 'B';

문자열 내 에서 처음 나타나는 문자열 만 바꾸는 함수 :


문자열 내 에서 마지막으로 나타나는 문자열 만 바꾸는 함수 :


감사합니다. 일반적으로 preg_replace를 사용합니다. 대부분의 경우 향후 조정이 필요할 경우 가장 유연하므로 27 % 느려질 것입니다
zzapper

@oLinkWebDevelopment 벤치 마크 스크립트를보고 싶습니다. 나는 그것이 유용 할 수 있다고 생각합니다.
Dave Morton

substr_replace()결과를 얻는 이유 는 간단합니다. 내부 함수이기 때문입니다. 내부 기능은 하위 계층에서 실행되므로 두 가지 동일한 기능을 수행하는 내부 및 사용자 정의 함수는 성능이 다릅니다. 그렇다면 왜 안 preg_match()되겠습니까? 정규식은 문자열을 여러 번 검색하기 때문에 모든 내부 문자열 조작 함수보다 거의 느립니다.
MAChitgarha

1
당신의 "우승자"( substr_replace($string, $replace, 0, strlen($search));) 의 벤치 마크가 단지 정적이라고 쓰지 않기를 바랍니다 0. 비정규 솔루션의 컨볼 루션 중 일부는 교체 할 위치를 알기 전에 시작점을 "찾아야"한다는 것입니다.
mickmackusa

55

불행히도, 나는 이것을 할 수있는 PHP 기능을 모른다.
다음과 같이 쉽게 자신의 것을 굴릴 수 있습니다.

function replace_first($find, $replace, $subject) {
    // stolen from the comments at PHP.net/str_replace
    // Splits $subject into an array of 2 items by $find,
    // and then joins the array with $replace
    return implode($replace, explode($find, $subject, 2));
}

나는 이것 대신에 모두를 사용 하는 가장 골치 아픈 버전 이라고 생각한다 . joinimplode
Titus

return implode($replace, explode($find, $subject, $limit+1));맞춤 교체 번호
beppe9000 23.08에

7

Regexp가 필요없이 문자열의 문자열 (대소 문자 구분)을 제한으로 바꾸는 이 작은 함수를 만들었습니다 . 잘 작동합니다.

function str_replace_limit($search, $replace, $string, $limit = 1) {
    $pos = strpos($string, $search);

    if ($pos === false) {
        return $string;
    }

    $searchLen = strlen($search);

    for ($i = 0; $i < $limit; $i++) {
        $string = substr_replace($string, $replace, $pos, $searchLen);

        $pos = strpos($string, $search);

        if ($pos === false) {
            break;
        }
    }

    return $string;
}

사용법 예 :

$search  = 'foo';
$replace = 'bar';
$string  = 'foo wizard makes foo brew for evil foo and jack';
$limit   = 2;

$replaced = str_replace_limit($search, $replace, $string, $limit);

echo $replaced;
// bar wizard makes bar brew for evil foo and jack

차라리 할 것이 비록 ===false대신 is_bool(더 명시 적으로 -이 엄지 손가락을주는거야 는 정규식 광기를 피했다해서 ! ... 그리고 동시에 작동하고 깨끗한 솔루션입니다 ...
jave.web

쉽게 사용자 정의 할 수있는 preg_솔루션을 선호하는 것은 광기가 아니라 개인적인 취향입니다. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);정규식을 두려워하지 않는 사람들에게는 읽기 쉽습니다. 대소 문자를 구분하지 않는 검색이 필요하십니까? i끝 패턴 구분 기호 뒤에 추가하십시오 . 유니 코드 / 멀티 바이트 지원이 필요하십니까? u끝 패턴 구분 기호 뒤에 추가하십시오 . 단어 경계 지원이 필요하십니까? \b검색 문자열의 양쪽에 추가하십시오 . 정규식을 원하지 않으면 정규식을 사용하지 마십시오. 물론 말을위한 말이지 만 광기는 아닙니다.
mickmackusa

3

가장 쉬운 방법은 정규식을 사용하는 것입니다.

다른 방법은 strpos ()를 사용하여 문자열의 위치를 ​​찾은 다음 substr_replace ()를 찾는 것입니다

그러나 나는 정말로 RegExp에 갈 것입니다.


이 "힌트"는이 페이지의 다른 게시물과 비교할 때 다소 모호합니다.
mickmackusa

3
function str_replace_once($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos === false) {
        return $subject;
    }

    return substr($subject, 0, $pos) . $replace . substr($subject, $pos + strlen($search));
}

코드 전용 답변은 StackOverflow에서 가치가 낮습니다. 왜냐하면 그들은 수천 명의 미래 연구원을 교육 / 강화하는 데 열악한 역할을하기 때문입니다.
mickmackusa

3

=> 코드가 수정되었으므로 너무 오래된 주석을 고려하십시오.

그리고 모두 감사합니다 나를 돕는는 것을 개선하기 위해

모든 버그, 저에게 알려주십시오. 내가 바로 고칠거야

그래서 가자 :

예를 들어 첫 번째 'o''ea' 로 바꾸기 :

$s='I love you';
$s=str_replace_first('o','ea',$s);
echo $s;

//output: I leave you

함수:

function str_replace_first($a,$b,$s)
         {
         $w=strpos($s,$a);
         if($w===false)return $s;
         return substr($s,0,$w).$b.substr($s,$w+strlen($a));
         }

$이 $ aaa vs aaaaaaaaa와 같은 문자를 반복하면 실패
Cristo

나는 substr($where,$b+strlen($this))그렇지 않다고 생각한다 substr($where,$b+1). 그리고 나는 그것이 substr_replace더 빠르다고 생각합니다 .
Titus

코드가 수정되었습니다. 이제 긴 문자열에서도 작동합니다
PYK

이 솔루션은 코딩 된대로 작동하지 않습니다. 증명 : 3v4l.org/cMeZj 변수 이름 문제를 해결하면 검색 값을 찾을 수 없으면 입력 문자열이 손상됩니다. 증명 : 3v4l.org/XHtfc
mickmackusa

누군가가 코드를 수정하도록 요청하는 것이 공정합니까? @mickmackusa 다시 확인해 주시겠습니까?
PYK

2
$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;

이것은 첫 번째 대답과 동일합니다. 게다가, 당신은 무엇을해야 preg_quote의를 $find표현으로 사용하기 전에.
Emil Vikström 2016 년

이것이 내가 사용한 것이므로 투표했습니다. 첫 번째 답변은 Drupal과 충돌을 일으켰으므로 Drupal 도우미 기능을 덮어 써야합니다. 방금 함수 내부에있는 코드를 가져 와서 나머지 코드와 함께 사용했습니다.
Dan Mantyla

이 코드 전용 답변은 페이지에 대한 중복 조언을 제공합니다 ( preg_quote()
결함

2

@renocor의 답변 을 확장하기 위해 와 100 % 역 호환되는 함수를 작성했습니다 str_replace(). 즉, 대체 할 수있다 모든 의 발생 str_replace()과를 str_replace_limit()에 대해 아무 것도 엉망으로하지 않고 심지어 사용하여 배열을 $search, $replace및 / 또는 $subject.

이 기능은 당신이 함수 호출을 대체하고자한다면, 완전히 독립적 일 수 ($string===strval(intval(strval($string))))있지만, 이후는 반대 권하고 싶습니다 valid_integer()문자열로 제공 정수를 처리 할 때 상당히 유용한 기능입니다.

참고 : 가능할 때마다 대신 str_replace_limit()사용 str_replace()되므로 성능에 대한 염려없이 모든 호출을 str_replace()대체 할 수 있습니다 str_replace_limit().

용법

<?php
$search = 'a';
$replace = 'b';
$subject = 'abcabc';
$limit = -1; // No limit
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

교체 2 개-bbcbbc

$limit = 1; // Limit of 1
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

교체 1 개-bbcabc

$limit = 10; // Limit of 10
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

교체 2 개-bbcbbc

함수

<?php

/**
 * Checks if $string is a valid integer. Integers provided as strings (e.g. '2' vs 2)
 * are also supported.
 * @param mixed $string
 * @return bool Returns boolean TRUE if string is a valid integer, or FALSE if it is not 
 */
function valid_integer($string){
    // 1. Cast as string (in case integer is provided)
    // 1. Convert the string to an integer and back to a string
    // 2. Check if identical (note: 'identical', NOT just 'equal')
    // Note: TRUE, FALSE, and NULL $string values all return FALSE
    $string = strval($string);
    return ($string===strval(intval($string)));
}

/**
 * Replace $limit occurences of the search string with the replacement string
 * @param mixed $search The value being searched for, otherwise known as the needle. An
 * array may be used to designate multiple needles.
 * @param mixed $replace The replacement value that replaces found search values. An
 * array may be used to designate multiple replacements.
 * @param mixed $subject The string or array being searched and replaced on, otherwise
 * known as the haystack. If subject is an array, then the search and replace is
 * performed with every entry of subject, and the return value is an array as well. 
 * @param string $count If passed, this will be set to the number of replacements
 * performed.
 * @param int $limit The maximum possible replacements for each pattern in each subject
 * string. Defaults to -1 (no limit).
 * @return string This function returns a string with the replaced values.
 */
function str_replace_limit(
        $search,
        $replace,
        $subject,
        &$count,
        $limit = -1
    ){

    // Set some defaults
    $count = 0;

    // Invalid $limit provided. Throw a warning.
    if(!valid_integer($limit)){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting an '.
                'integer', E_USER_WARNING);
        return $subject;
    }

    // Invalid $limit provided. Throw a warning.
    if($limit<-1){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_WARNING);
        return $subject;
    }

    // No replacements necessary. Throw a notice as this was most likely not the intended
    // use. And, if it was (e.g. part of a loop, setting $limit dynamically), it can be
    // worked around by simply checking to see if $limit===0, and if it does, skip the
    // function call (and set $count to 0, if applicable).
    if($limit===0){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_NOTICE);
        return $subject;
    }

    // Use str_replace() whenever possible (for performance reasons)
    if($limit===-1){
        return str_replace($search, $replace, $subject, $count);
    }

    if(is_array($subject)){

        // Loop through $subject values and call this function for each one.
        foreach($subject as $key => $this_subject){

            // Skip values that are arrays (to match str_replace()).
            if(!is_array($this_subject)){

                // Call this function again for
                $this_function = __FUNCTION__;
                $subject[$key] = $this_function(
                        $search,
                        $replace,
                        $this_subject,
                        $this_count,
                        $limit
                );

                // Adjust $count
                $count += $this_count;

                // Adjust $limit, if not -1
                if($limit!=-1){
                    $limit -= $this_count;
                }

                // Reached $limit, return $subject
                if($limit===0){
                    return $subject;
                }

            }

        }

        return $subject;

    } elseif(is_array($search)){
        // Only treat $replace as an array if $search is also an array (to match str_replace())

        // Clear keys of $search (to match str_replace()).
        $search = array_values($search);

        // Clear keys of $replace, if applicable (to match str_replace()).
        if(is_array($replace)){
            $replace = array_values($replace);
        }

        // Loop through $search array.
        foreach($search as $key => $this_search){

            // Don't support multi-dimensional arrays (to match str_replace()).
            $this_search = strval($this_search);

            // If $replace is an array, use the value of $replace[$key] as the replacement. If
            // $replace[$key] doesn't exist, just an empty string (to match str_replace()).
            if(is_array($replace)){
                if(array_key_exists($key, $replace)){
                    $this_replace = strval($replace[$key]);
                } else {
                    $this_replace = '';
                }
            } else {
                $this_replace = strval($replace);
            }

            // Call this function again for
            $this_function = __FUNCTION__;
            $subject = $this_function(
                    $this_search,
                    $this_replace,
                    $subject,
                    $this_count,
                    $limit
            );

            // Adjust $count
            $count += $this_count;

            // Adjust $limit, if not -1
            if($limit!=-1){
                $limit -= $this_count;
            }

            // Reached $limit, return $subject
            if($limit===0){
                return $subject;
            }

        }

        return $subject;

    } else {
        $search = strval($search);
        $replace = strval($replace);

        // Get position of first $search
        $pos = strpos($subject, $search);

        // Return $subject if $search cannot be found
        if($pos===false){
            return $subject;
        }

        // Get length of $search, to make proper replacement later on
        $search_len = strlen($search);

        // Loop until $search can no longer be found, or $limit is reached
        for($i=0;(($i<$limit)||($limit===-1));$i++){

            // Replace 
            $subject = substr_replace($subject, $replace, $pos, $search_len);

            // Increase $count
            $count++;

            // Get location of next $search
            $pos = strpos($subject, $search);

            // Break out of loop if $needle
            if($pos===false){
                break;
            }

        }

        // Return new $subject
        return $subject;

    }

}

4
부탁하면 좀 부풀어 오른다. 또한이 솔루션에서 가장 '증오'하는 것은 오류 처리입니다. 잘못된 값을 전달하면 스크립트가 중단됩니다. 당신은 그것이 전문적으로 보인다고 생각하지만, 오류 대신에 대신 통지 또는 경고를 생성합니다. 헛소리를 건너 뛰고 대신 false를 반환하거나 이와 같은 함수에 역 추적을 사용하지 않는 것이 좋습니다. 가장 좋은 해결책은 프로그래머가 출력이 잘못되었거나 예기치 않은 경우 수행 할 작업을 결정할 수 있다는 것입니다.
코드 비트

이 사용 @Erwinus E_USER_WARNINGA는 전역, 경고 , 하지 오류 . 역 추적은 어떤 코드가 유효하지 않은 데이터를 함수에 먼저 전달하는지 (생산에서 버그를 추적하는 데 절대적으로 필요함) 알아내는 데 매우 유용합니다. 반환으로 $subject대신 false/ null또는 단순히 내 사용 사례에 대한 개인의 선택이었다 오류를 던지고. str_replace()의 기능 과 일치하려면 잡을 수있는 치명적인 오류를 사용하는 것이 가장 좋습니다 str_replace().
0b10011

아, 사용중인 E_USER_WARNING에 대해 알지 못했습니다. 죄송합니다. 주제를 반환하는 문제는 함수 외부에 문제가 있다는 것을 결코 볼 수 없다는 것입니다. 즉, 기능을 똑똑하게 사용하면 기능의 절반 크기가 될 수 있습니다 (가능한 경우). 둘째, 설명은 복잡하지만 가치를 높이는 것과 같은 간단한 것에는 유용하지 않은 것을 설명 할 때 좋습니다. 전반적으로 나는 그것이 불필요하다고 생각합니다. 또한 기본적으로 런타임 메시지 (로그)를 억제하지 않는 서버에서이 코드를 사용하면 프로덕션 환경에서 경고를 사용하는 것이 보안 문제가 될 수 있습니다.
코드 비트

@Erwinus 나는 어떤 사람들은 다른 사람들뿐만 아니라 언어를 이해하지 못하기 때문에 자세한 의견을 들었습니다. 모든 경우에 대해 동일한 최종 결과를 얻는 더 좋은 방법을 알고 있다면 반드시 답변을 편집하십시오. 프로덕션 환경에서 오류 메시지가 표시되지 않으면이 기능보다 큰 문제가있는 것입니다.)
0b10011

TL; DR이 스 니펫은 너무 부풀어 서 정규 표현식 기능을 선택하는 것을 상상할 수 없습니다 (스크롤이 싫어요). 교체 횟수를 계산하려면에 해당 매개 변수가 있습니다 preg_replace(). 또한 preg_replace()/ regex는 비정규 함수가 우아하게 제공하지 않는 단어 경계 처리 (원하는 경우)를 제공합니다.
mickmackusa

2

내 테스트 결과에 따르면 karim79에서 제공하는 regular_express에 투표하고 싶습니다. (지금 투표 할만큼 평판이 좋지 않습니다!)

zombat의 솔루션은 너무 많은 함수 호출을 사용하며 코드를 단순화합니다. PHP 5.4를 사용하여 두 가지 솔루션을 100,000 번 실행했으며 결과는 다음과 같습니다.

$str = 'Hello abc, have a nice day abc! abc!';
$pos = strpos($str, 'abc');
$str = substr_replace($str, '123', $pos, 3);

==> 1.85 초

$str = 'Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/', '123', $str, 1);

==> 1.35 초

보시다시피 preg_replace의 성능은 많은 사람들이 생각하는 것만 큼 나쁘지 않습니다. 따라서 정규 표현이 복잡하지 않으면 고급 솔루션을 제안합니다.


첫 번째 스 니펫은 올바른 구현을 사용하지 못하기 때문에 불공평 한 비교입니다. 당신은 확인되지 않은 $pos위해 false, 그래서 바늘이 건초 더미에 존재하지 않을 때, 그것은 출력을 손상시킬 수 있습니다.
mickmackusa 2014

감사합니다 @ mickmackusa, 당신이 맞아요. 그러나 그것은 요점이 아닙니다. 나는이 코드가 구현의 효율성을 비교하기 위해 단순화되었다고 말했다.
헌터 우

바로 내 요점입니다. 정확히 동일한 프로세스를 수행하지 않는 벤치 마크 비교를 수행해서는 안됩니다. 사과를 반 오렌지와 비교하는 것은 유용하지 않습니다. 완전한 비정규 접근 방식을 완전히 구현하면 속도 차이가 더 심오해질 것입니다.
mickmackusa 2019

또 고마워 그러나 내가 원하는 것은 더 큰 차이를 만들지 않고 더 나은 구현을 찾는 것입니다.
헌터 우

2

zombat의 답변 (최고의 답변이라고 생각)을 확장 $limit하기 위해 교체 할 발생 수를 지정하는 매개 변수를 사용하는 재귀 버전의 함수를 만들었습니다 .

function str_replace_limit($haystack, $needle, $replace, $limit, $start_pos = 0) {
    if ($limit <= 0) {
        return $haystack;
    } else {
        $pos = strpos($haystack,$needle,$start_pos);
        if ($pos !== false) {
            $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
            return str_replace_limit($newstring, $needle, $replace, $limit-1, $pos+strlen($replace));
        } else {
            return $haystack;
        }
    }
}

품질 검사는 없으므로 $start_pos문자열 길이를 초과하면이 함수는 다음을 생성 Warning: strpos(): Offset not contained in string...합니다. 이 기능이 $start_pos길이를 초과 하면 교체하지 못합니다 . 실패 증명 : 3v4l.org/qGuVIR ... 함수는 return $haystack조건을 결합하고 다음 과 같은 단일 사용 변수 선언을 피할 수 있습니다 . 3v4l.org/Kdmqp 그러나이 페이지의 다른 부분에서 언급 한 것처럼 오히려 매우 깨끗하고 직접적인 비 재귀 preg_replace()호출을 사용하십시오.
mickmackusa 2019

네 그래서 당신은이 라인 추가 할 수있는 else한 Statment$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

$string = 'OOO.OOO.OOO.S';
$search = 'OOO';
$replace = 'B';

//replace ONLY FIRST occurance of "OOO" with "B"
    $string = substr_replace($string,$replace,0,strlen($search));
    //$string => B.OOO.OOO.S

//replace ONLY LAST occurance of "OOOO" with "B"
    $string = substr_replace($string,$replace,strrpos($string,$search),strlen($search)) 
    //$string => OOO.OOO.B.S

    //replace ONLY LAST occurance of "OOOO" with "B"
    $string = strrev(implode(strrev($replace),explode(strrev($search),strrev($string),2)))
    //$string => OOO.OOO.B.S

단일 문자

$string[strpos($string,$search)] = $replace;


//EXAMPLE

$string = 'O.O.O.O.S';
$search = 'O';
$replace = 'B';

//replace ONLY FIRST occurance of "O" with "B" 
    $string[strpos($string,$search)] = $replace;  
    //$string => B.O.O.O.S

//replace ONLY LAST occurance of "O" with "B" 
    $string[strrpos($string,$search)] = $replace; 
    // $string => B.O.O.B.S

검색 문자열이 입력 문자열의 오프셋 0에 있지 않으면 첫 번째 substr_replace () 스 니펫이 실패합니다. 실패 증명 : 3v4l.org/oIbRv 그리고 두 substr_replace()기술 모두 검색 값이 없으면 입력 문자열을 손상시킵니다. 실패의 증거 : 3v4l.org/HmEml (그리고 모두와 마지막 기술 rev통화가 심각하게 눈에 / 하드 뒤얽힌됩니다.)
mickmackusa

2

사람들이 말한 것을 보완하면 전체 문자열이 배열임을 기억하십시오.

$string = "Lorem ipsum lá lá lá";

$string[0] = "B";

echo $string;

"보렘 ipsum lá lá lá"


3
멀티 바이트 문자를 포함하지 않으면 기술이 실패합니다. 불행히도를 포함하는 샘플 입력 문자열을 제공했습니다 á. 실패의 데모
mickmackusa

다음을 string사용하여 멀티 바이트 문자열 인지 확인할 수 있습니다.mb_strlen($subject) != strlen($subject)
RousseauAlexandre

이 게시물은 질문에 대한 답변을 시도하지 않습니다.
mickmackusa 2014

2
$str = "/property/details&id=202&test=123#tab-6p";
$position = strpos($str,"&");
echo substr_replace($str,"?",$position,1);

substr_replace를 사용하면 문자열에서 첫 번째 문자 만 바꿀 수 있습니다. &는 여러 번 반복되지만 첫 번째 위치에서만 &로 대체해야합니까?


1

이 기능은 @renocor의 답변에서 많은 영향을 받았습니다. 함수를 멀티 바이트 안전하게 만듭니다.

function str_replace_limit($search, $replace, $string, $limit)
{
    $i = 0;
    $searchLength = mb_strlen($search);

    while(($pos = mb_strpos($string, $search)) !== false && $i < $limit)
    {
        $string = mb_substr_replace($string, $replace, $pos, $searchLength);
        $i += 1;
    }

    return $string;
}

function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
{
    $string = (array)$string;
    $encoding = is_null($encoding) ? mb_internal_encoding() : $encoding;
    $length = is_null($length) ? mb_strlen($string) - $start : $length;

    $string = array_map(function($str) use ($replacement, $start, $length, $encoding){

        $begin = mb_substr($str, 0, $start, $encoding);
        $end = mb_substr($str, ($start + $length), mb_strlen($str), $encoding);

        return $begin . $replacement . $end;

    }, $string);

    return ( count($string) === 1 ) ? $string[0] : $string;
}

0

이것을 사용할 수 있습니다 :

function str_replace_once($str_pattern, $str_replacement, $string){ 

        if (strpos($string, $str_pattern) !== false){ 
            $occurrence = strpos($string, $str_pattern); 
            return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern)); 
        } 

        return $string; 
    } 

php.net 에서이 예제를 찾았습니다.

용법:

$string = "Thiz iz an examplz";
var_dump(str_replace_once('z','Z', $string)); 

산출:

ThiZ iz an examplz

이렇게하면 성능이 약간 저하 될 수 있지만 가장 쉬운 솔루션입니다.


그것이 출력이 요점보다 무엇입니까? 첫 번째 소문자 "z"만 대문자 "Z"로 바꿔야합니까? 그것들을 모두 교체하는 대신? 나는 그것이 우리가 여기서 말하는 것이라고 생각했습니다.
Swivel

내 나쁜, 그것은 첫 번째 사건 만 교체합니다. 편집했습니다.
happyhardik

이 같은 조언은 이미 거의 3 년 전에 Bas에 의해 제공되었다 (과도하게 전화하지 않음 strpos()). 페이지에 새로운 값을 추가하지 않기 때문에 하향 조정됩니다.
mickmackusa

0

문자열에 멀티 바이트 문자가 포함되어 있지 않고 하나의 문자 만 바꾸려면 간단히 사용할 수 있습니다 strpos

오류를 처리하는 함수

/**
 * Replace the first occurence of given string
 *
 * @param  string $search  a char to search in `$subject`
 * @param  string $replace a char to replace in `$subject`
 * @param  string $subject
 * @return string
 *
 * @throws InvalidArgumentException if `$search` or `$replace` are invalid or if `$subject` is a multibytes string
 */
function str_replace_first(string $search , string $replace , string $subject) : string {
    // check params
    if(strlen($replace) != 1 || strlen($search) != 1) {
        throw new InvalidArgumentException('$search & $replace must be char');
    }elseif(mb_strlen($subject) != strlen($subject)){
        throw new InvalidArgumentException('$subject is an multibytes string');
    }
    // search 
    $pos = strpos($subject, $search);
    if($pos === false) {
        // not found
        return $subject;
    }

    // replace
    $subject[$replace] = $subject;

    return $subject;
}

0

루프 솔루션

<?php
echo replaceFirstMatchedChar("&", "?", "/property/details&id=202&test=123#tab-6");

function replaceFirstMatchedChar($searchChar, $replaceChar, $str)
{
    for ($i = 0; $i < strlen($str); $i++) {

        if ($str[$i] == $searchChar) {
            $str[$i] = $replaceChar;
            break;
        }
    }
    return $str;
}

-1

여기 약간 수정 된 str_replace () 함수 를 감싸기 위해 만든 간단한 클래스가 있습니다.

우리의 php :: str_rreplace () 함수를 사용하면 문자열의 마지막 X 인스턴스 만 바꾸려고 할 때 매우 유용한 리버스의 제한된 str_replace ()를 수행 할 수 있습니다.

이 예제는 모두 preg_replace ()를 사용 합니다 .

<?php
class php {

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_replace($find, $replace, $subject, $replacement_limit = -1) {
        $find_pattern = str_replace('/', '\/', $find);
        return preg_replace('/' . $find_pattern . '/', $replace, $subject, $replacement_limit);
    }

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_rreplace($find, $replace, $subject, $replacement_limit = -1) {
        return strrev( self::str_replace(strrev($find), strrev($replace), strrev($subject), $replacement_limit) );
    }
}

귀하의 게시물은 이미 포화 된 페이지에이 값을 추가하지 않습니다. 잘못된 도구를 사용하여 바늘 끈의 문자를 이스케이프 처리하기 때문에 많은 프린지 사례에서 정규식 솔루션이 실패합니다. 실패 증명 : 3v4l.org/dTdYK 2009 년에 심하게 찬성 하고 받아 들여진 대답은 이미이 기술의 올바른 실행을 보여줍니다. 두 번째 방법은 질문에 답변하지 않았으며 oLinkWebDevelopment에서 이미 제공 한 것입니다.
mickmackusa 2019

-1
$str = "Hello there folks!"
$str_ex = explode("there, $str, 2);   //explodes $string just twice
                                      //outputs: array ("Hello ", " folks")
$str_final = implode("", $str_ex);    // glues above array together
                                      // outputs: str("Hello  folks")

추가 공간이 하나 더 있지만 내 경우에는 backgound 스크립트와 마찬가지로 중요하지 않았습니다.


이 기술은 2009 년 에 toomuchphp에 의해 제공되었습니다 ! 나는 당신의 게시물이 연구원들에게 새로운 가치를 부여하지 않기 때문에 하향 투표했습니다. 답변을 게시하기 전에 솔루션이 페이지에 고유하고 페이지에 가치를 더하는지 확인하십시오.
mickmackusa

-3

이것이 나의 첫 번째 대답입니다, 나는 그것을 올바르게하기를 바랍니다. 이 문제에 대해 str_replace 함수의 네 번째 인수를 사용하지 않는 이유는 무엇입니까?

mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

count : 통과 한 경우 수행 된 교체 횟수로 설정됩니다.


편집 : str_replace 의 네 번째 매개 변수는 대체 횟수를 지정하는 변수 이기 때문에이 답변은 잘못 되었습니다. 이것은 preg_replace 와 일치하지 않습니다. 이것은 4 번째 매개 변수 $limit와 5 번째 매개 변수가 &$count있습니다.


네 번째 인수는 str_replace ()에 의해 대체 횟수로 설정됩니다. 따라서 변수가 아닌 정수를 전달할 때 오류가 발생하는 이유입니다.
arminrosu

-6

(카운트 값을 제공하여) 첫 번째 또는 첫 번째 인스턴스 만 교체 할 솔루션을 쉽게 찾을 수 있습니다. 마지막 또는 마지막 몇 인스턴스를 대체 할 솔루션이 많지 않습니다.

str_replace ($ find, $ replace, $ subject, -3)와 같은 것이 마지막 세 인스턴스를 대체해야 할 수도 있습니다.

어쨌든, 단지 제안.


4
2 년 전에 답변이 수락되었을 때 제안으로 질문에 답변하는 이유는 무엇입니까?!
mbinette

또한 -3은 매개 변수로 작동하지 않습니다. 4 번째 매개 변수가 출력되고 입력 매개 변수가 아니기 때문입니다. 충돌하는 코드를 게시하는 대신 제안한 내용을 테스트하는 것이 좋습니다.
Ghostwriter78

예, 그러나 이것은 잘못된 것입니다. 왜 시도 할 때 빈 화면 충돌이 발생합니까? 나는 보통 error_reporting (E_ALL); ini_set ( "display_errors", 1); 여전히 빈 화면 오류입니다.
더그 캐시디
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.