Menggunakan str_replace sehingga hanya berfungsi pada pertandingan pertama?


Jawaban:


346

Dapat dilakukan dengan 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'

Keajaiban ada di parameter opsional keempat [Batas]. Dari dokumentasi:

[Batas] - Penggantian maksimum yang mungkin untuk setiap pola dalam setiap string subjek. Default ke -1 (tanpa batas).


Padahal, lihat jawaban zombat untuk metode yang lebih efisien (kira-kira, 3-4x lebih cepat).


39
Kelemahan dari metode ini adalah penalti kinerja dari ekspresi reguler.
zombat

27
Kelemahan lain adalah Anda harus menggunakan preg_quote () pada "jarum" dan keluar dari meta-karakter $ dan \ dalam penggantian.
Josh Davis

32
Ini gagal sebagai solusi umum karena masalah pelarian yang buruk.
Jeremy Kauffman

2
Terlalu sering ekspresi reguler diberhentikan karena 'kinerja', jika kinerja adalah perhatian utama, kami tidak akan menulis PHP! Sesuatu selain '/' dapat digunakan untuk membungkus pola, mungkin '~', yang akan membantu menghindari masalah melarikan diri sampai batas tertentu. Itu tergantung apa data itu, dan dari mana asalnya.
ThomasRedstone

1
Disamping kerugian kinerja - apakah mereka yang mengeluh tentang pelolosan masalah memiliki sesuatu yang spesifik dalam pikiran, selain bug potensial di dalamnya preg_quote? Sebagai contoh, @ThomasRedstone khawatir pembatas /bisa berbahaya jika muncul $from, tetapi untungnya tidak: itu benar lolos karena preg_quoteparameter kedua (orang dapat dengan mudah mengujinya). Saya tertarik untuk mendengar tentang masalah khusus (yang akan menjadi bug keamanan PCRE serius dalam buku saya).
MvanGeest

611

Tidak ada versi itu, tetapi solusinya sama sekali tidak rusak.

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

Cukup mudah, dan menyimpan penalti performa dari ekspresi reguler.


Bonus: Jika Anda ingin mengganti kejadian terakhir , cukup gunakan strrpossebagai ganti strpos.


17
Bisa jauh lebih cepat dan akan menggunakan memori lebih sedikit daripada ekspresi reguler. Tidak tahu mengapa seseorang memilih itu ...
Josh Davis

12
Saya suka pendekatan ini, tetapi kode memiliki kesalahan, parameter terakhir panggilan substr_replace harus strlen ($ jarum), bukan strlen ($ ganti) .. harap berhati-hati tentang itu !!
Nelson

Ini "hacky" dalam arti bahwa dibutuhkan lebih banyak waktu untuk mencari tahu apa yang terjadi. Juga jika itu kode yang jelas, tidak akan disebutkan bahwa kode tersebut memiliki kesalahan. Jika mungkin membuat kesalahan dalam cuplikan sekecil itu, itu sudah terlalu gila.
Camilo Martin

9
Saya tidak setuju dengan @CamiloMartin sehubungan dengan jumlah baris vs kemungkinan kesalahan. Meskipun substr_replacefungsi yang agak sulit untuk digunakan karena semua parameter, masalah sebenarnya adalah bahwa melakukan manipulasi string oleh angka kadang-kadang sulit - Anda harus berhati-hati untuk mengirimkan variabel yang tepat / mengimbangi ke fungsi. Saya benar-benar mengatakan bahwa kode di atas adalah pendekatan yang paling mudah, dan bagi saya, logis.
Alex

1
Pendekatan yang brilian. Berfungsi dengan sempurna ketika mengganti nilai variabel yang telah menyimpan karakter regex di dalamnya (jadi preg_replace is bear). Ini mudah dan elegan.
Praesagus

96

Sunting: kedua jawaban telah diperbarui dan sekarang benar. Saya akan meninggalkan jawabannya karena timing fungsi masih berguna.

Sayangnya jawaban 'zombat' dan 'terlalu banyak php' tidak benar. Ini adalah revisi dari jawaban yang diposting zombat (karena saya tidak memiliki reputasi yang cukup untuk mengirim komentar):

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

Perhatikan strlen ($ jarum), bukan strlen ($ ganti). Contoh Zombat hanya akan bekerja dengan benar jika jarum dan penggantian memiliki panjang yang sama.

Berikut fungsi yang sama dalam suatu fungsi dengan tanda tangan yang sama dengan str_replace milik PHP sendiri:

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

Ini adalah jawaban revisi dari 'terlalu banyak php':

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

Catat 2 di bagian akhir daripada 1. Atau dalam format fungsi:

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

Saya menghitung waktu dua fungsi dan yang pertama dua kali lebih cepat ketika tidak ada kecocokan yang ditemukan. Mereka memiliki kecepatan yang sama ketika kecocokan ditemukan.


Mengapa tidak menggeneralisasi seperti ini: str_replace_flexible (campuran $ s, campuran $ r, int $ offset, batas int $) di mana fungsi menggantikan $ limit kemunculan dimulai pada pencocokan $ offset (ke-n).
Adam Friedman

Sayang sekali ini hanya berlaku untuk penggantian case-sensitive.
andreszs

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

76

Saya bertanya-tanya mana yang tercepat, jadi saya menguji semuanya.

Di bawah ini Anda akan menemukan:

  • Daftar lengkap semua fungsi yang telah dikontribusikan ke halaman ini
  • Pengujian patokan untuk setiap kontrubusi (waktu eksekusi rata-rata lebih dari 10.000 berjalan)
  • Tautan ke setiap jawaban (untuk kode lengkap)

Semua fungsi diuji dengan pengaturan yang sama:

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

Fungsi yang hanya menggantikan kemunculan pertama string dalam string:


Fungsi yang hanya menggantikan yang terakhir terjadinya string dalam string:


Terima kasih untuk ini, saya biasanya menggunakan preg_replace karena ini adalah yang paling fleksibel jika tweak di masa depan diperlukan dalam kebanyakan kasus 27% lebih lambat tidak akan menjadi signifikan
zzapper

@ oLinkWebDevelopment Saya tertarik melihat skrip benchmark Anda. Saya pikir itu bisa terbukti bermanfaat.
Dave Morton

Alasan mengapa substr_replace()menang hasilnya sederhana; karena ini merupakan fungsi internal. Dua fungsi internal yang dilakukan pengguna dan fungsi yang ditentukan pengguna berbeda dalam kinerja, karena fungsi internal berjalan di lapisan bawah. Jadi mengapa tidak preg_match()? Ekspresi reguler hampir lebih lambat daripada setiap fungsi manipulasi string internal, karena negara mereka mencari dalam string beberapa kali.
MAChitgarha

1
Saya harap patokan pada "pemenang" Anda ( substr_replace($string, $replace, 0, strlen($search));) tidak hanya menulis yang statis 0. Bagian dari konvolusi solusi non-regex adalah bahwa mereka perlu "menemukan" titik awal sebelum mengetahui di mana harus mengganti.
mickmackusa

55

Sayangnya, saya tidak tahu fungsi PHP yang bisa melakukan ini.
Anda dapat menggulung sendiri dengan mudah seperti ini:

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

Saya pikir ini adalah versi paling golf dari semuanya - menggunakan joinbukan implode.
Titus

return implode($replace, explode($find, $subject, $limit+1));untuk nomor ganti kustom
beppe9000

7

Saya membuat fungsi kecil ini yang menggantikan string pada string (case-sensitive) dengan batas, tanpa perlu Regexp. Ini bekerja dengan baik.

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

Contoh penggunaan:

$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

Meskipun saya lebih suka melakukannya ===falsedaripada is_bool(menjadi lebih eksplisit - saya memberikan jempol ini hanya karena telah menghindari kegilaan RegExp ! ... dan pada saat yang sama itu berfungsi dan membersihkan solusi ...
jave.web

Memilih solusi yang mudah disesuaikan preg_bukanlah kegilaan tetapi preferensi pribadi. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);cukup mudah dibaca untuk orang yang tidak takut regex. Perlu pencarian case-insensitive? Tambahkan isetelah pembatas pola akhir. Perlu dukungan unicode / multibyte? Tambahkan usetelah pembatas pola akhir. Perlu dukungan batas kata? Tambahkan \bdi kedua sisi string pencarian Anda. Jika Anda tidak ingin regex, jangan gunakan regex. Kuda untuk kursus, tetapi tentu saja bukan kegilaan.
mickmackusa

3

Cara termudah adalah dengan menggunakan ekspresi reguler.

Cara lain adalah menemukan posisi string dengan strpos () dan kemudian substr_replace ()

Tetapi saya benar-benar akan pergi untuk RegExp.


"Petunjuk" ini agak kabur / bernilai rendah dibandingkan dengan tulisan lain di halaman ini.
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));
}

Jawaban khusus kode hanya bernilai rendah di StackOverflow karena mereka melakukan pekerjaan yang buruk dalam mendidik / memberdayakan ribuan peneliti masa depan.
mickmackusa

3

=> KODE DIREVISI, jadi anggap beberapa komentar terlalu lama

Dan terima kasih semuanya telah membantu saya meningkatkannya

BUG apa pun, tolong komunikasikan saya; Saya akan memperbaikinya setelah

Jadi, mari kita pergi untuk:

Mengganti 'o' menjadi 'ea' pertama misalnya:

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

//output: I leave you

Fungsi:

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

Gagal jika $ this telah mengulangi karakter seperti aaa vs aaaaaaaaa
Cristo

Saya pikir itu harus substr($where,$b+strlen($this)), bukan substr($where,$b+1). Dan saya kira itu substr_replacelebih cepat.
Titus

Kode direvisi, sekarang berfungsi bahkan untuk string panjang
PYK

Solusi ini tidak berfungsi sebagai kode. Bukti: 3v4l.org/cMeZj Dan ketika Anda memperbaiki masalah penamaan variabel, itu tidak berfungsi ketika nilai pencarian tidak ditemukan - itu merusak string input. Bukti: 3v4l.org/XHtfc
mickmackusa

Apakah adil bagi seseorang yang meminta untuk MEM memperbaiki kode? @mickmackusa Bisakah Anda memeriksanya lagi?
PYK

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

Ini sama dengan jawaban pertama. Selain itu, Anda harus melakukan preg_quotedari $findsebelum menggunakannya sebagai ekspresi.
Emil Vikström

ini yang saya gunakan, jadi saya memilihnya. Jawaban pertama menyebabkan konflik dengan Drupal, itu harus ditimpa fungsi pembantu drupal. Jadi saya hanya mengambil kode yang ada di dalam fungsi dan menggunakannya sejalan dengan sisa kode ...
Dan Mantyla

Jawaban khusus kode ini memberikan saran berlebihan pada halaman (belum lagi kurang preg_quote(). Jawaban duplikat yang terlambat ini dapat dihapus dengan aman dari halaman karena sarannya disediakan oleh yang sebelumnya, dan jawaban yang diterima lebih tinggi dan
tervvatif

2

Untuk memperluas jawaban @ renocor , saya telah menulis sebuah fungsi yang 100% kompatibel dengan mundur str_replace(). Artinya, Anda dapat mengganti semua kejadian dari str_replace()dengan str_replace_limit()tanpa mengacaukan apa-apa, bahkan mereka menggunakan array untuk $search, $replacedan / atau $subject.

Fungsi bisa sepenuhnya mandiri, jika Anda ingin mengganti panggilan fungsi dengan ($string===strval(intval(strval($string)))), tapi saya sarankan menentangnya karena valid_integer()ini adalah fungsi yang agak berguna ketika berhadapan dengan bilangan bulat yang disediakan sebagai string.

Catatan: Kapan pun memungkinkan, str_replace_limit()akan digunakan str_replace()sebagai gantinya, sehingga semua panggilan ke str_replace()dapat diganti dengan str_replace_limit()tanpa mengkhawatirkan hit ke kinerja.

Pemakaian

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

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

1 penggantian - bbcabc

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

2 penggantian - bbcbbc

Fungsi

<?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
agak kembung jika Anda bertanya kepada saya. Juga apa yang paling saya 'benci' pada solusi ini adalah penanganan kesalahan. Ini merusak skrip jika Anda memberikan nilai yang salah. Anda pikir itu terlihat profesional tetapi tidak, bukannya kesalahan menghasilkan pemberitahuan atau peringatan sebagai gantinya. Lebih baik melewatkan omong kosong, ganti false atau null dan jangan pernah menggunakan backtrace dalam fungsi seperti ini. Solusi terbaik adalah bahwa programmer dapat memutuskan apa yang harus dilakukan ketika output salah / tidak terduga.
Codebeat

@Erwinus ini menggunakan E_USER_WARNINGseluruh, yang merupakan peringatan , bukan sebuah kesalahan . Backtrace sangat berguna untuk mengetahui kode apa yang meneruskan data yang tidak valid ke fungsi di tempat pertama (yang mutlak diperlukan untuk melacak bug dalam produksi). Sedangkan untuk kembali $subjectdaripada false/ nullatau melempar kesalahan, itu hanyalah pilihan pribadi untuk kasus penggunaan saya. Untuk mencocokkan str_replace()fungsionalitas, menggunakan kesalahan fatal yang bisa ditangkap akan menjadi taruhan terbaik (seperti str_replace()halnya ketika memberikan penutupan untuk dua argumen pertama).
0b10011

Ah, tidak memperhatikan tentang E_USER_WARNING yang Anda gunakan, maaf untuk itu. Masalah dengan mengembalikan subjek adalah Anda tidak akan pernah melihat ada sesuatu yang salah, di luar fungsi. Yang mengatakan, fungsinya bisa setengah ukuran jika Anda melakukannya dengan lebih cerdas (mungkin). Kedua, komentar baik-baik saja ketika menjelaskan sesuatu yang kompleks tetapi tidak terlalu berguna untuk hal-hal sederhana seperti meningkatkan nilai. Secara keseluruhan saya pikir itu tidak perlu besar. Selain itu, menggunakan peringatan di lingkungan produksi dapat menjadi masalah keamanan ketika Anda menggunakan kode ini di server yang tidak menekan pesan run-time secara default (log).
Codebeat

@Erwinus Saya adalah verbose ketika datang ke komentar karena beberapa orang tidak mengerti bahasa serta yang lain, dan komentar selalu dapat dihapus oleh orang-orang yang mengerti itu. Jika Anda tahu cara yang lebih baik untuk mendapatkan hasil akhir yang sama untuk semua kasus tepi, tentu saja, edit jawabannya. Dan jika lingkungan produksi Anda tidak menekan pesan kesalahan, Anda punya masalah yang lebih besar dari fungsi ini;)
0b10011

TL; DR Cuplikan ini sangat membengkak sehingga saya tidak bisa membayangkan memilihnya daripada fungsi regex (saya benci scrolling). Jika Anda ingin menghitung penggantian yang dibuat, ada parameter untuk itu di preg_replace(). Selanjutnya, preg_replace()/ regex menawarkan penanganan batas kata (jika diinginkan) - sesuatu yang fungsi non-regex tidak akan berikan secara elegan.
mickmackusa

2

Menurut hasil pengujian saya, saya ingin memilih yang biasa_express yang disediakan oleh karim79. (Saya tidak punya cukup reputasi untuk memilihnya sekarang!)

Solusi dari zombat menggunakan terlalu banyak pemanggilan fungsi, saya bahkan menyederhanakan kodenya. Saya menggunakan PHP 5.4 untuk menjalankan kedua solusi sebanyak 100.000 kali, dan inilah hasilnya:

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

==> 1,85 dtk

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

==> 1,35 detik

Seperti yang Anda lihat. Performa preg_replace tidak seburuk yang dipikirkan banyak orang. Jadi saya akan menyarankan solusi berkelas jika express reguler Anda tidak rumit.


Cuplikan pertama Anda adalah perbandingan yang tidak adil karena gagal menggunakan implementasi yang benar. Anda tidak memeriksa $posuntuk false, sehingga ketika jarum tidak ada di tumpukan jerami, hal itu akan merusak output.
mickmackusa

Terima kasih @mickmackusa, Anda benar. Tapi bukan itu intinya. Saya mengatakan kode ini disederhanakan hanya untuk membandingkan efisiensi implementasi.
Hunter Wu

Itulah poin saya. Anda tidak boleh membuat perbandingan tolok ukur yang tidak melakukan proses yang sama persis. Membandingkan apel dengan setengah jeruk tidak berguna. Sepenuhnya menerapkan pendekatan non-regex yang lengkap akan membuat perbedaan kecepatan lebih mendalam.
mickmackusa

Baiklah terima kasih lagi. Tetapi yang saya inginkan adalah menemukan implementasi yang lebih baik, bukan untuk membuat perbedaan yang lebih mendalam.
Hunter Wu

2

Untuk memperluas jawaban zombat (yang saya yakini sebagai jawaban terbaik), saya membuat versi rekursif dari fungsinya yang mengambil $limitparameter untuk menentukan berapa banyak kejadian yang ingin Anda ganti.

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

Catatan, tidak ada pemeriksaan kualitas pada $start_pos, jadi jika itu berada di luar panjang string, fungsi ini akan menghasilkan: Warning: strpos(): Offset not contained in string.... Fungsi ini gagal melakukan penggantian bila $start_posmelebihi panjang. Bukti Kegagalan: 3v4l.org/qGuVIR ... Fungsi Anda dapat menggabungkan return $haystackkondisi dan menghindari mendeklarasikan variabel sekali pakai seperti ini: 3v4l.org/Kdmqp Namun, seperti yang saya katakan di komentar di bagian lain halaman ini, saya lebih suka gunakan panggilan yang sangat bersih, langsung, non-rekursif preg_replace().
mickmackusa

ya sehingga Anda dapat menambahkan elsestatment baris ini$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

Untuk sebuah 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

Untuk satu karakter

$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

Cuplikan substr_replace () pertama gagal ketika string pencarian tidak diimbangi 0 dari string input. Bukti kegagalan: 3v4l.org/oIbRv Dan kedua substr_replace()teknik tersebut merusak string input ketika nilai pencarian tidak ada. Bukti kegagalan: 3v4l.org/HmEml (Dan teknik terakhir dengan semua revpanggilan secara serius berbelit-belit / keras di mata.)
mickmackusa

2

Melengkapi apa yang dikatakan orang, ingat bahwa seluruh string adalah array:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"


3
Kecuali itu berisi karakter multibyte ... dan kemudian teknik Anda gagal. Sangat disayangkan bahwa Anda menawarkan string input sampel yang berisi á. Demonstrasi kegagalan
mickmackusa

Anda dapat memverifikasi apakah Anda stringmenggunakan string multibytemb_strlen($subject) != strlen($subject)
RousseauAlexandre

Posting ini tidak berusaha menjawab pertanyaan yang diajukan.
mickmackusa

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

Dengan menggunakan substr_replace kita dapat mengganti kemunculan karakter pertama hanya dalam string. sebagai & diulang beberapa kali tetapi hanya pada posisi pertama kita harus mengganti & dengan?


1

Fungsi ini sangat terinspirasi oleh jawaban oleh @renocor. Itu membuat fungsi multi byte aman.

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

Anda bisa menggunakan ini:

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

Temukan contoh ini dari php.net

Pemakaian:

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

Keluaran:

ThiZ iz an examplz

Ini mungkin mengurangi kinerja sedikit, tetapi solusi termudah.


Jika itu output dari apa gunanya? Bukankah seharusnya hanya mengganti huruf kecil "z" dengan huruf besar "Z"? Alih-alih mengganti semuanya? Saya pikir itulah yang kami bicarakan di sini ...
Putar

Buruk saya, itu hanya akan menggantikan kejadian pertama. Diedit.
happyhardik

Nasihat yang sama ini sudah ditawarkan oleh Bas hampir 3 tahun sebelumnya (dan tanpa menelepon secara berlebihan strpos()). Diturunkan karena tidak menambah nilai baru ke halaman.
mickmackusa

0

Jika string Anda tidak mengandung karakter multibyte dan jika Anda ingin mengganti hanya satu karakter, Anda dapat menggunakannya strpos

Di sini fungsi yang menangani kesalahan

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

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

-1

Inilah kelas sederhana yang saya buat untuk membungkus fungsi str_replace () kami yang sedikit dimodifikasi .

Fungsi php :: str_rreplace () kami juga memungkinkan Anda untuk melakukan str_replace () terbalik yang terbatas, yang bisa sangat berguna ketika mencoba mengganti hanya instance X terakhir dari sebuah string.

Contoh-contoh ini menggunakan 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) );
    }
}

Posting Anda tidak menambah nilai pada halaman jenuh yang sudah ada ini. Solusi regex Anda gagal pada banyak kasus pinggiran karena Anda menggunakan alat yang salah untuk melarikan diri karakter dalam string jarum. Bukti kegagalan: 3v4l.org/dTdYK Jawaban yang sangat banyak dipilih dan diterima dari tahun 2009 sudah menunjukkan pelaksanaan yang tepat dari teknik ini. Metode kedua Anda tidak menjawab pertanyaan yang diajukan dan sudah disediakan oleh oLinkWebDevelopment.
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")

Ada satu ruang tambahan lagi tapi itu tidak masalah karena itu untuk skrip backgound dalam kasus saya.


Teknik ini disediakan oleh toomuchphp pada tahun 2009 ! Saya telah menurunkan suara karena pos Anda tidak menambah nilai baru bagi para peneliti. Harap pastikan, sebelum mengirim jawaban, bahwa solusi Anda unik untuk halaman dan menambah nilai ke halaman.
mickmackusa

-3

ini jawaban pertama saya di sini, saya berharap bisa melakukannya dengan benar. Mengapa tidak menggunakan argumen keempat fungsi str_replace untuk masalah ini?

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

Hitungan: Jika disahkan, ini akan diatur ke jumlah penggantian yang dilakukan.


sunting: Jawaban ini salah, karena parameter ke-4 dari str_replace adalah variabel yang mendapatkan jumlah penggantian yang dilakukan. Ini tidak konsisten dengan preg_replace , yang memiliki parameter ke-4 $limitdan parameter ke-5&$count .


Argumen keempat akan ditetapkan oleh str_replace () ke jumlah penggantian yang dilakukan. Itu sebabnya Anda mendapatkan kesalahan saat Anda melewatkan bilangan bulat dan bukan variabel untuk itu.
arminrosu

-6

Sangat mudah untuk menemukan solusi untuk mengganti hanya beberapa contoh pertama atau pertama (dengan memberikan nilai hitungan). Tidak ada banyak solusi untuk mengganti pasangan contoh terakhir atau terakhir.

Mungkin sesuatu seperti str_replace ($ find, $ replace, $ subject, -3) harus menggantikan tiga instance terakhir.

Pokoknya hanya saran saja.


4
Mengapa menjawab pertanyaan dengan saran ketika jawaban telah diterima dua tahun sebelumnya ?!
mbinette

Juga -3 tidak akan berfungsi sebagai parameter, karena parameter ke-4 adalah output dan bukan parameter input. Akan lebih baik jika Anda menguji apa yang Anda usulkan, daripada memposting kode yang macet.
Ghostwriter78

Ya, ini salah, namun, mengapa saya mendapatkan crash layar kosong, ketika saya mencobanya? Saya melakukan error_reporting biasa (E_ALL); ini_set ("display_errors", 1); Kesalahan layar masih kosong.
Doug Cassidy
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.