Usando str_replace para que ele atue apenas na primeira partida?

Respostas:

346

Pode ser feito com 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'

A mágica está no quarto parâmetro opcional [Limite]. A partir da documentação:

[Limite] - O número máximo de substituições possíveis para cada padrão em cada sequência de assunto. O padrão é -1 (sem limite).


Porém, veja a resposta do zombat para um método mais eficiente (aproximadamente, 3-4x mais rápido).

karim79
fonte
39
A desvantagem desse método é a penalidade de desempenho de expressões regulares.
zombat 10/08/09
27
Outra desvantagem é que você precisa usar preg_quote () na "agulha" e escapar dos meta-caracteres $ e \ na substituição.
21339 Josh Davis
32
Isso falha como uma solução genérica devido a problemas de escape desagradáveis.
Jeremy Kauffman
2
Com muita freqüência, expressões regulares são descartadas devido ao 'desempenho', se o desempenho fosse a principal preocupação, não estaríamos escrevendo PHP! Algo diferente de '/' poderia ser usado para quebrar o padrão, talvez '~', o que ajudaria a evitar o problema de fuga em algum grau. Depende do que são os dados e de onde vieram.
ThomasRedstone
1
Desvantagens do desempenho à parte - aqueles que se queixam de problemas de escape têm algo em mente, além de possíveis bugs preg_quote? Por exemplo, @ThomasRedstone teme que o delimitador /possa ser perigoso se aparecer $from, mas, felizmente, não é: ele escapou adequadamente por causa do preg_quotesegundo parâmetro do parâmetro (pode-se testá-lo facilmente). Eu gostaria de saber sobre problemas específicos (que seriam graves erros de segurança do PCRE no meu livro).
MvanGeest 31/01
611

Não há versão, mas a solução não é hacky.

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

Muito fácil e salva a penalidade de desempenho de expressões regulares.


Bônus: se você deseja substituir a última ocorrência, use apenas strrposno lugar de strpos.

zombat
fonte
17
Pode ser muito mais rápido e usará menos memória que as expressões regulares. Não faço ideia por que alguém iria votar que para baixo ...
Josh Davis
12
Eu gosto dessa abordagem, mas o código tem um erro, o último parâmetro da chamada substr_replace deve ser strlen ($ needle) em vez de strlen ($ replace) .. por favor, tenha cuidado com isso !!
Nelson
É "hacky" no sentido de que leva muito mais tempo para descobrir o que está acontecendo. Além disso, se fosse um código claro, não teria sido mencionado que o código tem um erro. Se é possível cometer um erro em um trecho tão pequeno, já é muito hacky.
Camilo Martin
9
Não concordo com @CamiloMartin no que diz respeito ao número de linhas versus a possibilidade de erros. Embora substr_replaceseja uma função um tanto difícil de usar devido a todos os parâmetros, o problema real é que a manipulação de seqüências de caracteres por números às vezes é complicada - é preciso ter cuidado para passar a variável / deslocamento correta para as funções. Na verdade, eu chegaria ao ponto de dizer que o código acima é a abordagem mais direta e, para mim, lógica.
Alex
1
Abordagem brilhante. Funciona perfeitamente ao substituir valores de variáveis ​​que reservaram caracteres regex neles (portanto, preg_replace é bear). Isso é direto e elegante.
Praesagus 07/11
96

Editar: as duas respostas foram atualizadas e agora estão corretas. Deixarei a resposta, pois os tempos das funções ainda são úteis.

Infelizmente, as respostas de 'zombat' e 'demais php' não estão corretas. Esta é uma revisão da resposta que o zombat postou (como eu não tenho reputação suficiente para postar um comentário):

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

Observe o strlen ($ agulha), em vez de strlen ($ replace). O exemplo do Zombat só funcionará corretamente se a agulha e a substituição tiverem o mesmo comprimento.

Aqui está a mesma funcionalidade em uma função com a mesma assinatura que o próprio str_replace do PHP:

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

Esta é a resposta revisada de 'muito php':

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

Observe os 2 no final em vez de 1. Ou no formato da função:

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

Cronometrei as duas funções e a primeira é duas vezes mais rápida quando nenhuma correspondência é encontrada. Eles têm a mesma velocidade quando uma correspondência é encontrada.

Bas
fonte
Por que não generalizar isso como: str_replace_flexible ($ s misturado, $ r misturado, int $ deslocamento, int $ limite) em que a função substitui as ocorrências de $ limit começando na correspondência $ offset (enésima).
Adam Friedman
Pena que isso se aplica apenas a substituições que diferenciam maiúsculas de minúsculas.
andreszs
4
@Andrew stripos()to the rescue :-)
Gras Double
76

Eu me perguntava qual era o mais rápido, então testei todos eles.

Abaixo você encontrará:

  • Uma lista abrangente de todas as funções que foram contribuídas para esta página
  • Teste de benchmark para cada contrubuição (tempo médio de execução superior a 10.000 execuções)
  • Links para cada resposta (para o código completo)

Todas as funções foram testadas com as mesmas configurações:

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

Funções que substituem apenas a primeira ocorrência de uma sequência dentro de uma sequência:


Funções que substituem apenas a última ocorrência de uma sequência dentro de uma sequência:

oLinkWebDevelopment
fonte
Obrigado por isso, eu geralmente uso preg_replace, pois é o mais flexível, se futuro ajuste são necessários na maioria dos casos 27% mais lentas não vai ser significativo
ZZAPPER
@oLinkWebDevelopment Gostaria de ver seu script de benchmark. Eu acho que isso pode ser útil.
Dave Morton
A razão pela qual substr_replace()ganha o resultado é simples; porque é uma função interna. Duas funções internas e definidas pelo usuário que fazem a mesma coisa diferem no desempenho, porque a interna é executada em camadas inferiores. Então porque não preg_match()? Expressões regulares são quase mais lentas que todas as funções de manipulação interna de cadeias, devido à sua nação de pesquisar em uma cadeia várias vezes.
MAChitgarha
1
Espero que a referência no seu "vencedor" ( substr_replace($string, $replace, 0, strlen($search));) não tenha apenas escrito essa estática 0. Parte da convolução das soluções que não são regex é que elas precisam "encontrar" o ponto de partida antes de saber onde substituir.
Mckmackusa
55

Infelizmente, não conheço nenhuma função PHP que possa fazer isso.
Você pode rolar seus próprios facilmente assim:

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));
}
muito php
fonte
Eu acho que esta é a versão mais divertida de todas - usando em joinvez de implode.
Titus
return implode($replace, explode($find, $subject, $limit+1));para números de substituição personalizados
beppe9000
7

Eu criei essa pequena função que substitui string em string (diferencia maiúsculas de minúsculas) por limit, sem a necessidade de Regexp. Funciona bem.

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

Exemplo de uso:

$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
Parziphal
fonte
Embora eu prefira, em ===falsevez de is_bool(ser mais explícito - estou desistindo, simplesmente porque evitou a loucura do RegExp ! ... e, ao mesmo tempo que está a trabalhar e limpa solução ...
jave.web
Preferir uma preg_solução facilmente personalizável não é loucura, mas uma preferência pessoal. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);é muito simples de ler para pessoas que não temem regex. Precisa de pesquisa que não diferencia maiúsculas de minúsculas? Adicione iapós o delimitador de padrão final. Precisa de suporte unicode / multibyte? Adicione uapós o delimitador de padrão final. Precisa de suporte para limites de palavras? Adicione \bnos dois lados da sua sequência de pesquisa. Se você não deseja regex, não use regex. Cavalos para cursos, mas certamente não loucura.
Mckmackusa
3

A maneira mais fácil seria usar a expressão regular.

A outra maneira é encontrar a posição da string com strpos () e, em seguida, um substr_replace ()

Mas eu realmente iria para o RegExp.

Rufinus
fonte
Essa "dica" é bastante vaga / de baixo valor em comparação com outras postagens nesta página.
Mckmackusa
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));
}
luchaninov
fonte
As respostas somente de código são de baixo valor no StackOverflow, porque fazem um trabalho ruim de educar / capacitar milhares de futuros pesquisadores.
Mckmackusa
3

=> O CÓDIGO FOI REVISADO, portanto, considere alguns comentários muito antigos

E obrigado a todos por me ajudarem a melhorar isso

Qualquer erro, por favor me comunique; Eu vou consertar isso logo depois

Então, vamos para:

Substituindo o primeiro 'o' por 'ea', por exemplo:

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

//output: I leave you

A função:

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));
         }
PYK
fonte
Falha se $ this tem repetido caracteres como aaa vs aaaaaaaaa
Cristo
Eu acho que deveria ser substr($where,$b+strlen($this)), não substr($where,$b+1). E acho que substr_replaceé mais rápido.
Titus
Código foi revisado, agora ele funciona mesmo para longas seqüências
PYK
Esta solução não funciona como codificada. Prova: 3v4l.org/cMeZj E quando você corrige o problema de nomeação de variáveis, ele não funciona quando o valor da pesquisa não é encontrado - danifica a sequência de entrada. Prova: 3v4l.org/XHtfc
mickmackusa
É justo alguém pedir para corrigir o código? @mickmackusa Você pode verificar isso de novo, por favor?
PYK
2
$string = 'this is my world, not my world';
$find = 'world';
$replace = 'farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;
zack
fonte
É exatamente o mesmo que a primeira resposta. Além disso, você deve fazer uma preg_quotede $findantes de usá-lo como uma expressão.
Emil Vikström
isso é o que eu usei, então votei para cima. A primeira resposta causou um conflito com o Drupal, ele deve ter substituído a função auxiliar do drupal. Então, eu só tomou o código que estava dentro da função e usou-o em linha com o resto do código ...
Dan Mantyla
Esta resposta somente de código fornece conselhos redundantes na página (para não mencionar que está faltando preg_quote(). Esta resposta duplicada tardia pode ser removida com segurança da página porque seu conselho é fornecido pela resposta aceita anterior e com maior voto positivo.
mickmackusa
2

Para expandir a resposta do @ renocor , escrevi uma função 100% compatível com versões anteriores str_replace(). Ou seja, você pode substituir todas as ocorrências de str_replace()com str_replace_limit()sem mexer qualquer coisa, até mesmo aqueles que utilizam matrizes para o $search, $replacee / ou$subject .

A função pode ser completamente independente, se você quiser substituir a chamada de função ($string===strval(intval(strval($string)))), mas eu recomendo que ela valid_integer()seja uma função bastante útil ao lidar com números inteiros fornecidos como seqüências de caracteres.

Nota: Sempre que possível, str_replace_limit()será usado str_replace()para que todas as chamadas para str_replace()possam ser substituídas str_replace_limit()sem se preocupar com um impacto no desempenho.

Uso

<?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 substituições - bbcbbc

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

1 substituições - bbcabc

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

2 substituições - bbcbbc

Função

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

    }

}
0b10011
fonte
4
meio inchado se você me perguntar. Além disso, o que eu mais odeio nessa solução é o tratamento de erros. Ele quebra o script se você passar valores incorretos. Você acha que parece profissional, mas não é, em vez de um erro produzir um aviso ou aviso. Melhor é ignorar as besteiras, retornar falso ou nulo e nunca usar um backtrace em uma função como esta. A melhor solução é que o programador possa decidir o que fazer quando a saída estiver errada / inesperada.
Codebeat
@ Erwinus Isso usa o tempo E_USER_WARNINGtodo, o que é um aviso , não um erro . O backtrace é extremamente útil para descobrir qual código está passando os dados inválidos para a função em primeiro lugar (o que é absolutamente necessário para rastrear erros na produção). Quanto a retornar em $subjectvez de false/ nullou gerar um erro, essa foi simplesmente uma escolha pessoal para o meu caso de uso. Para combinar com str_replace()a funcionalidade, usar erros fatais capturáveis ​​seria a melhor aposta (como str_replace()acontece ao fornecer um encerramento para os dois primeiros argumentos).
0101001
Ah, não percebi o E_USER_WARNING que você está usando, desculpe por isso. O problema de devolver o assunto é que você nunca pode ver que havia algo errado fora da função. Dito isto, a função pode ter metade do tamanho, se você o fizer de maneira mais inteligente (é possível). Segundo, os comentários são bons quando explicam algo complexo, mas não muito útil para coisas simples como aumentar um valor. No geral, eu acho que é desnecessário enorme. Além disso, o uso de avisos em um ambiente de produção pode ser um problema de segurança quando você usa esse código em um servidor que não suprime mensagens em tempo de execução por padrão (logs).
Codebeat
@ Erwinus Eu era detalhado quando se tratava dos comentários, porque algumas pessoas não entendem o idioma tão bem quanto outros, e os comentários sempre podem ser removidos por aqueles que o entendem. Se você souber uma maneira melhor de obter o mesmo resultado final para todos os casos extremos, edite a resposta. E se o seu ambiente de produção não suprimir as mensagens de erro, você terá um problema maior do que esta função;)
0b10011 17/07/2013
TL; DR Esse trecho é tão inchado que não consigo imaginar escolhê-lo em uma função regex (eu odeio rolagem). Se você quiser contar as substituições feitas, há um parâmetro para isso em preg_replace(). Além disso, preg_replace()/ regex oferece manipulação de limites de palavras (se desejável) - algo que as funções que não sejam de regex não fornecerão elegantemente.
Mckmackusa
2

De acordo com o resultado do meu teste, gostaria de votar no regular_express fornecido por karim79. (Não tenho reputação suficiente para votar agora!)

A solução do zombat usa muitas chamadas de funções, até simplifico os códigos. Estou usando o PHP 5.4 para executar as duas soluções 100.000 vezes, e aqui está o resultado:

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

==> 1,85 s

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

==> 1,35 s

Como você pode ver. O desempenho do preg_replace não é tão ruim quanto muitas pessoas pensam. Então, eu sugiro a solução elegante se o seu expresso regular não for complicado.

Hunter Wu
fonte
Seu primeiro snippet é uma comparação injusta porque falha ao usar uma implementação correta. Você não está verificando $pospara false, então, quando a agulha não existe no palheiro, ele irá danificar a saída.
Mckmackusa
Obrigado @mickmackusa, você está certo. Mas esse não é o ponto. Eu disse que esse código é simplificado apenas para comparar a eficiência das implementações.
Hunter Wu
Esse é exatamente o meu ponto. Você nunca deve fazer comparações de benchmark que não executam exatamente o mesmo processo. Comparar maçãs com meias laranjas não é útil. A implementação completa da abordagem completa sem regex tornará a diferença de velocidade mais profunda.
Mckmackusa 23/02/19
Bem, obrigado novamente. Mas o que eu quero é encontrar a melhor implementação, não fazer mais diferença profunda.
Hunter Wu
2

Para expandir a resposta do zombat (que acredito ser a melhor resposta), criei uma versão recursiva de sua função que usa um $limitparâmetro para especificar quantas ocorrências você deseja substituir.

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;
        }
    }
}
jtate
fonte
Note, não há nenhuma verificação de qualidade sobre $start_pos, por isso, se ele está fora do comprimento da corda, esta função irá gerar: Warning: strpos(): Offset not contained in string.... Essa função falha ao fazer uma substituição quando $start_posestá além do comprimento. Prova de falha: 3v4l.org/qGuVIR ... Sua função pode combinar as return $haystackcondições e evitar a declaração de variáveis ​​de uso único como esta: 3v4l.org/Kdmqp No entanto, como já disse em comentários em outros lugares desta página, prefiro use uma preg_replace()chamada muito limpa, direta e não recursiva .
Mckmackusa
sim para que você pode adicionar esta linha elsestatment$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A
2

Para uma string

$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

Para um único caractere

$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
oLinkWebDevelopment
fonte
O primeiro trecho substr_replace () falha quando a cadeia de pesquisa não está no deslocamento 0 da cadeia de entrada. Prova de falha: 3v4l.org/oIbRv E as duas substr_replace()técnicas danificam a sequência de entrada quando o valor da pesquisa não está presente. Prova do fracasso: 3v4l.org/HmEml (E essa última técnica com todas as revchamadas está seriamente complicada / difícil para os olhos.)
mickmackusa
2

Complementando o que as pessoas disseram, lembre-se de que toda a cadeia é uma matriz:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"

Leandro Castro
fonte
3
A menos que contenha caracteres multibyte ... e sua técnica falhe. Que pena você ter oferecido uma sequência de entrada de amostra contendo á. Demonstração de fracasso
mickmackusa
Você pode verificar se o seu stringé uma string de vários bytes usandomb_strlen($subject) != strlen($subject)
RousseauAlexandre
Esta postagem não tenta responder à pergunta que está sendo feita.
Mckmackusa
2
$str = "/property/details&id=202&test=123#tab-6p";
$position = strpos($str,"&");
echo substr_replace($str,"?",$position,1);

Usando substr_replace, podemos substituir a ocorrência do primeiro caractere apenas na string. como & é repetido várias vezes, mas somente na primeira posição temos que substituir & por?

pooja goyal
fonte
1

Esta função é fortemente inspirada na resposta de @renocor. Torna a função multi byte segura.

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;
}
blablabla
fonte
0

Você pode usar isto:

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

Encontrei este exemplo em php.net

Uso:

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

Resultado:

ThiZ iz an examplz

Isso pode reduzir um pouco o desempenho, mas a solução mais fácil.

happyhardik
fonte
Se esse é o resultado, qual é o sentido? Não deveria substituir apenas o primeiro "z" minúsculo por um "Z" maiúsculo? Em vez de substituir todos eles? Eu pensei que era o que estávamos falando aqui ...
giratória
Meu mal, isso só substituirá a primeira ocorrência. Editado.
happyhardik
Esse mesmo conselho já foi oferecido por Bas quase três anos antes (e sem ligar demais strpos()). Voto negativo porque não adiciona nenhum novo valor à página.
Mckmackusa
0

Se a sequência não contiver caracteres multibyte e se você quiser substituir apenas um caractere, poderá simplesmente usar strpos

Aqui uma função que lida com erros

/**
 * 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;
}
RousseauAlexandre
fonte
0

Solução For Loop

<?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;
}
Preto
fonte
-1

Aqui está uma classe simples que eu criei para agrupar nossas funções str_replace () ligeiramente modificadas .

Nossa função php :: str_rreplace () também permite executar um str_replace () reverso e limitado, o que pode ser muito útil ao tentar substituir apenas as instâncias X finais de uma string.

Esses exemplos usam 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) );
    }
}
Aran
fonte
Sua postagem não agrega valor a esta página já saturada. Sua solução regex falha em muitos casos adicionais porque você usou a ferramenta incorreta para escapar caracteres na cadeia de agulhas. Prova de falha: 3v4l.org/dTdYK A resposta altamente votada e aceita de 2009 já mostra a execução adequada dessa técnica. Seu segundo método não responde à pergunta e já foi fornecido pelo oLinkWebDevelopment.
Mckmackusa
-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")

Há mais um espaço adicional, mas não importava como era para o script de backgound no meu caso.

loqan
fonte
Essa técnica foi fornecida pela toomuchphp em 2009 ! Eu diminuí a votação porque sua postagem não agrega valor aos pesquisadores. Antes de postar uma resposta, verifique se sua solução é exclusiva para a página e agrega valor à página.
Mckmackusa
-3

esta é a minha primeira resposta aqui, espero fazê-lo corretamente. Por que não usar o quarto argumento da função str_replace para esse problema?

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

count: se aprovado, será definido como o número de substituições realizadas.


edit: Esta resposta está errada, porque o quarto parâmetro de str_replace é uma variável que recebe o número de substituições efetuadas. Isso é inconsistente com preg_replace , que possui um 4º parâmetro $limite um 5º parâmetro &$count.

Don_Diego
fonte
Os quartos argumentos serão definidos por str_replace () para o número de substituições que foram feitas. É por isso que você recebe um erro quando passa um número inteiro e não uma variável para ele.
Arminrosu
-6

É fácil encontrar uma solução para substituir apenas a primeira ou a primeira instância (fornecendo o valor da contagem). Não há muitas soluções para substituir a última ou a última instância.

Talvez algo como str_replace ($ find, $ replace, $ subject, -3) deva substituir as três últimas instâncias.

Enfim, apenas uma sugestão.

Hassan
fonte
4
Por que responder a uma pergunta com uma sugestão quando uma resposta foi aceita dois anos antes ?!
mbinette
Também -3 não funcionará como parâmetro, porque o quarto parâmetro é emitido e não o parâmetro de entrada. Seria melhor se você testasse o que propõe, em vez de postar um código que trava.
precisa
Sim, isso está errado, no entanto, por que ocorre uma falha na tela em branco quando tento? Eu fiz o usual error_reporting (E_ALL); ini_set ("display_errors", 1); Ainda erro de tela em branco.
Doug Cassidy