Str_replace'ı yalnızca ilk maçta etkili olacak şekilde mi kullanıyorsunuz?


Yanıtlar:


346

Preg_replace ile yapılabilir :

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'

Sihir isteğe bağlı dördüncü parametrede [Limit]. Belgelerden:

[Limit] - Her bir konu dizesindeki her bir desen için mümkün olan maksimum yedek. Varsayılan değer -1'dir (sınır yoktur).


Gerçi, bkz zombat cevabını daha verimli bir yöntem için (kabaca, 3-4x daha hızlı).


39
Bu yöntemin dezavantajı, düzenli ifadelerin performans cezasıdır.
zombat

27
Başka bir dezavantajı "iğne" üzerinde preg_quote () kullanmak ve yerine $ ve \ meta karakter kaçmak zorunda olmasıdır.
Josh Davis

32
Bu kötü kaçış sorunları nedeniyle genel bir çözüm olarak başarısız.
Jeremy Kauffman

2
Çoğu zaman 'performans' nedeniyle düzenli ifadeler reddedilir, eğer performans birincil endişe olsaydı, PHP yazmazdık! Deseni sarmak için '/' dışında bir şey, belki de '~', kaçan problemi bir dereceye kadar önlemeye yardımcı olabilir. Verilerin ne olduğuna ve nereden geldiğine bağlıdır.
ThomasRedstone

1
Performans olumsuz yanı - kaçış sorunlarından şikayet edenler, potansiyel hataların yanı sıra akılda belirli bir şey var preg_quotemı? Örneğin, @ThomasRedstone, sınırlayıcının /göründüğünde tehlikeli olabileceğinden endişe eder $from, ancak neyse ki öyle değildir: preg_quoteikinci parametresi nedeniyle düzgün bir şekilde kaçar (biri kolayca test edebilir). Belirli konuları (kitabımda ciddi PCRE güvenlik hataları olurdu) duymak isterim.
MvanGeest

611

Bunun bir sürümü yok, ancak çözüm hiç de aceleci değil.

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

Oldukça kolay ve düzenli ifadelerin performans cezasını kaydeder.


Bonus: değiştirmek istiyorsanız son geçtiği, sadece kullanmak strrposyerine strpos.


17
Çok daha hızlı olabilir ve normal ifadelerden daha az bellek kullanır. Birinin bunu neden oy kullanacağına dair hiçbir fikrim yok ...
Josh Davis

12
Bu yaklaşım gibi, ama kod bir hata var, substr_replace çağrısının son parametresi strlen ($ replace) yerine strlen ($ iğne) olmalıdır .. lütfen bu konuda dikkatli olun!
Nelson

Neler olup bittiğini anlamanın çok daha fazla zaman alması anlamında "hacky" dir. Ayrıca, açık bir kod olsaydı, kodda bir hata olduğu söylenemezdi. Böyle küçük bir pasajda bir hata yapmak mümkünse, zaten çok acayip.
Camilo Martin

9
Satır sayısı ve hata olasılığı konusunda @CamiloMartin ile aynı fikirde değilim. substr_replaceTüm parametreler nedeniyle kullanmak için biraz hantal bir işlev olsa da, asıl mesele, sayılarla dize manipülasyonunun bazen zor olması - işlevlere doğru değişkeni / ofseti geçirmeye dikkat etmeniz gerekir. Aslında yukarıdaki kodun en basit ve bana göre mantıklı bir yaklaşım olduğunu söyleyecek kadar ileri giderdim.
Alex

1
Mükemmel yaklaşım. İçlerinde regex karakterleri ayrılmış değişken değerleri değiştirirken mükemmel çalışır (yani preg_replace ayıdır). Bu basit ve zariftir.
Praesagus

96

Düzenleme: her iki yanıt da güncellendi ve artık doğru. İşlev zamanlamaları hala faydalı olduğundan cevabı bırakacağım.

'Zombat' ve 'çok fazla php' nin cevapları maalesef doğru değil. Bu (bir yorum göndermek için yeterli itibara sahip değilim) yayınlanan cevap zombat bir revizyon olduğunu:

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

Strlen ($ replace) yerine steni ($ iğne) not edin. Zombat örneği yalnızca iğne ve değiştirme aynı uzunlukta olduğunda doğru şekilde çalışacaktır.

İşte PHP'nin kendi str_replace işleviyle aynı imzalı bir işlevde aynı işlevsellik:

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

Bu 'çok fazla php' nin gözden geçirilmiş cevabıdır:

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

Sonunda 1 yerine 2 değerini not edin veya işlev biçiminde:

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

İki işlevi zamanladım ve hiçbir eşleşme bulunmadığında birincisi iki kat daha hızlı. Bir eşleşme bulunduğunda aynı hızdadırlar.


Neden bu tür genelleştirme: str_replace_f Flexible (karışık $ s, karışık $ r, int $ ofset, int $ limit), burada fonksiyon $ ofset (nth) eşleşmesinden başlayarak $ limit oluşumlarının yerini alır.
Adam Friedman

Çok kötü, bu yalnızca büyük / küçük harfe duyarlı değiştirme işlemleri için geçerlidir.
andreszs

4
@Andrew stripos()to the rescue :-)
Gras Double

76

Hangisinin en hızlı olduğunu merak ettim, bu yüzden hepsini test ettim.

Aşağıda bulacaksınız:

  • Bu sayfaya katkıda bulunan tüm işlevlerin kapsamlı bir listesi
  • Her bir katkı için kıyaslama testi (10.000 çalışma üzerinde ortalama yürütme süresi)
  • Her cevaba bağlantılar (tam kod için)

Tüm işlevler aynı ayarlarla test edilmiştir:

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

Bir dize içindeki bir dizenin yalnızca ilk oluşumunu değiştiren işlevler :


Bir dize içinde yalnızca bir dizenin son tekrarlamasını değiştiren işlevler :


Bunun için teşekkürler, genellikle çoğu zaman gelecekteki tweak gerekiyorsa en esnek olduğu için preg_replace kullanıyorum% 27 daha yavaş önemli olmayacak
zzapper

@oLinkWebDevelopment Benchmark betiğinizi görmek isterim. Bence faydalı olabilir.
Dave Morton

Sonucu substr_replace()kazanmasının nedeni basittir; çünkü bu dahili bir işlevdir. Aynı şeyi yapan iki dahili ve kullanıcı tanımlı işlev performans açısından farklılık gösterir, çünkü dahili işlev alt katmanlarda çalışır. Neden olmasın preg_match()? Düzenli ifadeler, bir dizede birden çok kez arama yaptıkları için her dahili dize düzenleme işlevinden neredeyse daha yavaştır.
MAChitgarha

1
Umarım "kazananı" ( substr_replace($string, $replace, 0, strlen($search));) üzerindeki kriter sadece statik yazmaz 0. Normal olmayan çözümlerin evrişiminin bir kısmı, nerede değiştirileceğini bilmeden önce başlangıç ​​noktasını "bulmaları" gerektiğidir.
mickmackusa

55

Ne yazık ki, bunu yapabilen herhangi bir PHP işlevi bilmiyorum.
Kendinizi bu şekilde kolayca yuvarlayabilirsiniz:

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

Bence bu hepsinin en golfçü versiyonu - joinyerine kullanıyor implode.
Titus

return implode($replace, explode($find, $subject, $limit+1));özel numaraları değiştirmek için
beppe9000

7

Regexp gerek kalmadan, dize (büyük / küçük harf duyarlı) dize sınır ile değiştirir bu küçük işlevi oluşturdu . İyi çalışıyor.

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

Örnek kullanım:

$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

Daha açık olmak yerine yapmayı ===falsetercih is_bool(etsem de - sadece RegExp deliliğinden kaçındığı için bu başparmakları bırakıyorum ! ... ve aynı zamanda çalışıyor ve temiz bir çözüm ...
jave.web

Kolayca özelleştirilebilir bir preg_çözümü tercih etmek delilik değil, kişisel bir tercihtir. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);regex'ten korkmayan insanlar için okunması oldukça basittir. Büyük / küçük harfe duyarlı olmayan aramaya mı ihtiyacınız var? iSon desen sınırlayıcıdan sonra ekleyin . Unicode / multibyte desteğine mi ihtiyacınız var? uSon desen sınırlayıcıdan sonra ekleyin . Sözcük sınırı desteğine mi ihtiyacınız var? Ekle \bArama dizisi her iki tarafında. Normal ifade istemiyorsanız normal ifade kullanmayın. Kurslar için atlar, ama kesinlikle delilik değil.
mickmackusa

3

En kolay yol düzenli ifadeyi kullanmak olacaktır.

Diğer yol, strpos () ve sonra bir substr_replace () ile dizenin konumunu bulmaktır

Ama gerçekten RegExp için gitmek istiyorum.


Bu "ipucu", bu sayfadaki diğer yayınlara kıyasla oldukça belirsiz / düşük değerlidir.
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));
}

Sadece kod yanıtları StackOverflow'da düşük değerlidir, çünkü gelecekteki binlerce araştırmacıyı eğitmek / güçlendirmek için zayıf bir iş çıkarmaktadırlar.
mickmackusa

3

=> KOD REVİZE EDİLDİ, bu yüzden bazı yorumları çok eski düşünün

Ve herkese bunu geliştirmeme yardım ettiği için teşekkürler

Herhangi bir hata, lütfen bana ulaşın; Bunu hemen tamir edeceğim

Öyleyse, gidelim:

İlk 'o' yerine 'ea' yerine örneğin:

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

//output: I leave you

İşlev:

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

Bu başarısız olursa $ $ aaa vs aaaaaaaaa gibi karakterleri tekrarladı
Cristo

Bence öyle olmalı substr($where,$b+strlen($this)), değil substr($where,$b+1). Ve sanırım bu substr_replacedaha hızlı.
Titus

Kod revize edildi, şimdi uzun dizeler için bile çalışıyor
PYK

Bu çözüm kodlanmış olarak çalışmaz. İspat: 3v4l.org/cMeZj Değişken adlandırma sorununu çözdüğünüzde , arama değeri bulunmadığında çalışmaz - giriş dizesine zarar verir. Kanıt: 3v4l.org/XHtfc
mickmackusa

Birisi kodu DÜZELTME isterse adil mi? @mickmackusa Tekrar kontrol edebilir misiniz lütfen?
PYK

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

Bu sadece ilk cevapla aynı. Ayrıca, sen yapmalıyım preg_quoteait $findbir ifadesi olarak kullanmadan önce.
Emil Vikström

kullandığım bu, bu yüzden oy verdim. İlk cevap Drupal ile çatışmaya neden oldu, bir drupal yardımcı fonksiyonunun üzerine yazmış olmalı. Bu yüzden sadece fonksiyonun içindeki kodu aldım ve kodun geri kalanıyla birlikte kullandım ...
Dan Mantyla

Bu kod-tek cevap sayfadaki gereksiz tavsiyelerde bulunur (Burada eksik saymıyorum preg_quote()Bu geç yinelenen cevap güvenle sayfasından tasfiye edilebilir onun tavsiyesi önceki sağladığı ve daha yüksek upvoted kabul cevap çünkü..
mickmackusa

2

@ Renocor'un cevabını genişletmek için ,% 100 geriye dönük uyumlu bir fonksiyon yazdım str_replace(). Yani yerine, olan bütün tekrarlarını str_replace()ile str_replace_limit(), kullanıcıların hiçbir şey bozmadan hatta kullanarak dizileri $search, $replaceve / veya $subject.

İşlev çağrısını değiştirmek istiyorsanız işlev tamamen bağımsız olabilir($string===strval(intval(strval($string)))) , ancak valid_integer()dize olarak sağlanan tamsayılarla uğraşırken oldukça kullanışlı bir işlev olduğu için buna karşı öneriyorum .

Not: Mümkün olduğunda, str_replace_limit()kullanacağı str_replace()tüm çağrılar için çok yerine str_replace()değiştirilebilir str_replace_limit()performans isabet endişesi duymadan.

kullanım

<?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 değiştirme - bbcbbc

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

1 yedek - bbcabc

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

2 değiştirme - bbcbbc

fonksiyon

<?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
Bana sorarsan biraz şişkin. Ayrıca bu çözümde en çok nefret ettiğim şey hata işleme. Yanlış değerler iletirseniz komut dosyasını kırar. Profesyonel göründüğünü düşünüyorsunuz, ancak bir hata yerine bir uyarı veya uyarı üretmiyor. Daha iyisi, saçmalığı atlamak, bunun yerine yanlış döndürmek veya null döndürmek ve böyle bir işlevde asla bir geri iz kullanmamaktır. En iyi çözüm, programcının çıktı yanlış / beklenmedik olduğunda ne yapılacağına karar verebilmesidir.
Codebeat

Bu kullanımlar @Erwinus E_USER_WARNINGbir olduğunu boyunca, uyarı , değil bir hata . Geri izleme, hangi verilerin geçersiz veriyi işleve ilk etapta geçirdiğini bulmak için son derece yararlıdır (üretimdeki hataları izlemek için kesinlikle gereklidir). / / Bir hata atmak $subjectyerine geri dönmeye gelince , bu benim kullanım durumum için kişisel bir seçim oldu. İşlevselliği eşleştirmek için , catchable ölümcül hatalarını kullanmak en iyi seçenektir ( ilk iki argüman için bir kapatma sağlarken olduğu gibi ). falsenullstr_replace()str_replace()
0b10011

Ah, kullandığınız E_USER_WARNING hakkında fark etmediniz, bunun için üzgünüm. Konuyu döndürmeyle ilgili sorun, işlevin dışında yanlış bir şey olduğunu asla görememenizdir. Bununla birlikte, daha akıllı yaparsanız işlev yarı boyutta olabilir (mümkündür). İkincisi, karmaşık bir şeyi açıkladığında yorumlar iyidir, ancak bir değeri artırmak gibi basit şeyler için çok yararlı değildir. Genel olarak gereksiz büyük olduğunu düşünüyorum. Ayrıca, bu kodu varsayılan olarak çalışma zamanı iletilerini (günlükler) bastırmayan bir sunucuda kullandığınızda uyarıları üretim ortamında kullanmak güvenlik sorunu olabilir.
Codebeat

@Erwinus Yorumlar söz konusu olduğunda çok titizdim çünkü bazı insanlar dili ve diğerlerini anlamıyorlar ve yorumlar onu anlayanlar tarafından her zaman kaldırılabilir. Tüm uç durumlar için aynı sonucu elde etmenin daha iyi bir yolunu biliyorsanız, yanıtı düzenleyin. Ve üretim ortamınız hata mesajlarını bastırmazsa, bu fonksiyondan daha büyük bir sorununuz var;)
0b10011 17:13

TL; DR Bu snippet o kadar şişirilmiş ki, bir regex işlevi üzerinde seçmeyi hayal edemiyorum (kaydırmaktan nefret ediyorum). Yapılan değiştirmeleri saymak istiyorsanız, bunun için bir parametre vardır preg_replace(). Ayrıca, preg_replace()/ regex sözcük sınır işleme (istenirse) sunar - normal olmayan işlevlerin zarif bir şekilde sağlamayacağı bir şey.
mickmackusa

2

Test sonucuma göre, karim79 tarafından sağlanan normal_ifade'ye oy vermek istiyorum. (Şimdi oy kullanmak için yeterli itibarım yok!)

Zombat'ın çözümü çok fazla işlev çağrısı kullanıyor, hatta kodları basitleştiriyorum. Her iki çözümü de 100.000 kez çalıştırmak için PHP 5.4 kullanıyorum ve işte sonuç:

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

==> 1,85 saniye

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

==> 1,35 sn

Gördüğün gibi. Preg_replace'ın performansı birçok insanın düşündüğü kadar kötü değil. Bu yüzden düzenli ifadeniz karmaşık değilse şık bir çözüm öneririm.


İlk snippet'iniz haksız bir karşılaştırmadır çünkü doğru bir uygulama kullanamaz. Sen kontrol etmiyor $posiçin false, bu nedenle iğne samanlıkta yok, bunun çıkışını zarar verir.
mickmackusa

Teşekkürler mickmackusa, haklısın. Ama mesele bu değil. Bu kodun sadece uygulamaların verimliliğini karşılaştırmak için basitleştirildiğini söyledim.
Hunter Wu

Bu kesinlikle benim isaret ettigim sey. Kesinlikle aynı işlemi yapmayan kıyaslama karşılaştırmaları yapmamalısınız. Elmaları yarım portakallarla karşılaştırmak yararlı değildir. Regex olmayan eksiksiz yaklaşımın tam olarak uygulanması, hız farkını daha derinleştirecektir.
mickmackusa

Tekrar teşekkürler. Ama istediğim daha iyi bir uygulama bulmak, daha fazla fark yaratmak değil.
Hunter Wu

2

Zombat'ın cevabını genişletmek için (ki en iyi cevap olduğuna inanıyorum), işlevinin yinelemeli bir sürümünü, $limitkaç kez tekrarlamak istediğinizi belirtmek için bir parametre alır .

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

Not üzerinde hiçbir kalite kontrolü yoktur $start_poso dize uzunluğu ötesinde eğer öyleyse, bu fonksiyon oluşturur: Warning: strpos(): Offset not contained in string.... Bu işlev $start_pos, uzunluğun ötesinde bir yedek yapamaz . Hata Kanıtı: 3v4l.org/qGuVIR ... Fonksiyonunuz return $haystackkoşulları birleştirebilir ve bu tür tek kullanımlık değişkenleri beyan etmekten kaçınabilir: 3v4l.org/Kdmqp Ancak, bu sayfanın başka yerlerindeki yorumlarda söylediğim gibi, çok temiz, doğrudan, özyinelemesiz bir preg_replace()çağrı kullanın.
mickmackusa

evet bu yüzden bu hat ekleyebileceğiniz elsestatment$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

Bir dize için

$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

Tek bir karakter için

$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

Arama dizesi giriş dizesinin 0 ofsetinde olmadığında ilk substr_replace () snippet'i başarısız olur. Hata kanıtı: 3v4l.org/oIbRv Ve her iki substr_replace()teknik de arama değeri mevcut olmadığında giriş dizesine zarar verir. Başarısızlık kanıtı: 3v4l.org/HmEml (Ve tüm revçağrılarla son teknik ciddi şekilde kıvrık / göze sert geliyor.)
mickmackusa

2

İnsanların söylediklerini tamamlayan dizenin tamamının bir dizi olduğunu unutmayın:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"


3
Çok baytlı karakterler içermediği sürece ... ve sonra tekniğiniz başarısız olur. Ne kadar talihsiz bir örnek girdi dizesi teklif etti á. Başarısızlık gösteri
mickmackusa

stringKullanarak çok baytlı bir dize olup olmadığını doğrulayabilirsinizmb_strlen($subject) != strlen($subject)
RousseauAlexandre

Bu yazı, sorulan soruya cevap vermeye çalışmaz.
mickmackusa

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

Substr_replace kullanarak ilk karakterin oluşumunu yalnızca dizede değiştirebiliriz. olarak & birden çok kez tekrarlanır, ancak sadece ilk konumda & yerine?


1

Bu işlev, @renocor'un cevabından büyük ölçüde ilham alıyor. Fonksiyonu çok baytlı güvenli hale getirir.

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

Bunu kullanabilirsiniz:

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

Bu örneği php.net adresinden buldum

Kullanımı:

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

Çıktı:

ThiZ iz an examplz

Bu, performansı biraz azaltabilir, ancak en kolay çözümdür.


Eğer çıktı ne ise bu nokta ne? Sadece ilk küçük "z" harfini büyük "Z" harfiyle değiştirmemeli midir? Hepsini değiştirmek yerine? Burada bahsettiğimiz şeyin bu olduğunu düşündüm ...
döner

Benim hatam, sadece ilk oluşumun yerini alacak. Düzenlenen.
happyhardik

Aynı tavsiye Bas tarafından yaklaşık 3 yıl önce (ve aşırı çağrı olmadan strpos()) zaten sunuldu . Sayfaya yeni bir değer katmadığı için indirildi.
mickmackusa

0

Dizeniz herhangi bir çok baytlı karakter içermiyorsa ve yalnızca bir karakter değiştirmek istiyorsanız, strpos

İşte hataları işleyen bir işlev

/**
 * 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

Döngü Çözümü için

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

İşte biraz değiştirilmiş str_replace () ' ı sarmak için oluşturduğum basit bir sınıf fonksiyonlarımızı .

Bizim php :: str_rreplace () fonksiyonumuz, bir dizenin yalnızca son X örneğini değiştirmeye çalışırken çok kullanışlı olabilecek bir ters, sınırlı str_replace () gerçekleştirmenize de izin verir.

Bu örneklerin her ikisi de preg_replace () yöntemini kullanır .

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

Yayınınız, zaten doymuş olan bu sayfaya değer katmıyor. Regex çözümünüz, iğne dizesindeki karakterlerden kaçmak için yanlış aracı kullandığınız için birçok saçak durumunda başarısız oluyor. Başarısızlık Kanıtı: 3v4l.org/dTdYK ağır upvoted ve gelen kabul cevap 2009 zaten bu tekniğin uygun yürütülmesini gösterir. İkinci yönteminiz oLinkWebDevelopment tarafından sorulan ve zaten verilen soruya cevap vermiyor.
mickmackusa

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

Bir ek alan daha var ama benim durumumda artalan komut dosyası için olduğu gibi önemli değildi.


Bu teknik 2009 yılında toomuchphp tarafından sağlandı ! Yazınızı araştırmacılara yeni bir değer katmadığı için indirdim. Lütfen yanıt göndermeden önce çözümünüzün sayfaya özgü olduğundan ve sayfaya değer kattığından emin olun.
mickmackusa

-3

bu benim ilk cevabım, umarım doğru yapmayı umarım. Neden bu sorun için str_replace işlevinin dördüncü bağımsız değişkenini kullanmıyorsunuz?

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

count: Geçilirse, gerçekleştirilen değiştirme sayısına ayarlanır.


edit: Bu cevap yanlış, çünkü str_replace 4 parametresi yapılan değiştirme sayısı atanan bir değişkendir. Bu, 4. parametreye ve 5. parametreye sahip preg_replace ile tutarsız .$limit&$count


Dördüncü argümanlar str_replace () tarafından yapılan değişikliklerin sayısına ayarlanır. Bu nedenle, değişken değil tamsayıyı ilettiğinizde hata alırsınız.
arminrosu

-6

Yalnızca ilk veya ilk birkaç örneği değiştirmek için bir çözüm bulmak kolaydır (sayı değerini vererek). Örneğin son veya son çiftini değiştirmek için çok fazla çözüm yoktur.

Belki str_replace ($ find, $ replace, $ Subject, -3) gibi bir şey son üç örneğin yerine geçmelidir.

Her neyse, sadece bir öneri.


4
İki yıl önce bir cevap kabul edildiğinde neden bir soruyu bir öneriyle cevaplıyoruz ?!
mbinette

Ayrıca, parametre olarak çalışmaz, çünkü 4. parametre giriş parametresi değil, çıktıdır. Kilitlenen kodu göndermek yerine teklifinizi test ederseniz daha iyi olur.
Ghostwriter78

Evet, bu yanlış, ancak denediğimde neden boş bir ekran çökmesi alıyorum? Her zamanki error_reporting (E_ALL) yaptım; ini_set ("display_errors", 1); Hala boş ekran hatası.
Doug Cassidy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.