Web

Как использовать str_replace, чтобы она действовала только при первом совпадении

Мне нужна версия, str_replace(), которая заменяет только первое вхождение $search в $subject. Есть ли простое решение для этого, или мне нужно использовать какое-то специальное решение?

 

Ответ 1

Решение самое обыкновенное:

$pos = strpos($haystack, $needle);

if ($pos !== false) {

    $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));

}

Довольно просто и избавляет от потери производительности при использовании регулярных выражений. Если же, вы хотите заменить последнее вхождение, просто используйте strrpos вместо strpos.

 

Ответ 2

Это можно сделать с помощью 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'); 

// выводит  '123def abcdef abcdef'

Суть заключается в необязательном четвертом параметре [Limit]. 

Из документации:

Максимально возможные замены для каждого шаблона в каждой строке темы. По умолчанию -1 (без ограничений).

 

Ответ 3

К сожалению, предыдущие ответы не совсем верны. Это пересмотр предыдущих ответов:

$pos = strpos($haystack,$needle);

if ($pos !== false) {

    $newstring = substr_replace($haystack,$replace,$pos,strlen($needle));

}

Обратите внимание на strlen($Need), вместо использования strlen($replace). Пример будет работать правильно, только если строка поиска и замены имеют одинаковую длину. Вот та же функциональность в функции с той же сигнатурой, что и встроенная в PHP str_replace:

function str_replace_first($search, $replace, $subject) {

    $pos = strpos($subject, $search);

    if ($pos !== false) {

        return substr_replace($subject, $replace, $pos, strlen($search));

    }

    return $subject;

}

Или так:

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

 

Обратите внимание на «2» в конце вместо «1». И в формате функции:

function str_replace_first($search, $replace, $subject) {

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

}

Я рассчитал время для двух функций, и первая работает в два раза быстрее, когда совпадений не найдено. Они имеют одинаковую скорость при обнаружении совпадения.

 

Ответ 4

Мне было интересно узнать, какая из функций самая быстрая, поэтому я протестировал их все. Ниже вы найдете:

  1. Полный список всех функций, которые были добавлены на эту страницу.

  2. Эталонное тестирование для каждой функции (среднее время выполнения за 10 000 запусков)

Все функции были протестированы с одинаковыми настройками:

$string   = 'OOO.OOO.OOO.S';

$search = 'OOO'; 

$replace = 'B';

Функции, которые заменяют только первое вхождение строки, получая новую строку:

  • substr_replace($string, $replace, 0, strlen($search));

[ПРИМЕР ТЕСТА]      => zombat

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000062883

[МЕДЛЕННЕЕ НА]     => FASTEST

  • replace_first($search, $replace, $string);

[ПРИМЕР ТЕСТА]     => too much php

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000073902

[МЕДЛЕННЕЕ НА]    => 17.52%

  • preg_replace($search, $replace, $string, 1);

[ПРИМЕР ТЕСТА]      => karim79

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000077519

[МЕДЛЕННЕЕ НА]     => 23.27%

  • str_replace_once($search, $replace, $string);

[ПРИМЕР ТЕСТА]      => happyhardik

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]    => 0.0000082286

[МЕДЛЕННЕЕ НА]     => 30.86%

  • str_replace_limit($search, $replace, $string, $count, 1);

[ПРИМЕР ТЕСТА]     => bfrohs - expanded renocor

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000083342

[МЕДЛЕННЕЕ НА]    => 32.54%

  • str_replace_limit($search, $replace, $string, 1);

[ПРИМЕР ТЕСТА]      => renocor

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000093116

[МЕДЛЕННЕЕ НА]     => 48.08%

  • str_replace_limit($string, $search, $replace, 1, 0);

[ПРИМЕР ТЕСТА]      => jayoaK

[OOO.OOO.OOO.S]    => B.OOO.OOO.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000093862

[МЕДЛЕННЕЕ НА]     => 49.26%

 

Функции, которые заменяют только последнее вхождение строки, получая новую строку:

  • substr_replace($string, $replace, strrpos($string, $search), strlen($search));

[ПРИМЕР ТЕСТА]     => oLinkSoftware - modified zombat

[OOO.OOO.OOO.S]   => OOO.OOO.B.S

[СРЕДНЕЕ ВРЕМЯ]  => 0.0000068083

[МЕДЛЕННЕЕ НА]    => FASTEST

  • strrev(implode(strrev($replace), explode(strrev($search), strrev($string), 2)));

[ПРИМЕР ТЕСТА]      => oLinkSoftware

[OOO.OOO.OOO.S]    => OOO.OOO.B.S

[СРЕДНЕЕ ВРЕМЯ]   => 0.0000084460

[МЕДЛЕННЕЕ НА]    => 24.05%

 

Ответ 5

К сожалению, я не знаю ни одной функции PHP, которая могла бы это сделать. Вы можете создать собственную довольно легко, например, так:

function replace_first($find, $replace, $subject) {

    // Разделяет $subject на массив из 2 элементов по $find,

    // и затем объединяет массив с помощью $replace

    return implode($replace, explode($find, $subject, 2));

}

 

Ответ 6

Я создал эту маленькую функцию, которая заменяет строку на другую строку (с учетом регистра) с ограничением, без необходимости использования Regexp.

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;

}

 

Пример использования:

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

 

Ответ 7

Например, такой код:

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

}

 

Ответ 8

Я написал функцию, которая на 100% обратно совместима с str_replace(). То есть вы можете заменить все вхождения str_replace() на str_replace_limit(), ничего не испортив, даже те варианты, которые используют массивы для $search, $replace, и/или $subject.

Функция может быть полностью независимой, если вы захотите заменить вызов функции на ($string===strval(intval(strval($string)))), но я бы не рекомендовал этого делать, поскольку valid_integer() является довольно полезной функцией при работе с целыми числами, представленными в виде строк.

Примечание: Когда это возможно, str_replace_limit() будет использовать str_replace(), поэтому все вызовы str_replace() можно заменить на str_replace_limit(), не беспокоясь о снижении производительности.

Применение

<?php

$search = 'a';

$replace = 'b';

$subject = 'abcabc';

$limit = -1; // без ограничения

$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);

echo $count.' replacements -- '.$new_string;

результат: 2 замены – bbcbbc

 

$limit = 1; // ограничено до «1»

$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);

echo $count.' replacements -- '.$new_string;

результат: 1 замена – bbcabc

 

$limit = 10; // ограничено до «10»

$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);

echo $count.' replacements -- '.$new_string;

результат: 2 замены -- bbcbbc

 

Непосредственно функция

<?php

/**

 * Проверяет, является ли $string действительным целым числом. Целые числа, представленные в виде строк (например, '2' против 2)

 * также поддерживаются.

 * @param mixed $string

 * @return bool Возвращает булево значение TRUE, если строка является допустимым целым числом, или FALSE, если это не так. 

 */

function valid_integer($string){

    // 1. Приведение к строке (в случае, если представлено целое число)

    // 1. Преобразование строки в целое число и обратно в строку

    // 2. Проверить, идентичны ли они (примечание: "идентичны", а не просто "равны")

    // Примечание: значения TRUE, FALSE и NULL $string возвращают FALSE

    $string = strval($string);

    return ($string===strval(intval($string)));

}

 

/**

 * Заменяет $limit повторений строки поиска строкой замены.

 * @param mixed $search Искомое значение, иначе называемое целевым значением.

 * массив может быть использован для обозначения нескольких таких значений.

 * @param mixed $replace Значение замены, которое заменяет найденные значения поиска. 

 * массив может быть использован для указания нескольких замен.

 * @param mixed $subject Строка или массив, по которым выполняется поиск и замена. Если subject является массивом, то поиск и замена

 * выполняется с каждым элементом subject, и возвращаемое значение также является массивом. 

 * @param string $count Если передано, то будет установлено количество замен,

 * @param int $limit Максимально возможное количество замен для каждого шаблона в каждой теме. По умолчанию -1 (без ограничения).

 * @return string Эта функция возвращает строку с замененными значениями.

 */

function str_replace_limit($search, $replace, $subject, &$count, $limit = -1) {

    // Установите некоторые значения по умолчанию

    $count = 0;

    // Указано неверное значение $limit. Вызов предупреждения.

    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;

    }

    // Указано неверное значение $limit. Вызов предупреждения.

    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;

    }

    // Замена не требуется. Вызовите уведомление, так как это, скорее всего, не предполагалось

    // использовать. А если и предполагалось (например, для части цикла, динамически устанавливающая $limit), то это можно 

// обойти, просто проверив, что $limit===0, и если это так, то пропустите вызов функции.

    // вызов функции (и установить $count в 0, если необходимо).

    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;

    }

    // Используйте str_replace(), когда это возможно (по соображениям производительности)

    if($limit===-1){

        return str_replace($search, $replace, $subject, $count);

    }

    if(is_array($subject)){

        // Перебираем значения $subject и вызываем эту функцию для каждого из них.

        foreach($subject as $key => $this_subject){

            // Пропустите значения, которые являются массивами (для соответствия функции str_replace()).

            if(!is_array($this_subject)){

                // Вызовите эту функцию снова

                $this_function = __FUNCTION__;

                $subject[$key] = $this_function($search, $replace, $this_subject, $this_count, $limit);

                // Корректировка $count

                $count += $this_count;

                // корректировка $limit, если не «-1»

                if($limit!=-1){

                    $limit -= $this_count;

                }

                // Достигнут $limit, возврат $subject

                if($limit===0){

                    return $subject;

                }

            }

        }

        return $subject;

    } elseif(is_array($search)){

        // Рассматривайте $replace как массив, только если $search также является массивом (для соответствия str_replace())

        // Очистите ключи $search (для соответствия str_replace()).

        $search = array_values($search);

        // Очистите ключи $replace, если необходимо(чтобы соответствовать str_replace()).

        if(is_array($replace)){

            $replace = array_values($replace);

        }

 

        // Перебираем массив $search.

        foreach($search as $key => $this_search){

            // Не поддерживаются многомерные массивы (чтобы соответствовать str_replace()).

            $this_search = strval($this_search);

            // Если $replace - массив, используйте значение $replace[$key] в качестве замены. Если

            // $replace[$key] не существует, просто пустая строка (для соответствия 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);

            }

            // Вызовите эту функцию снова

            $this_function = __FUNCTION__;

            $subject = $this_function($this_search, $this_replace, $subject, $this_count, $limit);

            // корректировка $count

            $count += $this_count;

            // корректировка $limit, если не «-1»

            if($limit!=-1){

                $limit -= $this_count;

            }

            // достигнут $limit, возврат $subject

            if($limit===0){

                return $subject;

            }

        }

        return $subject;

 

    } else {

        $search = strval($search);

        $replace = strval($replace);

        // получение позиции первого вхождения $search

        $pos = strpos($subject, $search);

        // возврат $subject, если $search не найден

        if($pos===false){

            return $subject;

        }

        // Получение длины $search, чтобы в дальнейшем произвести правильную замену

        $search_len = strlen($search);

        // Цикл до тех пор, пока $search больше не будет найден, или пока не будет достигнут $limit

        for($i=0;(($i<$limit)||($limit===-1));$i++){

            // замена 

            $subject = substr_replace($subject, $replace, $pos, $search_len);

            // инкремент $count

            $count++;

            // получение следующей позиции $search

            $pos = strpos($subject, $search);

            // Выйти из цикла, если $needle

            if($pos===false) {

                break;

            }

        }

        // возврат нового $subject

        return $subject;

    }

}

 

 

Схожие статьи

Web

Где PHP хранит журнал ошибок?

Web

Есть ли в PHP потоки

Web

Как удалить все непечатаемые символы в строке

Web

Как создать сервер веб-сокетов на PHP

×