Використовуєте str_replace, щоб він діяв лише на першому матчі?


325

Я хочу, щоб версія, str_replace()що замінює лише перше виникнення $searchв $subject. Чи є просте рішення цього питання, чи мені потрібно хакітське рішення?


Можливо, ви знайдете s($subject)->replaceFirst($search)і s($subject)->replaceFirstIgnoreCase($search)корисні, як це знайдено в цій самостійній бібліотеці .
каре

Відповіді:


346

Це можна зробити за допомогою preg_replace :

function str_replace_first($from, $to, $content)
{
    $from = '/'.preg_quote($from, '/').'/';

    return preg_replace($from, $to, $content, 1);
}

echo str_replace_first('abc', '123', 'abcdef abcdef abcdef'); 
// outputs '123def abcdef abcdef'

Магія полягає у необов'язковому четвертому параметрі [Limit]. З документації:

[Обмеження] - максимально можливі заміни для кожного шаблону в кожному предметному рядку. За замовчуванням до -1 (без обмеження).


Хоча дивіться відповідь зомба для більш ефективного методу (приблизно, у 3-4 рази швидше).


39
Мінусом цього методу є виконання покарань регулярних виразів.
zombat

27
Ще одним недоліком є ​​те, що ви повинні використовувати preg_quote () на "голку" та уникнути мета-символів $ та \ в заміну.
Джош Девіс

32
Це не вдається як загальне рішення через неприємні проблеми.
Джеремі Кауффман

2
Занадто часто регулярні вирази відхиляються через "performance", якби виконання було головним питанням, ми б не писали PHP! Для обгортання шаблону може бути використане щось інше, ніж '/', можливо, '~', що допоможе певною мірою уникнути проблеми, що випливає. Це залежить від того, які дані є, і звідки вони надійшли.
ThomasRedstone

1
Недоліки продуктивності в бік - чи мають ті, хто скаржиться на проблеми, що уникнути проблеми, маючи на увазі щось, окрім потенційних помилок preg_quote? Наприклад, @ThomasRedstone побоюється, що роздільник /може бути небезпечним, якщо він з'явиться в $from, але, на щастя, це не так: він належним чином вийшов через preg_quoteдругий параметр 's (це можна легко перевірити). Мені буде цікаво дізнатися про конкретні проблеми (які могли б бути серйозними помилками безпеки PCRE в моїй книзі).
MvanGeest

610

Немає його версії, але рішення зовсім не хакі.

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

Досить просто та економить штраф за регулярні вирази.


Бонус: Якщо ви хочете замінити останнє явище, просто використовуйте strrposзамість strpos.


17
Може бути набагато швидшим і використовуватиме менше пам'яті, ніж звичайні вирази. Поняття не маю, чому хтось проголосував би за таке ...
Джош Девіс

12
Мені подобається такий підхід, але в коді є помилка, останній параметр виклику substr_replace повинен бути strlen ($ голка) замість strlen ($ замінити) .. будь ласка, будьте про це !!
Нельсон

Це "хакі" в тому сенсі, що потрібно значно більше часу, щоб з'ясувати, що відбувається. Крім того, якби це був чіткий код, не було б зазначено, що в коді є помилка. Якщо в такому маленькому фрагменті можливо помилитися, це вже занадто хакі.
Каміло Мартін

9
Я не згоден з @CamiloMartin щодо кількості рядків проти можливості помилок. Хоча substr_replaceце є дещо громіздкою функцією, яка використовується завдяки всім параметрам, справжня проблема полягає в тому, що виконувати обробку рядків цифрами іноді досить складно - потрібно бути обережним, щоб передати правильну змінну / зміщення функції. Я фактично пішов би так далеко, щоб сказати, що наведений вище код є найпростішим, і, на мій погляд, логічним підходом.
Олексій

1
Блискучий підхід. Відмінно працює при заміні змінних значень, які мають в них зарезервовані символи регулярних виразів (тому preg_replace - ведмідь) Це прямо і елегантно.
Praesagus

96

Редагувати: обидві відповіді оновлено і тепер правильні. Я залишу відповідь, оскільки терміни функціонування все ще корисні.

На жаль, відповіді "zombat" і "too much php", на жаль, невірні. Це перегляд опублікованого відповіді zombat (оскільки мені не вистачає репутації, щоб розмістити коментар):

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

Зверніть увагу на strlen ($ ігла), а не strlen ($ заміна). Приклад Zombat буде правильно працювати лише в тому випадку, якщо голка та заміна однакової довжини.

Ось такий же функціонал у функції з тією ж підписом, що і власний str_replace PHP:

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

Це переглянута відповідь "занадто багато php":

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

Зверніть увагу на 2 в кінці замість 1. Або у форматі функції:

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

Я приурочив дві функції, і перша вдвічі швидша, коли не знайдено відповідності. Вони мають однакову швидкість, коли знайдено відповідність.


Чому б не узагальнити таке: str_replace_f гибкий (змішаний $ s, змішаний $ r, int $ offset, int $ limit), де функція замінює $ limit події, починаючи з $ offset (nth) відповідності.
Адам Фрідман

Дуже погано це стосується лише чутливих до регістру замін.
andreszs

4
@Andrew stripos()на допомогу :-)
Гра" подвійний

76

Мені було цікаво, хто з них найшвидший, тому я перевірив їх усіх.

Нижче ви знайдете:

  • Повний список усіх функцій, що були внесені на цю сторінку
  • Бенчмарк тестування для кожного складання (середній час виконання понад 10 000 запусків)
  • Посилання на кожну відповідь (для повного коду)

Усі функції були протестовані з однаковими налаштуваннями:

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

Функції, які замінюють лише перше виникнення рядка в рядку:


Функції, які замінюють лише останнє виникнення рядка в рядку:


Дякую за це, я, як правило, використовую preg_replace, оскільки він є найбільш гнучким, якщо потрібна майбутня настройка в більшості випадків на 27% повільніше не буде значущою
zzapper

@oLinkWebDevelopment Мені було б цікаво переглянути ваш базовий сценарій. Я думаю, що це може виявитися корисним.
Дейв Мортон

Причина, чому substr_replace()виграє результат, проста; тому що це внутрішня функція. Дві внутрішні та визначені користувачем функції відрізняються між собою за продуктивністю, оскільки внутрішня функціонує в нижчих шарах. Отже, чому б і ні preg_match()? Регулярні вирази майже повільніші, ніж кожна функція маніпуляції внутрішнім рядком, через їх націю шукати в рядку кілька разів.
MAChitgarha

1
Я сподіваюся, що тест про вашого "переможця" ( substr_replace($string, $replace, 0, strlen($search));) не просто написав цю статику 0. Частина згортки нерегекс-рішень полягає в тому, що їм потрібно «знайти» вихідну точку, перш ніж знати, де її замінити.
mickmackusa

55

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

function replace_first($find, $replace, $subject) {
    // stolen from the comments at PHP.net/str_replace
    // Splits $subject into an array of 2 items by $find,
    // and then joins the array with $replace
    return implode($replace, explode($find, $subject, 2));
}

Я думаю, що це найголовніша версія їх усіх - joinзамість цього implode.
Тит

return implode($replace, explode($find, $subject, $limit+1));для користувацьких номерів заміни
beppe9000

7

Я створив цю маленьку функцію, яка замінює рядок на рядку (залежно від регістру) лімітом, без необхідності 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;
// bar wizard makes bar brew for evil foo and jack

Хоча я б скоріше зробив ===falseзамість того, is_bool(щоб бути більш чітким - я віддаю цей великий палець лише тому, що він уникнув божевілля RegExp ! ... і в той же час це робоче і чисте рішення ...
jave.web

Віддати перевагу легко настроюваному preg_рішенню - це не божевілля, а особисті переваги. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);досить простий для читання людям, які не бояться регулярних виразів. Вам потрібен невідчутливий регістр пошуку? Додайте iпісля кінця роздільник візерунка. Потрібна підтримка unicode / multibyte? Додайте uпісля кінця роздільник візерунка. Вам потрібна підтримка межових слів? Додайте \bз обох сторін пошукової рядок. Якщо ви не хочете регулярного вираження, не використовуйте регулярний вираз. Коні на курси, але точно не божевілля.
mickmackusa

3

Найпростішим способом було б використання регулярного вираження.

Інший спосіб - знайти позицію рядка з strpos (), а потім substr_replace ()

Але я б дійсно пішов на RegExp.


Цей "натяк" є досить невиразним / низьким значенням порівняно з іншими публікаціями на цій сторінці.
mickmackusa

3
function str_replace_once($search, $replace, $subject) {
    $pos = strpos($subject, $search);
    if ($pos === false) {
        return $subject;
    }

    return substr($subject, 0, $pos) . $replace . substr($subject, $pos + strlen($search));
}

Відповіді, що стосуються лише коду, мають низьку цінність у StackOverflow, оскільки вони погано працюють з навчанням / розширенням можливостей тисяч майбутніх дослідників.
mickmackusa

3

=> КОД було переглянуто, тому вважайте деякі коментарі занадто старими

І дякую всім за те, що допомогли мені покращити це

Будь-яка помилка, будь ласка, повідомте мене; Я виправлю це відразу після

Отже, давайте:

Заміна першого "o" на "ea", наприклад:

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

//output: I leave you

Функція:

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

Не вдасться, якщо $ це повторило такі символи, як aaa vs aaaaaaaaa
Cristo

Я думаю, що це має бути substr($where,$b+strlen($this)), ні substr($where,$b+1). І я думаю, що substr_replaceце швидше.
Тит

Кодекс було переглянуто, тепер він працює навіть для довгих рядків
PYK,

Це рішення не працює як закодоване. Доведення: 3v4l.org/cMeZj І коли ви виправляєте проблему з іменуванням змінної, вона не працює, коли значення пошуку не знайдено - це пошкоджує рядок введення. Доказ: 3v4l.org/XHtfc
mickmackusa

Чи справедливо, коли хтось запитує виправити код? @mickmackusa Чи можете ви це ще раз перевірити?
PYK

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

Це точно так само, як перша відповідь. Крім того, ви повинні зробити preg_quoteз , $findперш ніж використовувати його в якості вираження.
Еміль Вікстрьом

це те, що я використав, тож я проголосував за це. Перша відповідь викликала конфлікт з Drupal, він, мабуть, переписав функцію помічника друпала. Тож я просто взяв код, який знаходився у функції, і використав його відповідно до решти коду ...
Dan Mantyla,

Ця відповідь, кодована лише за допомогою коду, надає зайві поради на сторінці (не кажучи вже про її відсутність preg_quote(). Цю пізню дублюючу відповідь можна сміливо видалити зі сторінки, оскільки її поради надаються попередньою та більш високоприйнятою прийнятою відповіддю.
mickmackusa

2

Щоб розширити відповідь на @ renocor , я написав функцію, яка на 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; // No limit
$new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
echo $count.' replacements -- '.$new_string;

2 заміни - bbcbbc

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

1 заміна - bbcabc

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

2 заміни - bbcbbc

Функція

<?php

/**
 * Checks if $string is a valid integer. Integers provided as strings (e.g. '2' vs 2)
 * are also supported.
 * @param mixed $string
 * @return bool Returns boolean TRUE if string is a valid integer, or FALSE if it is not 
 */
function valid_integer($string){
    // 1. Cast as string (in case integer is provided)
    // 1. Convert the string to an integer and back to a string
    // 2. Check if identical (note: 'identical', NOT just 'equal')
    // Note: TRUE, FALSE, and NULL $string values all return FALSE
    $string = strval($string);
    return ($string===strval(intval($string)));
}

/**
 * Replace $limit occurences of the search string with the replacement string
 * @param mixed $search The value being searched for, otherwise known as the needle. An
 * array may be used to designate multiple needles.
 * @param mixed $replace The replacement value that replaces found search values. An
 * array may be used to designate multiple replacements.
 * @param mixed $subject The string or array being searched and replaced on, otherwise
 * known as the haystack. If subject is an array, then the search and replace is
 * performed with every entry of subject, and the return value is an array as well. 
 * @param string $count If passed, this will be set to the number of replacements
 * performed.
 * @param int $limit The maximum possible replacements for each pattern in each subject
 * string. Defaults to -1 (no limit).
 * @return string This function returns a string with the replaced values.
 */
function str_replace_limit(
        $search,
        $replace,
        $subject,
        &$count,
        $limit = -1
    ){

    // Set some defaults
    $count = 0;

    // Invalid $limit provided. Throw a warning.
    if(!valid_integer($limit)){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting an '.
                'integer', E_USER_WARNING);
        return $subject;
    }

    // Invalid $limit provided. Throw a warning.
    if($limit<-1){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_WARNING);
        return $subject;
    }

    // No replacements necessary. Throw a notice as this was most likely not the intended
    // use. And, if it was (e.g. part of a loop, setting $limit dynamically), it can be
    // worked around by simply checking to see if $limit===0, and if it does, skip the
    // function call (and set $count to 0, if applicable).
    if($limit===0){
        $backtrace = debug_backtrace();
        trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                'a positive integer', E_USER_NOTICE);
        return $subject;
    }

    // Use str_replace() whenever possible (for performance reasons)
    if($limit===-1){
        return str_replace($search, $replace, $subject, $count);
    }

    if(is_array($subject)){

        // Loop through $subject values and call this function for each one.
        foreach($subject as $key => $this_subject){

            // Skip values that are arrays (to match str_replace()).
            if(!is_array($this_subject)){

                // Call this function again for
                $this_function = __FUNCTION__;
                $subject[$key] = $this_function(
                        $search,
                        $replace,
                        $this_subject,
                        $this_count,
                        $limit
                );

                // Adjust $count
                $count += $this_count;

                // Adjust $limit, if not -1
                if($limit!=-1){
                    $limit -= $this_count;
                }

                // Reached $limit, return $subject
                if($limit===0){
                    return $subject;
                }

            }

        }

        return $subject;

    } elseif(is_array($search)){
        // Only treat $replace as an array if $search is also an array (to match str_replace())

        // Clear keys of $search (to match str_replace()).
        $search = array_values($search);

        // Clear keys of $replace, if applicable (to match str_replace()).
        if(is_array($replace)){
            $replace = array_values($replace);
        }

        // Loop through $search array.
        foreach($search as $key => $this_search){

            // Don't support multi-dimensional arrays (to match str_replace()).
            $this_search = strval($this_search);

            // If $replace is an array, use the value of $replace[$key] as the replacement. If
            // $replace[$key] doesn't exist, just an empty string (to match str_replace()).
            if(is_array($replace)){
                if(array_key_exists($key, $replace)){
                    $this_replace = strval($replace[$key]);
                } else {
                    $this_replace = '';
                }
            } else {
                $this_replace = strval($replace);
            }

            // Call this function again for
            $this_function = __FUNCTION__;
            $subject = $this_function(
                    $this_search,
                    $this_replace,
                    $subject,
                    $this_count,
                    $limit
            );

            // Adjust $count
            $count += $this_count;

            // Adjust $limit, if not -1
            if($limit!=-1){
                $limit -= $this_count;
            }

            // Reached $limit, return $subject
            if($limit===0){
                return $subject;
            }

        }

        return $subject;

    } else {
        $search = strval($search);
        $replace = strval($replace);

        // Get position of first $search
        $pos = strpos($subject, $search);

        // Return $subject if $search cannot be found
        if($pos===false){
            return $subject;
        }

        // Get length of $search, to make proper replacement later on
        $search_len = strlen($search);

        // Loop until $search can no longer be found, or $limit is reached
        for($i=0;(($i<$limit)||($limit===-1));$i++){

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

            // Increase $count
            $count++;

            // Get location of next $search
            $pos = strpos($subject, $search);

            // Break out of loop if $needle
            if($pos===false){
                break;
            }

        }

        // Return new $subject
        return $subject;

    }

}

4
якось роздутий, якщо ви запитаєте мене. Крім того, що я найбільше «ненавиджу» у цьому рішенні, - це обробка помилок. Він порушує сценарій, якщо ви передаєте неправильні значення. Ви думаєте, що це виглядає професійно, але це не так, замість помилки надсилайте повідомлення або попередження. Краще - пропустити фігня, повернути помилкове замість цього чи нулеве і ніколи не використовувати зворотний слід у такій функції. Найкраще рішення - програміст може вирішити, що робити, коли вихід невірний / несподіваний.
Codebeat

@Erwinus цьому використовується E_USER_WARNINGпід всім , що є попередженням , НЕ помилка . Зворотний трек надзвичайно корисний для з'ясування того, який код передає недійсні дані в першу чергу функції (що абсолютно необхідно для відстеження помилок у виробництві). Що стосується повернення $subjectзамість false/ nullабо кидання помилки, це був просто особистий вибір для мого випадку використання. Для відповідності str_replace()функціональності найкраще скористатись уловлюваними фатальними помилками (як str_replace()це робиться при закритті перших двох аргументів).
0b10011

Ах, не помітили про E_USER_WARNING, який ви використовуєте, вибачте за це. Проблема з поверненням теми полягає в тому, що ви ніколи не можете побачити, що було щось не так, поза функцією. Однак, функція може бути вдвічі меншою, якщо робити її розумнішою (можливо). По-друге, коментарі чудові, коли це пояснює щось складне, але не дуже корисне для простих речей, таких як збільшення значення. В цілому я вважаю, що це зайве величезне. Крім того, використання попереджень у виробничому середовищі може бути проблемою безпеки, коли ви використовуєте цей код на сервері, який не пригнічує повідомлення під час запуску повідомлень за замовчуванням (журнали).
Codebeat

@Erwinus Я був багатослівним, коли говорив про коментарі, оскільки деякі люди не розуміють мови так само добре, як інші, а коментарі завжди можуть бути видалені тими, хто її розуміє. Якщо ви знаєте кращий спосіб отримати однаковий кінцевий результат для всіх крайових випадків, будь-яким чином відредагуйте відповідь. І якщо ваше виробниче середовище не пригнічує повідомлення про помилки, у вас є більша проблема, ніж ця функція;)
0b10011

TL; DR Цей фрагмент настільки роздутий, що я не уявляю, як вибрати його за допомогою функції регулярних виразів (ненавиджу прокрутку). Якщо ви хочете порахувати зроблені заміни, є параметр для цього в preg_replace(). Крім того, preg_replace()/ regex пропонує обробку меж слів (якщо бажано) - те, що не-регулярні функції не забезпечують елегантності.
mickmackusa

2

Відповідно до результатів тесту, я хотів би проголосувати за регулярний_вираз, який надає karim79. (У мене зараз недостатньо репутації, щоб проголосувати за це!)

Рішення від zombat використовує занадто багато функціональних викликів, я навіть спрощую коди. Я використовую PHP 5.4 для запуску обох рішень в 100 000 разів, і ось результат:

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

==> 1,85 сек

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

==> 1,35 сек

Як ви можете бачити. Продуктивність preg_replace не така вже й погана, як багато хто думає. Тому я б запропонував стильне рішення, якщо ваш регулярний експрес не є складним.


Ваш перший фрагмент - несправедливе порівняння, оскільки він не використовує правильну реалізацію. Ви не перевіряти $posна false, так що, коли голка не існує в стозі сіна, це зашкодить вихід.
mickmackusa

Дякую @mickmackusa, ти маєш рацію. Але це не сенс. Я сказав, що цей код спрощений просто для порівняння ефективності реалізації.
Мисливець Ву

Це саме моя думка. Ніколи не слід робити порівняльні показники, які не виконують точно той самий процес. Порівнювати яблука з напів-апельсинами не корисно. Повна реалізація повного підходу без повторного виведення зробить різницю швидкості більш глибокою.
mickmackusa

Ну, ще раз дякую. Але те, що я хочу, - знайти кращу реалізацію, а не робити більш глибоку різницю.
Мисливець Ву

2

Щоб розширити відповідь на зомбат (що я вважаю найкращою відповіддю), я створив рекурсивну версію його функції, яка бере $limitпараметр, щоб вказати, скільки подій потрібно замінити.

function str_replace_limit($haystack, $needle, $replace, $limit, $start_pos = 0) {
    if ($limit <= 0) {
        return $haystack;
    } else {
        $pos = strpos($haystack,$needle,$start_pos);
        if ($pos !== false) {
            $newstring = substr_replace($haystack, $replace, $pos, strlen($needle));
            return str_replace_limit($newstring, $needle, $replace, $limit-1, $pos+strlen($replace));
        } else {
            return $haystack;
        }
    }
}

Зверніть увагу, що немає ніякої перевірки якості на $start_pos, тому , якщо він знаходиться за межами довжини рядка, то ця функція буде генерувати: Warning: strpos(): Offset not contained in string.... Ця функція не може зробити заміну, якщо $start_posвона перевищує довжину. Доказ відмови: 3v4l.org/qGuVIR ... Ваша функція може поєднувати return $haystackумови та уникати декларування змінних для одноразового використання, як це: 3v4l.org/Kdmqp Однак, як я вже говорив у коментарях в інших місцях на цій сторінці, я вважаю за краще використовувати дуже чистий, прямий, нерекурсивний preg_replace()дзвінок.
mickmackusa

да так , що ви можете додати цей рядок elseПостулати$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A

2

Для струни

$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

Для одного персонажа

$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

Перший фрагмент substr_replace () виходить з ладу, коли рядок пошуку не зміщено 0 вхідного рядка. Підтвердження відмови: 3v4l.org/oIbRv І обидва substr_replace()методи пошкоджують вхідний рядок, коли значення пошуку немає. Доказ відмови: 3v4l.org/HmEml (І ця остання техніка з усіма revдзвінками серйозно перекручена / важко на очах.)
mickmackusa

2

Доповнюючи сказане людьми, пам’ятайте, що весь рядок - це масив:

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

$string[0] = "B";

echo $string;

"Borem ipsum lá lá lá"


3
Якщо він не містить багатобайтових символів ... і тоді ваша техніка виходить з ладу. Як прикро, що вам запропонували зразок вхідного рядка, що містить á. Демонстрація невдачі
mickmackusa

Ви можете перевірити, чи stringє ваш багатобайтовий рядок, використовуючиmb_strlen($subject) != strlen($subject)
RousseauAlexandre

Ця публікація не намагається відповісти на запитання, яке задають.
mickmackusa

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

Використовуючи substr_replace, ми можемо замінити виникнення першого символу лише в рядку. як & повторюється кілька разів, але лише на першій позиції нам потрібно замінити & на?


1

Ця функція сильно натхненна відповіддю @renocor. Це робить функцію багатобайтової безпечною.

function str_replace_limit($search, $replace, $string, $limit)
{
    $i = 0;
    $searchLength = mb_strlen($search);

    while(($pos = mb_strpos($string, $search)) !== false && $i < $limit)
    {
        $string = mb_substr_replace($string, $replace, $pos, $searchLength);
        $i += 1;
    }

    return $string;
}

function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
{
    $string = (array)$string;
    $encoding = is_null($encoding) ? mb_internal_encoding() : $encoding;
    $length = is_null($length) ? mb_strlen($string) - $start : $length;

    $string = array_map(function($str) use ($replacement, $start, $length, $encoding){

        $begin = mb_substr($str, 0, $start, $encoding);
        $end = mb_substr($str, ($start + $length), mb_strlen($str), $encoding);

        return $begin . $replacement . $end;

    }, $string);

    return ( count($string) === 1 ) ? $string[0] : $string;
}

0

Ви можете скористатися цим:

function str_replace_once($str_pattern, $str_replacement, $string){ 

        if (strpos($string, $str_pattern) !== false){ 
            $occurrence = strpos($string, $str_pattern); 
            return substr_replace($string, $str_replacement, strpos($string, $str_pattern), strlen($str_pattern)); 
        } 

        return $string; 
    } 

Знайшов цей приклад із php.net

Використання:

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

Вихід:

ThiZ iz an examplz

Це може трохи зменшити продуктивність, але найпростіше рішення.


Якщо це вихід, ніж то, в чому суть? Чи не слід це замінити лише першим малим "z" на велике "Z"? Замість того, щоб замінити їх усіх? Я думав, що про це ми говорили тут ...
Поворот

Моє погано, це замінить лише перше виникнення. Відредаговано.
happyhardik

Цю саму пораду Бас вже запропонував майже на 3 роки раніше (і без зайвого заклику strpos()). Захищений, оскільки він не додає нової цінності на сторінку.
mickmackusa

0

Якщо рядок не містить багатобайтових символів, і якщо ви хочете замінити лише один символ, ви можете просто використовувати strpos

Тут функція, яка обробляє помилки

/**
 * Replace the first occurence of given string
 *
 * @param  string $search  a char to search in `$subject`
 * @param  string $replace a char to replace in `$subject`
 * @param  string $subject
 * @return string
 *
 * @throws InvalidArgumentException if `$search` or `$replace` are invalid or if `$subject` is a multibytes string
 */
function str_replace_first(string $search , string $replace , string $subject) : string {
    // check params
    if(strlen($replace) != 1 || strlen($search) != 1) {
        throw new InvalidArgumentException('$search & $replace must be char');
    }elseif(mb_strlen($subject) != strlen($subject)){
        throw new InvalidArgumentException('$subject is an multibytes string');
    }
    // search 
    $pos = strpos($subject, $search);
    if($pos === false) {
        // not found
        return $subject;
    }

    // replace
    $subject[$replace] = $subject;

    return $subject;
}

0

Для петлевого рішення

<?php
echo replaceFirstMatchedChar("&", "?", "/property/details&id=202&test=123#tab-6");

function replaceFirstMatchedChar($searchChar, $replaceChar, $str)
{
    for ($i = 0; $i < strlen($str); $i++) {

        if ($str[$i] == $searchChar) {
            $str[$i] = $replaceChar;
            break;
        }
    }
    return $str;
}

-1

Ось простий клас, який я створив, щоб обернути наші трохи змінені функції str_replace () .

Наша функція php :: str_rreplace () також дозволяє здійснити зворотний, обмежений str_replace (), який може бути дуже зручним при спробі замінити лише остаточний екземпляр X рядка.

Обидва ці приклади використовують preg_replace () .

<?php
class php {

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_replace($find, $replace, $subject, $replacement_limit = -1) {
        $find_pattern = str_replace('/', '\/', $find);
        return preg_replace('/' . $find_pattern . '/', $replace, $subject, $replacement_limit);
    }

    /**
    * str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
    *
    * @param string   $find
    * @param string   $replace
    * @param string   $subject
    * @param int      $replacement_limit | -1 to replace all references
    *
    * @return string
    */
    public static function str_rreplace($find, $replace, $subject, $replacement_limit = -1) {
        return strrev( self::str_replace(strrev($find), strrev($replace), strrev($subject), $replacement_limit) );
    }
}

Ваша публікація не додає значення цьому вже на насиченій сторінці. Рішення для регулярних виразів не вдається на багатьох випадках із бахромою, оскільки ви використовували неправильний інструмент для втечі символів у рядку голки. Підтвердження невдачі: 3v4l.org/dTdYK Сильно схвалена та прийнята відповідь з 2009 року вже свідчить про належне виконання цієї методики. Ваш другий метод не відповідає на поставлене запитання, і його вже надав oLinkWebDevelopment.
mickmackusa

-1
$str = "Hello there folks!"
$str_ex = explode("there, $str, 2);   //explodes $string just twice
                                      //outputs: array ("Hello ", " folks")
$str_final = implode("", $str_ex);    // glues above array together
                                      // outputs: str("Hello  folks")

Є ще один додатковий простір, але це не має значення, як це було для зворотного сценарію в моєму випадку.


Ця методика була надана toomuchphp ще в 2009 році ! Я відмовився, оскільки ваша публікація не додає нової цінності дослідникам. Перш ніж публікувати відповідь, переконайтесь, що ваше рішення є унікальним для сторінки та додає цінність для сторінки.
mickmackusa

-3

це моя перша відповідь тут, я сподіваюсь це зробити правильно. Чому б не використати для цієї проблеми четвертий аргумент функції str_replace?

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

кількість: Якщо це буде зроблено, це буде встановлено кількість виконаних замін.


редагувати: Ця відповідь неправильна, тому що 4-й параметр str_replace є змінною, якій присвоюється кількість виконаних замін. Це суперечить preg_replace , який має 4-й параметр $limitі 5-й параметр &$count.


Четвертий аргумент буде встановлений str_replace () до кількості виконаних замін. Ось чому ви отримуєте помилку, коли передаєте ціле число, а не змінну до нього.
arminrosu

-6

Неважко знайти рішення замінити лише першу чи першу пару примірників (давши значення підрахунку). Існує не так багато рішень для заміни останньої або останньої пари примірників.

Можливо, щось на зразок str_replace ($ find, $ substitu, $ subject, -3) має замінити останні три екземпляри.

У всякому разі, просто пропозиція.


4
Навіщо відповідати на запитання з пропозицією, коли відповідь була прийнята за два роки до цього ?!
мбінет

Також -3 не працюватиме як параметр, оскільки 4-й параметр є вихідним, а не вхідним параметром. Було б краще, якщо ви протестуєте те, що пропонуєте, замість того, щоб розміщувати код, який виходить з ладу.
Ghostwriter78

Так, це неправильно, однак, чому я отримую збій із порожнім екраном, коли я його пробую? Я зробив звичайне повідомлення про помилки (E_ALL); ini_set ("display_errors", 1); Помилка порожнього екрана.
Даг Кассіді
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.