Menggunakan str_replace sehingga hanya berfungsi pada pertandingan pertama?
325
Saya ingin versi str_replace()yang hanya menggantikan kemunculan pertama $searchdi $subject. Apakah ada solusi mudah untuk ini, atau saya perlu solusi hacky?
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.
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):
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:
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.
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 $replacereturn implode($replace, explode($find, $subject,2));}
$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 ()
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.
<?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 -1if($limit!=-1){
$limit -= $this_count;}// Reached $limit, return $subjectif($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 -1if($limit!=-1){
$limit -= $this_count;}// Reached $limit, return $subjectif($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 foundif($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 reachedfor($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 $needleif($pos===false){break;}}// Return new $subjectreturn $subject;}}
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.
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;
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.
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?
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 paramsif(strlen($replace)!=1|| strlen($search)!=1){thrownewInvalidArgumentException('$search & $replace must be char');}elseif(mb_strlen($subject)!= strlen($subject)){thrownewInvalidArgumentException('$subject is an multibytes string');}// search
$pos = strpos($subject, $search);if($pos ===false){// not foundreturn $subject;}// replace
$subject[$replace]= $subject;return $subject;}
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.
<?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
*/publicstaticfunction 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
*/publicstaticfunction 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.
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?
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.
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.
s($subject)->replaceFirst($search)
dans($subject)->replaceFirstIgnoreCase($search)
membantu, seperti yang ditemukan di perpustakaan mandiri ini .Jawaban:
Dapat dilakukan dengan preg_replace :
Keajaiban ada di parameter opsional keempat [Batas]. Dari dokumentasi:
Padahal, lihat jawaban zombat untuk metode yang lebih efisien (kira-kira, 3-4x lebih cepat).
sumber
preg_quote
? Sebagai contoh, @ThomasRedstone khawatir pembatas/
bisa berbahaya jika muncul$from
, tetapi untungnya tidak: itu benar lolos karenapreg_quote
parameter kedua (orang dapat dengan mudah mengujinya). Saya tertarik untuk mendengar tentang masalah khusus (yang akan menjadi bug keamanan PCRE serius dalam buku saya).Tidak ada versi itu, tetapi solusinya sama sekali tidak rusak.
Cukup mudah, dan menyimpan penalti performa dari ekspresi reguler.
Bonus: Jika Anda ingin mengganti kejadian terakhir , cukup gunakan
strrpos
sebagai gantistrpos
.sumber
substr_replace
fungsi 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.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):
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:
Ini adalah jawaban revisi dari 'terlalu banyak php':
Catat 2 di bagian akhir daripada 1. Atau dalam format fungsi:
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.
sumber
stripos()
to the rescue :-)Saya bertanya-tanya mana yang tercepat, jadi saya menguji semuanya.
Di bawah ini Anda akan menemukan:
Semua fungsi diuji dengan pengaturan yang sama:
Fungsi yang hanya menggantikan kemunculan pertama string dalam string:
substr_replace($string, $replace, 0, strlen($search));
replace_first($search, $replace, $string);
preg_replace($search, $replace, $string, 1);
str_replace_once($search, $replace, $string);
str_replace_limit($search, $replace, $string, $count, 1);
str_replace_limit($search, $replace, $string, 1);
str_replace_limit($string, $search, $replace, 1, 0);
Fungsi yang hanya menggantikan yang terakhir terjadinya string dalam string:
substr_replace($string, $replace, strrpos($string, $search), strlen($search));
strrev(implode(strrev($replace), explode(strrev($search), strrev($string), 2)));
sumber
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 tidakpreg_match()
? Ekspresi reguler hampir lebih lambat daripada setiap fungsi manipulasi string internal, karena negara mereka mencari dalam string beberapa kali.substr_replace($string, $replace, 0, strlen($search));
) tidak hanya menulis yang statis0
. Bagian dari konvolusi solusi non-regex adalah bahwa mereka perlu "menemukan" titik awal sebelum mengetahui di mana harus mengganti.Sayangnya, saya tidak tahu fungsi PHP yang bisa melakukan ini.
Anda dapat menggulung sendiri dengan mudah seperti ini:
sumber
join
bukanimplode
.return implode($replace, explode($find, $subject, $limit+1));
untuk nomor ganti kustomSaya membuat fungsi kecil ini yang menggantikan string pada string (case-sensitive) dengan batas, tanpa perlu Regexp. Ini bekerja dengan baik.
Contoh penggunaan:
sumber
===false
daripadais_bool(
menjadi lebih eksplisit - saya memberikan jempol ini hanya karena telah menghindari kegilaan RegExp ! ... dan pada saat yang sama itu berfungsi dan membersihkan solusi ...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? Tambahkani
setelah pembatas pola akhir. Perlu dukungan unicode / multibyte? Tambahkanu
setelah pembatas pola akhir. Perlu dukungan batas kata? Tambahkan\b
di kedua sisi string pencarian Anda. Jika Anda tidak ingin regex, jangan gunakan regex. Kuda untuk kursus, tetapi tentu saja bukan kegilaan.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.
sumber
sumber
=> KODE DIREVISI, jadi anggap beberapa komentar terlalu lama
Jadi, mari kita pergi untuk:
Mengganti 'o' menjadi 'ea' pertama misalnya:
Fungsi:
sumber
substr($where,$b+strlen($this))
, bukansubstr($where,$b+1)
. Dan saya kira itusubstr_replace
lebih cepat.sumber
preg_quote
dari$find
sebelum menggunakannya sebagai ekspresi.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 danUntuk memperluas jawaban @ renocor , saya telah menulis sebuah fungsi yang 100% kompatibel dengan mundur
str_replace()
. Artinya, Anda dapat mengganti semua kejadian daristr_replace()
denganstr_replace_limit()
tanpa mengacaukan apa-apa, bahkan mereka menggunakan array untuk$search
,$replace
dan / atau$subject
.Fungsi bisa sepenuhnya mandiri, jika Anda ingin mengganti panggilan fungsi dengan
($string===strval(intval(strval($string))))
, tapi saya sarankan menentangnya karenavalid_integer()
ini adalah fungsi yang agak berguna ketika berhadapan dengan bilangan bulat yang disediakan sebagai string.Catatan: Kapan pun memungkinkan,
str_replace_limit()
akan digunakanstr_replace()
sebagai gantinya, sehingga semua panggilan kestr_replace()
dapat diganti denganstr_replace_limit()
tanpa mengkhawatirkan hit ke kinerja.Pemakaian
Fungsi
sumber
E_USER_WARNING
seluruh, 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$subject
daripadafalse
/null
atau melempar kesalahan, itu hanyalah pilihan pribadi untuk kasus penggunaan saya. Untuk mencocokkanstr_replace()
fungsionalitas, menggunakan kesalahan fatal yang bisa ditangkap akan menjadi taruhan terbaik (sepertistr_replace()
halnya ketika memberikan penutupan untuk dua argumen pertama).preg_replace()
. Selanjutnya,preg_replace()
/ regex menawarkan penanganan batas kata (jika diinginkan) - sesuatu yang fungsi non-regex tidak akan berikan secara elegan.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:
==> 1,85 dtk
==> 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.
sumber
$pos
untukfalse
, sehingga ketika jarum tidak ada di tumpukan jerami, hal itu akan merusak output.Untuk memperluas jawaban zombat (yang saya yakini sebagai jawaban terbaik), saya membuat versi rekursif dari fungsinya yang mengambil
$limit
parameter untuk menentukan berapa banyak kejadian yang ingin Anda ganti.sumber
$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_pos
melebihi panjang. Bukti Kegagalan: 3v4l.org/qGuVIR ... Fungsi Anda dapat menggabungkanreturn $haystack
kondisi 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-rekursifpreg_replace()
.else
statment baris ini$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Untuk sebuah string
Untuk satu karakter
sumber
substr_replace()
teknik tersebut merusak string input ketika nilai pencarian tidak ada. Bukti kegagalan: 3v4l.org/HmEml (Dan teknik terakhir dengan semuarev
panggilan secara serius berbelit-belit / keras di mata.)Melengkapi apa yang dikatakan orang, ingat bahwa seluruh string adalah array:
"Borem ipsum lá lá lá"
sumber
á
. Demonstrasi kegagalanstring
menggunakan string multibytemb_strlen($subject) != strlen($subject)
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?
sumber
Fungsi ini sangat terinspirasi oleh jawaban oleh @renocor. Itu membuat fungsi multi byte aman.
sumber
Anda bisa menggunakan ini:
Temukan contoh ini dari php.net
Pemakaian:
Keluaran:
Ini mungkin mengurangi kinerja sedikit, tetapi solusi termudah.
sumber
strpos()
). Diturunkan karena tidak menambah nilai baru ke halaman.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
sumber
Untuk Solusi Loop
sumber
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 () .
sumber
Ada satu ruang tambahan lagi tapi itu tidak masalah karena itu untuk skrip backgound dalam kasus saya.
sumber
ini jawaban pertama saya di sini, saya berharap bisa melakukannya dengan benar. Mengapa tidak menggunakan argumen keempat fungsi str_replace untuk masalah ini?
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
$limit
dan parameter ke-5&$count
.sumber
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.
sumber