Myślę, że będzie to najbardziej wydajne/proste rozwiązanie (jednak nie uruchomiłem go przez jakiś test w czasie wykonywania).
function str_replace_except_last($needle, $replace, $text) {
if ($last_pos = strrpos($text, $needle)) {
$text = str_replace($needle, $replace, substr($text, 0, $last_pos)) . substr($text, $last_pos);
}
return $text;
}
Ponieważ pytanie było również związane z wydajnością, postanowiłem przetestować dwie wersje (kopalnia i ten oferowany przez @ Don'tPanic, która jest oparta na tablicach).
pierwsze - Powiem tylko, że przedwczesna optymalizacja jest źródłem wszelkiego zła
Teraz, gdy skończysz, że możemy przejść obok :)
postanowiłem stworzyć losowy ciąg znaków 10M, łańcuch będzie również zawierał .
(który będzie naszą igłą).
Uruchomiłem dwie funkcje 1000 razy na tym samym łańcuchu i sprawdziłem średni czas, w którym każda z funkcji była uruchamiana.
Sprawdziłem również, ile pamięci zajęła każda funkcja.
Oto wyniki:
- Stworzenie łańcucha znaków 10M trwało 2,3 sekundy.
- Przebieg funkcja łańcuch o średniej 0,016 sekundę (iteracji)
Wykorzystanie pamięci została 9,8
- Przebieg funkcja tablicy średnio 0,049 sekundę (kolejnych iteracjach)
Wykorzystanie pamięci została 45.1M
Oto kompletny kod (jeśli chcesz uruchomić go samodzielnie):
$ITERATIONS = 1000;
function generateRandomString($length = 10) {
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ. ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
function func_str($needle, $replace, $text) {
$m1 = memory_get_usage();
if ($last_pos = strrpos($text, $needle)) {
$text = str_replace($needle, $replace, substr($text, 0, $last_pos)) . substr($text, $last_pos);
}
$m2 = memory_get_usage();
//echo "memory diff ". ($m2-$m1) ."\n";
return $text;
}
function func_arr($needle, $replace, $text) {
$m1 = memory_get_usage();
$array = explode($replace, $text, substr_count($text, $replace));
$text = implode($replacement, $array);
$m2 = memory_get_usage();
//echo "memory diff ". ($m2-$m1) ."\n";
return $text;
}
$m1 = memory_get_usage();
$s = microtime(true);
$str1 = generateRandomString(10000000);
$e = microtime(true);
echo "create took ". ($e-$s) ." seconds\n";
echo "Number of occurances: " . substr_count($str1, '.') . "\n";
$s = microtime(true);
for ($i = 0; $i < $ITERATIONS; $i++) {
func_str(".","",$str1);
}
$e = microtime(true);
echo "remove took ". ($e-$s) ." seconds, avg: ". ($e-$s)/$ITERATIONS ."\n";
$s = microtime(true);
for ($i = 0; $i < $ITERATIONS; $i++) {
func_arr(".","",$str1);
}
$e = microtime(true);
echo "remove took ". ($e-$s) ." seconds, avg: ". ($e-$s)/$ITERATIONS ."\n";
(I wykomentowane wyjściu użycia pamięci wewnętrznej funkcji, jeśli chcesz go co możliwe usuń komentarz).
@ Blackackam, Jeśli jesteś zainteresowany, dodałem informacje o wydajności w mojej odpowiedzi. – Dekel
Taka bardzo interesująca intuicja podpowiadała mi, że prawdopodobnie macierzyste funkcje PHP na łańcuchach mogłyby działać nieco lepiej niż tablice (to prawdopodobnie ma znaczenie tylko wtedy, gdy funkcja jest zapętlana bardzo często ;-) Może wynik nie jest tak jasny w porównaniu do regexu rozwiązanie? Mogę przetestować później. – Blackbam
Regex zwykle wymaga więcej czasu do uruchomienia (a także większej ilości pamięci). Funkcja regex przez @Surberus trwała 20 M i ~ 0,03 s (dwa razy więcej niż czas funkcji łańcuchowej, nieco ponad połowę czasu funkcji tablicowej). – Dekel