Używasz str_replace, aby działał tylko przy pierwszym dopasowaniu?
325
Chcę, aby wersja str_replace()tego zastąpiła tylko pierwsze wystąpienie $searchw $subject. Czy istnieje na to łatwe rozwiązanie, czy też potrzebuję rozwiązania hacky?
Minusem tej metody jest obniżenie wydajności wyrażeń regularnych.
zombat
27
Kolejnym minusem jest to, że musisz użyć preg_quote () na „igle” i uciec meta-znaków $ i \ w zamianie.
Josh Davis,
32
Nie udaje się to jako ogólne rozwiązanie z powodu nieprzyjemnych problemów związanych z ucieczką.
Jeremy Kauffman,
2
Zdecydowanie zbyt często wyrażenia regularne są odrzucane z powodu „wydajności”, gdyby wydajność była głównym problemem, nie pisalibyśmy w PHP! Do zawinięcia wzoru można użyć czegoś innego niż „/”, na przykład „~”, co pomogłoby w pewnym stopniu uniknąć problemu ucieczki. Zależy, jakie są dane i skąd pochodzą.
ThomasRedstone,
1
Pomijając wady wydajności - czy ci, którzy narzekają na unikanie problemów, mają na myśli coś konkretnego oprócz potencjalnych błędów preg_quote? Na przykład @ThomasRedstone martwi się, że separator /może być niebezpieczny, jeśli się pojawi $from, ale na szczęście tak nie jest: jest poprawnie uciekany z powodu preg_quotedrugiego parametru (można to łatwo przetestować). Chciałbym usłyszeć o konkretnych problemach (które mogłyby stanowić poważne błędy bezpieczeństwa PCRE w mojej książce).
MvanGeest,
610
Nie ma żadnej wersji, ale rozwiązanie wcale nie jest hakujące.
Może być znacznie szybszy i zużywa mniej pamięci niż wyrażenia regularne. Nie mam pojęcia, dlaczego ktoś miałby tak głosować ...
Josh Davis,
12
Podoba mi się to podejście, ale kod zawiera błąd, ostatnim parametrem wywołania substr_replace powinien być strlen ($ igła) zamiast strlen ($ replace) .. uwaga na to !!
Nelson
Jest „hacky” w tym sensie, że potrzeba znacznie więcej czasu, aby dowiedzieć się, co się dzieje. Również jeśli byłby to czysty kod, nie wspominano by, że kod zawiera błąd. Jeśli można popełnić błąd w tak małym fragmencie, jest już zbyt hackerski.
Camilo Martin
9
Nie zgadzam się z @CamiloMartin w odniesieniu do liczby linii vs. możliwości błędów. Chociaż substr_replacejest to dość niewygodna funkcja ze względu na wszystkie parametry, prawdziwym problemem jest to, że manipulowanie ciągami liczbowymi jest czasami trudne - musisz uważać, aby przekazać odpowiednią zmienną / offset do funkcji. I tak posunąłbym się do stwierdzenia, że powyższy kod jest najprostszym, i dla mnie, logicznym, podejściem.
Alex
1
Genialne podejście. Działa doskonale, zastępując wartości zmiennych, które mają w nich zarezerwowane znaki regularne (więc preg_replace jest opatrzony). To jest proste i eleganckie.
Praesagus
96
Edycja: obie odpowiedzi zostały zaktualizowane i są teraz poprawne. Pozostawię odpowiedź, ponieważ czasy funkcji są nadal przydatne.
Odpowiedzi „zombat” i „za dużo php” są niestety nieprawidłowe. To jest korekta opublikowanej odpowiedzi zombat (ponieważ nie mam wystarczającej reputacji, aby opublikować komentarz):
Zanotuj strlen ($ igła), zamiast strlen ($ replace). Przykład Zombata będzie działał poprawnie tylko wtedy, gdy igła i zamiennik będą tej samej długości.
Oto ta sama funkcjonalność w funkcji z tym samym podpisem, co własny str_replace PHP:
Dlaczego nie uogólnić tego na: str_replace_fiętkie (mieszane $ s, mieszane $ r, int $ offset, int $ limit), gdzie funkcja zastępuje wystąpienia $ limit zaczynające się od dopasowania $ offset (n-ty).
Adam Friedman
Szkoda, że dotyczy to tylko rozróżniania wielkości liter.
Dzięki za to zazwyczaj używam preg_replace, ponieważ jest najbardziej elastyczny, jeśli przyszłe poprawki są wymagane w większości przypadków 27% wolniej nie będzie znaczące
zzapper
@oLinkWebDevelopment Chciałbym zobaczyć twój skrypt testowy. Myślę, że może się to przydać.
Dave Morton,
Powód, dla którego substr_replace()wygrywa wynik, jest prosty; ponieważ jest to funkcja wewnętrzna. Dwie funkcje wewnętrzne i funkcje zdefiniowane przez użytkownika różnią się pod względem wydajności, ponieważ wewnętrzna działa w niższych warstwach. Dlaczego więc nie preg_match()? Wyrażenia regularne są prawie wolniejsze niż każda wewnętrzna funkcja manipulacji ciągiem, ze względu na ich naród wyszukiwania w ciągu wiele razy.
MAChitgarha,
1
Mam nadzieję, że punkt odniesienia dla twojego „zwycięzcy” ( substr_replace($string, $replace, 0, strlen($search));) nie tylko napisał to statyczne 0. Częścią splotu rozwiązań innych niż wyrażenia regularne jest to, że muszą „znaleźć” punkt wyjścia, zanim będą wiedzieć, gdzie wymienić.
mickmackusa,
55
Niestety nie znam żadnej funkcji PHP, która mogłaby to zrobić.
Możesz rzucić własnym dość łatwo w ten sposób:
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
Chociaż wolałbym to zrobić, ===falsezamiast is_bool(być bardziej wyraźnym - kciuk w górę, tylko dlatego, że udało mi się uniknąć szaleństwa RegExp ! ... a jednocześnie działa i czyste rozwiązanie ...
jave.web
Preferowanie łatwego do dostosowania preg_rozwiązania nie jest szaleństwem, ale osobistą preferencją. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);jest dość łatwy do odczytania dla osób, które nie boją się wyrażeń regularnych. Potrzebujesz wyszukiwania bez rozróżniania wielkości liter? Dodaj ipo ograniczniku wzoru końcowego. Potrzebujesz obsługi Unicode / Multibyte? Dodaj upo ograniczniku wzoru końcowego. Potrzebujesz obsługi granicy słów? Dodaj \bpo obu stronach szukanego ciągu. Jeśli nie chcesz wyrażenia regularnego, nie używaj wyrażenia regularnego. Konie na kursy, ale na pewno nie szaleństwo.
Nie powiedzie się, jeśli $ to powtarzało się znaki takie jak aaa vs aaaaaaaaa
Cristo
Myślę, że powinno być substr($where,$b+strlen($this)), nie substr($where,$b+1). I myślę, że substr_replaceto jest szybsze.
Tytus
Kod został zmieniony, teraz działa nawet dla długich łańcuchów
PYK
To rozwiązanie nie działa w sposób zakodowany. Dowód: 3v4l.org/cMeZj A kiedy naprawisz problem nazewnictwa zmiennych, nie działa to, gdy szukana wartość nie zostanie znaleziona - uszkadza łańcuch wejściowy. Dowód: 3v4l.org/XHtfc
mickmackusa
Czy to jest sprawiedliwe, jeśli ktoś prosi o USUNIĘCIE kodu? @mickmackusa Czy możesz to jeszcze raz sprawdzić?
PYK
2
$string ='this is my world, not my world';
$find ='world';
$replace ='farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;
To jest tak samo jak pierwsza odpowiedź. Poza tym, trzeba zrobić preg_quotez $findprzed użyciem go jako wyrazu.
Emil Vikström
tego właśnie użyłem, więc podniosłem głos. Pierwsza odpowiedź spowodowała konflikt z Drupalem, musiał on nadpisać funkcję pomocniczą drupala. Więc właśnie wziąłem kod, który był wewnątrz funkcji i użyłem go w jednej linii z resztą kodu ...
Dan Mantyla,
Ta odpowiedź tylko do kodu zawiera zbędne porady na stronie (nie wspominając o jej braku preg_quote(). Tę późno zduplikowaną odpowiedź można bezpiecznie usunąć ze strony, ponieważ jej porady są udzielane przez wcześniejszą i wyżej ocenianą akceptowaną odpowiedź.
mickmackusa
2
Aby rozwinąć odpowiedź @ renocor , napisałem funkcję, która jest w 100% kompatybilna wstecz str_replace(). Oznacza to, że można zastąpić wszystkie wystąpienia str_replace()ze str_replace_limit()nie brudząc się niczego, nawet tych z wykorzystaniem tablic dla $search, $replacei / lub$subject .
Funkcja może być całkowicie samodzielna, jeśli chcesz zastąpić wywołanie funkcji ($string===strval(intval(strval($string)))), ale odradzam to, ponieważ valid_integer()jest to dość przydatna funkcja w przypadku liczb całkowitych podanych jako łańcuchy.
Uwaga: Gdy tylko jest to możliwe, str_replace_limit()użyje str_replace()zamiast tego, więc wszystkie połączenia z str_replace()można zastąpićstr_replace_limit() bez obawy o obniżenie wydajności.
<?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;}}
trochę wzdęty, jeśli mnie zapytasz. Najbardziej „nienawidzę” tego rozwiązania, to obsługa błędów. Łamie skrypt, jeśli przekażesz niepoprawne wartości. Myślisz, że wygląda profesjonalnie, ale tak nie jest, zamiast błędu zamiast tego wyślij zawiadomienie lub ostrzeżenie. Lepiej jest pominąć bzdury, zamiast tego zwrócić wartość false lub zero i nigdy nie używać wstecznego śledzenia w takiej funkcji. Najlepszym rozwiązaniem jest to, że programista może zdecydować, co zrobić, gdy dane wyjściowe są nieprawidłowe / nieoczekiwane.
Codebeat
@Erwinus używa E_USER_WARNINGprzez cały czas, co jest ostrzeżenie , nie błąd . Śledzenie jest niezwykle przydatne, aby dowiedzieć się, jaki kod przekazuje nieprawidłowe dane do funkcji w pierwszej kolejności (co jest absolutnie niezbędne do śledzenia błędów w produkcji). Jeśli chodzi o powrót $subjectzamiast false/ nulllub zgłoszenie błędu, był to po prostu osobisty wybór dla mojego przypadku użycia. Aby dopasować str_replace()funkcjonalność, najlepszym rozwiązaniem byłoby użycie wykrywalnych błędów krytycznych (podobnie str_replace()jak w przypadku zamknięcia dwóch pierwszych argumentów).
0b10011
Ach, nie zauważyłem E_USER_WARNING, którego używasz, przepraszam za to. Problem ze zwróceniem tematu polega na tym, że poza funkcją nigdy nie widać, że coś było nie tak. To powiedziawszy, funkcja może być o połowę mniejsza, jeśli zrobisz to mądrzej (jest to możliwe). Po drugie, komentarze są w porządku, gdy wyjaśniają coś złożonego, ale niezbyt przydatne do prostych rzeczy, takich jak zwiększenie wartości. Ogólnie uważam, że jest to niepotrzebne ogromne. Używanie ostrzeżeń w środowisku produkcyjnym może również stanowić problem z bezpieczeństwem, gdy użyjesz tego kodu na serwerze, który domyślnie nie tłumi komunikatów w czasie wykonywania (logi).
Codebeat
@Erwinus Mówiłem dużo, jeśli chodzi o komentarze, ponieważ niektórzy ludzie nie rozumieją języka tak dobrze, jak inni, a komentarze mogą zawsze zostać usunięte przez tych, którzy go rozumieją. Jeśli znasz lepszy sposób na uzyskanie tego samego wyniku końcowego dla wszystkich przypadków na krawędziach, z pewnością zmień odpowiedź. A jeśli twoje środowisko produkcyjne nie
ukrywa
TL; DR Ten fragment jest tak rozdęty, że nie wyobrażam sobie wybierania go zamiast funkcji wyrażenia regularnego (nienawidzę przewijania). Jeśli chcesz policzyć dokonane zamiany, w parametrze znajduje się parametr preg_replace(). Ponadto preg_replace()/ regex oferuje obsługę granicy słów (jeśli jest to pożądane) - coś, czego funkcje inne niż wyrażenia regularne nie zapewnią elegancko.
mickmackusa
2
Zgodnie z moim wynikiem testu chciałbym głosować na regular_express dostarczony przez karim79. (Nie mam wystarczającej reputacji, aby głosować teraz!)
Rozwiązanie zombat wykorzystuje zbyt wiele wywołań funkcji, ja nawet upraszczam kody. Używam PHP 5.4 do uruchomienia obu rozwiązań 100 000 razy, a oto wynik:
$str ='Hello abc, have a nice day abc! abc!';
$pos = strpos($str,'abc');
$str = substr_replace($str,'123', $pos,3);
==> 1,85 sek
$str ='Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/','123', $str,1);
==> 1,35 sek
Jak widzisz. Wydajność preg_replace nie jest tak zła, jak wielu myśli. Proponuję więc klasyczne rozwiązanie, jeśli twój regularny ekspres nie jest skomplikowany.
Twój pierwszy fragment jest niesprawiedliwym porównaniem, ponieważ nie używa poprawnej implementacji. Nie sprawdzamy $posza false, więc gdy igła nie istnieje w stogu siana, to uszkodzić wyjście.
mickmackusa,
Dzięki @mickmackusa, masz rację. Ale nie o to chodzi. Powiedziałem, że ten kod jest uproszczony tylko w celu porównania wydajności implementacji.
Hunter Wu
Właśnie o to mi chodzi. Nigdy nie wolno dokonywać porównań porównawczych, które nie wykonują dokładnie tego samego procesu. Porównywanie jabłek do pół pomarańczy nie jest przydatne. Pełne wdrożenie pełnego podejścia innego niż wyrażenia regularne sprawi, że różnica prędkości będzie głębsza.
mickmackusa
Cóż, jeszcze raz dziękuję. Ale chcę znaleźć lepsze wdrożenie, a nie robić głębszych różnic.
Hunter Wu
2
Aby rozwinąć odpowiedź zombata (którą uważam za najlepszą), stworzyłem rekurencyjną wersję jego funkcji, która przyjmuje $limitparametr określający, ile wystąpień chcesz zastąpić.
Uwaga, nie ma jakości sprawdzenie $start_pos, więc jeśli to jest poza długością łańcucha, funkcja ta będzie generować: Warning: strpos(): Offset not contained in string.... Ta funkcja nie zastępuje, gdy $start_posjest poza długością. Dowód niepowodzenia: 3v4l.org/qGuVIR ... Twoja funkcja może łączyć return $haystackwarunki i unikać deklarowania zmiennych jednorazowych, takich jak: 3v4l.org/Kdmqp Jednak, jak powiedziałem w komentarzach w innym miejscu na tej stronie, wolałbym użyj bardzo czystego, bezpośredniego, nierekurencyjnego preg_replace()połączenia.
mickmackusa
tak, aby można było dodać tę elselinijkę$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A
2
Dla sznurka
$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
Dla jednego znaku
$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
Pierwszy fragment substr_replace () kończy się niepowodzeniem, gdy szukany ciąg nie znajduje się w przesunięciu 0 ciągu wejściowego. Dowód niepowodzenia: 3v4l.org/oIbRv I obie substr_replace()techniki uszkadzają łańcuch wejściowy, gdy szukana wartość nie jest obecna. Dowód niepowodzenia: 3v4l.org/HmEml (I ta ostatnia technika ze wszystkimi revwywołaniami jest poważnie zawiła / trudna dla oczu.)
mickmackusa
2
Uzupełniając to, co mówili ludzie, pamiętaj, że cały ciąg jest tablicą:
$string ="Lorem ipsum lá lá lá";
$string[0]="B";
echo $string;
Chyba że zawiera znaki wielobajtowe ... a wtedy twoja technika zawiedzie. Jak niefortunnie, że zaoferowałeś przykładowy ciąg wejściowy zawierający á. Demonstracja niepowodzenia
mickmackusa,
Możesz sprawdzić, czy masz stringciąg wielobajtowy, używającmb_strlen($subject) != strlen($subject)
RousseauAlexandre
Ten post nie próbuje odpowiedzieć na zadane pytanie.
Za pomocą substr_replace możemy zastąpić wystąpienie pierwszego znaku tylko ciągiem. ponieważ & jest powtarzane wiele razy, ale tylko na pierwszej pozycji musimy go zastąpić?
Jeśli to jest wynik, o co chodzi? Czy nie powinno zastępować tylko pierwszej małej litery „Z” wielką literą „Z”? Zamiast zastąpić je wszystkie? Myślałem, że o to tutaj rozmawiamy ...
Swivel
Mój zło, to zastąpi tylko pierwsze wystąpienie. Edytowane.
happyhardik
Ta sama rada była już oferowana przez Bas prawie 3 lata wcześniej (i bez nadmiernego dzwonienia strpos()). Odebrane, ponieważ nie dodaje żadnej nowej wartości do strony.
mickmackusa,
0
Jeśli ciąg nie zawiera żadnych znaków wielobajtowych i jeśli chcesz zastąpić tylko jeden znak, możesz po prostu użyć strpos
Tutaj funkcja obsługująca błędy
/**
* 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;}
Oto prosta klasa, którą stworzyłem, aby zawinąć nieco zmodyfikowaną str_replace () funkcje .
Nasza funkcja php :: str_rreplace () pozwala również na wykonanie odwróconego, ograniczonego str_replace (), co może być bardzo przydatne, gdy próbujesz zastąpić tylko ostatnie X wystąpienie ciągu.
<?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));}}
Twój post nie dodaje wartości temu już nasyconej stronie. Twoje rozwiązanie wyrażenia regularnego kończy się niepowodzeniem w wielu przypadkowych przypadkach, ponieważ użyłeś niepoprawnego narzędzia do zmiany znaków w ciągu igły. Dowód niepowodzenia: 3v4l.org/dTdYK Mocno pozytywnie oceniona i zaakceptowana odpowiedź z 2009 roku pokazuje już prawidłowe wykonanie tej techniki. Druga metoda nie odpowiada na zadane pytanie i została już dostarczona przez oLinkWebDevelopment.
Ta technika została dostarczona przez toomuchphp w 2009 roku ! Oddałem głos, ponieważ twój post nie wnosi żadnej nowej wartości do naukowców. Przed opublikowaniem odpowiedzi upewnij się, że Twoje rozwiązanie jest unikalne dla strony i stanowi wartość dodaną dla strony.
mickmackusa
-3
to jest moja pierwsza odpowiedź tutaj, mam nadzieję, że zrobię to poprawnie. Dlaczego nie użyć czwartego argumentu funkcji str_replace dla tego problemu?
count: Jeśli przejdzie, zostanie ustawiona liczba wykonanych zamian.
edytuj: Ta odpowiedź jest niepoprawna, ponieważ czwarty parametr str_replace jest zmienną, której przypisuje się liczbę wykonanych zamian. Jest to niespójne z preg_replace , który ma czwarty parametr $limiti piąty parametr &$count.
Czwarte argumenty zostaną ustawione przez str_replace () na liczbę dokonanych zamian. Dlatego pojawia się błąd, gdy podajesz liczbę całkowitą, a nie zmienną.
arminrosu
-6
Łatwo jest znaleźć rozwiązanie, które zastąpi tylko pierwszą lub pierwszą parę instancji (podając wartość zliczania). Nie ma wielu rozwiązań, które mogłyby zastąpić ostatnią lub ostatnią parę instancji.
Może coś takiego jak str_replace ($ find, $ replace, $ subject, -3) powinno zastąpić ostatnie trzy instancje.
Po co odpowiadać na pytanie z sugestią, kiedy odpowiedź została zaakceptowana dwa lata wcześniej ?!
mbinette
Również -3 nie będzie działać jako parametr, ponieważ czwarty parametr jest wyjściowy, a nie wejściowy. Byłoby lepiej, gdybyś przetestował to, co proponujesz, zamiast publikować kod, który ulega awarii.
Ghostwriter78
Tak, to źle, jednak dlaczego mam awarię pustego ekranu, kiedy próbuję? Zrobiłem zwykłe raportowanie błędów (E_ALL); ini_set („display_errors”, 1); Nadal występuje błąd pustego ekranu.
s($subject)->replaceFirst($search)
is($subject)->replaceFirstIgnoreCase($search)
pomocny, jak w tej samodzielnej bibliotece .Odpowiedzi:
Można to zrobić za pomocą preg_replace :
Magia tkwi w opcjonalnym czwartym parametrze [Limit]. Z dokumentacji:
Jednak zobacz odpowiedź zombata na bardziej wydajną metodę (z grubsza, 3-4x szybciej).
źródło
preg_quote
? Na przykład @ThomasRedstone martwi się, że separator/
może być niebezpieczny, jeśli się pojawi$from
, ale na szczęście tak nie jest: jest poprawnie uciekany z powodupreg_quote
drugiego parametru (można to łatwo przetestować). Chciałbym usłyszeć o konkretnych problemach (które mogłyby stanowić poważne błędy bezpieczeństwa PCRE w mojej książce).Nie ma żadnej wersji, ale rozwiązanie wcale nie jest hakujące.
Całkiem proste i oszczędza karę wydajności wyrażeń regularnych.
Premia: Jeśli chcesz zastąpić ostatnie wystąpienie, po prostu użyj
strrpos
zamiaststrpos
.źródło
substr_replace
jest to dość niewygodna funkcja ze względu na wszystkie parametry, prawdziwym problemem jest to, że manipulowanie ciągami liczbowymi jest czasami trudne - musisz uważać, aby przekazać odpowiednią zmienną / offset do funkcji. I tak posunąłbym się do stwierdzenia, że powyższy kod jest najprostszym, i dla mnie, logicznym, podejściem.Edycja: obie odpowiedzi zostały zaktualizowane i są teraz poprawne. Pozostawię odpowiedź, ponieważ czasy funkcji są nadal przydatne.
Odpowiedzi „zombat” i „za dużo php” są niestety nieprawidłowe. To jest korekta opublikowanej odpowiedzi zombat (ponieważ nie mam wystarczającej reputacji, aby opublikować komentarz):
Zanotuj strlen ($ igła), zamiast strlen ($ replace). Przykład Zombata będzie działał poprawnie tylko wtedy, gdy igła i zamiennik będą tej samej długości.
Oto ta sama funkcjonalność w funkcji z tym samym podpisem, co własny str_replace PHP:
Oto poprawiona odpowiedź „za dużo php”:
Zwróć uwagę na 2 na końcu zamiast 1. Lub w formacie funkcji:
Zmierzyłem czas obu funkcji, a pierwsza jest dwa razy szybsza, gdy nie znaleziono dopasowania. Są takie same prędkości po znalezieniu dopasowania.
źródło
stripos()
na ratunek :-)Zastanawiałem się, który z nich był najszybszy, więc przetestowałem je wszystkie.
Poniżej znajdziesz:
Wszystkie funkcje zostały przetestowane przy tych samych ustawieniach:
Funkcje, które zastępują tylko pierwsze wystąpienie ciągu w ciągu:
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);
Funkcje, które zastępują tylko ostatnie wystąpienie ciągu w ciągu:
substr_replace($string, $replace, strrpos($string, $search), strlen($search));
strrev(implode(strrev($replace), explode(strrev($search), strrev($string), 2)));
źródło
substr_replace()
wygrywa wynik, jest prosty; ponieważ jest to funkcja wewnętrzna. Dwie funkcje wewnętrzne i funkcje zdefiniowane przez użytkownika różnią się pod względem wydajności, ponieważ wewnętrzna działa w niższych warstwach. Dlaczego więc niepreg_match()
? Wyrażenia regularne są prawie wolniejsze niż każda wewnętrzna funkcja manipulacji ciągiem, ze względu na ich naród wyszukiwania w ciągu wiele razy.substr_replace($string, $replace, 0, strlen($search));
) nie tylko napisał to statyczne0
. Częścią splotu rozwiązań innych niż wyrażenia regularne jest to, że muszą „znaleźć” punkt wyjścia, zanim będą wiedzieć, gdzie wymienić.Niestety nie znam żadnej funkcji PHP, która mogłaby to zrobić.
Możesz rzucić własnym dość łatwo w ten sposób:
źródło
join
zamiastimplode
.return implode($replace, explode($find, $subject, $limit+1));
dla niestandardowych numerów wymianyStworzyłem tę małą funkcję, która zamienia ciąg znaków na ciąg znaków (wielkość liter ma znaczenie) z limitem, bez potrzeby Regexp. To działa dobrze.
Przykładowe użycie:
źródło
===false
zamiastis_bool(
być bardziej wyraźnym - kciuk w górę, tylko dlatego, że udało mi się uniknąć szaleństwa RegExp ! ... a jednocześnie działa i czyste rozwiązanie ...preg_
rozwiązania nie jest szaleństwem, ale osobistą preferencją.return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);
jest dość łatwy do odczytania dla osób, które nie boją się wyrażeń regularnych. Potrzebujesz wyszukiwania bez rozróżniania wielkości liter? Dodaji
po ograniczniku wzoru końcowego. Potrzebujesz obsługi Unicode / Multibyte? Dodaju
po ograniczniku wzoru końcowego. Potrzebujesz obsługi granicy słów? Dodaj\b
po obu stronach szukanego ciągu. Jeśli nie chcesz wyrażenia regularnego, nie używaj wyrażenia regularnego. Konie na kursy, ale na pewno nie szaleństwo.Najprostszym sposobem byłoby użycie wyrażenia regularnego.
Innym sposobem jest znalezienie pozycji ciągu za pomocą strpos (), a następnie substr_replace ()
Ale naprawdę wybrałbym RegExp.
źródło
źródło
=> KOD ZOSTAŁ ZMIENIONY, więc uważaj niektóre komentarze za stare
Więc chodźmy na:
Zamiana pierwszego „o” na „ea” na przykład:
Funkcja:
źródło
substr($where,$b+strlen($this))
, niesubstr($where,$b+1)
. I myślę, żesubstr_replace
to jest szybsze.źródło
preg_quote
z$find
przed użyciem go jako wyrazu.preg_quote()
. Tę późno zduplikowaną odpowiedź można bezpiecznie usunąć ze strony, ponieważ jej porady są udzielane przez wcześniejszą i wyżej ocenianą akceptowaną odpowiedź.Aby rozwinąć odpowiedź @ renocor , napisałem funkcję, która jest w 100% kompatybilna wstecz
str_replace()
. Oznacza to, że można zastąpić wszystkie wystąpieniastr_replace()
zestr_replace_limit()
nie brudząc się niczego, nawet tych z wykorzystaniem tablic dla$search
,$replace
i / lub$subject
.Funkcja może być całkowicie samodzielna, jeśli chcesz zastąpić wywołanie funkcji
($string===strval(intval(strval($string))))
, ale odradzam to, ponieważvalid_integer()
jest to dość przydatna funkcja w przypadku liczb całkowitych podanych jako łańcuchy.Uwaga: Gdy tylko jest to możliwe,
str_replace_limit()
użyjestr_replace()
zamiast tego, więc wszystkie połączenia zstr_replace()
można zastąpićstr_replace_limit()
bez obawy o obniżenie wydajności.Stosowanie
Funkcjonować
źródło
E_USER_WARNING
przez cały czas, co jest ostrzeżenie , nie błąd . Śledzenie jest niezwykle przydatne, aby dowiedzieć się, jaki kod przekazuje nieprawidłowe dane do funkcji w pierwszej kolejności (co jest absolutnie niezbędne do śledzenia błędów w produkcji). Jeśli chodzi o powrót$subject
zamiastfalse
/null
lub zgłoszenie błędu, był to po prostu osobisty wybór dla mojego przypadku użycia. Aby dopasowaćstr_replace()
funkcjonalność, najlepszym rozwiązaniem byłoby użycie wykrywalnych błędów krytycznych (podobniestr_replace()
jak w przypadku zamknięcia dwóch pierwszych argumentów).preg_replace()
. Ponadtopreg_replace()
/ regex oferuje obsługę granicy słów (jeśli jest to pożądane) - coś, czego funkcje inne niż wyrażenia regularne nie zapewnią elegancko.Zgodnie z moim wynikiem testu chciałbym głosować na regular_express dostarczony przez karim79. (Nie mam wystarczającej reputacji, aby głosować teraz!)
Rozwiązanie zombat wykorzystuje zbyt wiele wywołań funkcji, ja nawet upraszczam kody. Używam PHP 5.4 do uruchomienia obu rozwiązań 100 000 razy, a oto wynik:
==> 1,85 sek
==> 1,35 sek
Jak widzisz. Wydajność preg_replace nie jest tak zła, jak wielu myśli. Proponuję więc klasyczne rozwiązanie, jeśli twój regularny ekspres nie jest skomplikowany.
źródło
$pos
zafalse
, więc gdy igła nie istnieje w stogu siana, to uszkodzić wyjście.Aby rozwinąć odpowiedź zombata (którą uważam za najlepszą), stworzyłem rekurencyjną wersję jego funkcji, która przyjmuje
$limit
parametr określający, ile wystąpień chcesz zastąpić.źródło
$start_pos
, więc jeśli to jest poza długością łańcucha, funkcja ta będzie generować:Warning: strpos(): Offset not contained in string...
. Ta funkcja nie zastępuje, gdy$start_pos
jest poza długością. Dowód niepowodzenia: 3v4l.org/qGuVIR ... Twoja funkcja może łączyćreturn $haystack
warunki i unikać deklarowania zmiennych jednorazowych, takich jak: 3v4l.org/Kdmqp Jednak, jak powiedziałem w komentarzach w innym miejscu na tej stronie, wolałbym użyj bardzo czystego, bezpośredniego, nierekurencyjnegopreg_replace()
połączenia.else
linijkę$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Dla sznurka
Dla jednego znaku
źródło
substr_replace()
techniki uszkadzają łańcuch wejściowy, gdy szukana wartość nie jest obecna. Dowód niepowodzenia: 3v4l.org/HmEml (I ta ostatnia technika ze wszystkimirev
wywołaniami jest poważnie zawiła / trudna dla oczu.)Uzupełniając to, co mówili ludzie, pamiętaj, że cały ciąg jest tablicą:
„Borem ipsum lá lá lá”
źródło
á
. Demonstracja niepowodzeniastring
ciąg wielobajtowy, używającmb_strlen($subject) != strlen($subject)
Za pomocą substr_replace możemy zastąpić wystąpienie pierwszego znaku tylko ciągiem. ponieważ & jest powtarzane wiele razy, ale tylko na pierwszej pozycji musimy go zastąpić?
źródło
Ta funkcja jest silnie zainspirowana odpowiedzią @renocor. To sprawia, że funkcja jest wielobajtowa bezpieczna.
źródło
Możesz użyć tego:
Znaleziono ten przykład z php.net
Stosowanie:
Wynik:
Może to nieco obniżyć wydajność, ale jest to najłatwiejsze rozwiązanie.
źródło
strpos()
). Odebrane, ponieważ nie dodaje żadnej nowej wartości do strony.Jeśli ciąg nie zawiera żadnych znaków wielobajtowych i jeśli chcesz zastąpić tylko jeden znak, możesz po prostu użyć
strpos
Tutaj funkcja obsługująca błędy
źródło
Rozwiązanie Loop
źródło
Oto prosta klasa, którą stworzyłem, aby zawinąć nieco zmodyfikowaną str_replace () funkcje .
Nasza funkcja php :: str_rreplace () pozwala również na wykonanie odwróconego, ograniczonego str_replace (), co może być bardzo przydatne, gdy próbujesz zastąpić tylko ostatnie X wystąpienie ciągu.
W obu tych przykładach użyto preg_replace () .
źródło
Jest jeszcze jedna dodatkowa przestrzeń, ale nie miało to znaczenia, jak w moim przypadku dla skryptu backgound.
źródło
to jest moja pierwsza odpowiedź tutaj, mam nadzieję, że zrobię to poprawnie. Dlaczego nie użyć czwartego argumentu funkcji str_replace dla tego problemu?
edytuj: Ta odpowiedź jest niepoprawna, ponieważ czwarty parametr str_replace jest zmienną, której przypisuje się liczbę wykonanych zamian. Jest to niespójne z preg_replace , który ma czwarty parametr
$limit
i piąty parametr&$count
.źródło
Łatwo jest znaleźć rozwiązanie, które zastąpi tylko pierwszą lub pierwszą parę instancji (podając wartość zliczania). Nie ma wielu rozwiązań, które mogłyby zastąpić ostatnią lub ostatnią parę instancji.
Może coś takiego jak str_replace ($ find, $ replace, $ subject, -3) powinno zastąpić ostatnie trzy instancje.
W każdym razie tylko sugestia.
źródło