str_replaceを使用して、最初の一致に対してのみ機能するようにしますか?


325

私は、バージョンの欲しいstr_replace()ものが唯一の最初の発生置き換え$searchでは$subject。これに対する簡単な解決策はありますか、それともハックな解決策が必要ですか?


回答:


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'

魔法はオプションの4番目のパラメーター[制限]にあります。ドキュメントから:

[制限]-各対象文字列の各パターンの可能な最大置換。デフォルトは-1(制限なし)です。


ただし、より効率的な方法については、zombatの回答を参照してください(およそ3〜4倍高速)。


39
この方法の欠点は、正規表現によるパフォーマンスの低下です。
zombat 2009

27
もう1つの欠点は、「針」でpreg_quote()を使用し、メタ文字$および\をエスケープして置換する必要があることです。
ジョシュデイビス

32
これは厄介なエスケープ問題のため、一般的なソリューションとして失敗します。
Jeremy Kauffman、2011

2
「パフォーマンス」が原因で正規表現が却下されることがあまりにも多く、パフォーマンスが主な関心事である場合は、PHPを作成しません。'/'以外の何かを使用してパターンをラップできます。これは、エスケープの問題をある程度回避するのに役立ちます。それは、データが何であり、どこから来たかに依存します。
ThomasRedstone 2015

1
パフォーマンスのマイナス面はさておき-エスケープの問題について不満を言う人は、潜在的なバグ以外に何か特別なことを考えていpreg_quoteますか?たとえば、@ ThomasRedstoneは、区切り文字/がに表示された場合に危険である可能性があることを心配していますが$from、幸いそれはそうではありませんpreg_quote。特定の問題(私の本ではPCREの重大なセキュリティバグとなる)について聞きたいです。
MvanGeest 2017年

611

それのバージョンはありませんが、解決策はまったくハッキーではありません。

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

かなり簡単で、正規表現によるパフォーマンスの低下を防ぎます。


おまけ:最後のオカレンスを置き換える場合はstrrpos、の代わりに使用しstrposます。


17
正規表現よりもはるかに高速でメモリ使用量が少なくなります。なぜ誰かが反対票を投じるのかわからない...
ジョシュデイビス

12
私はこのアプローチが好きですが、コードにエラーがあります。substr_replace呼び出しの最後のパラメーターは、strlen($ replace)ではなくstrlen($ needle)にする必要があります。
ネルソン

何が起こっているのかを理解するのにかなり長い時間がかかるという意味で「ハッキー」です。また、それが明確なコードである場合、コードにエラーがあることは言及されていません。このような小さなスニペットで間違いを犯す可能性がある場合、すでにハックされすぎています。
Camilo Martin

9
行数と間違いの可能性の点で@CamiloMartinに同意しません。一方でsubstr_replace、すべてのパラメータのために使用するために、やや扱いにくい関数であり、本当の問題は、数字で文字列操作を行うことがちょうどであるということですトリッキー時々 -あなたは関数にオフセット右の変数を/渡すことに注意する必要があります。実際には、上記のコードが最も簡単で、私にとっては論理的なアプローチであるとまで言っています。
アレックス

1
素晴らしいアプローチ。regex文字が予約されている変数値を置き換えるときに完全に機能します(したがって、preg_replaceはbearです)。これは簡単でエレガントです。
Praesagus 2014年

96

編集:両方の回答が更新され、現在は正しいです。関数のタイミングはまだ有用なので、私は答えを残しておきます。

「zombat」および「too much php」による回答は、残念ながら正しくありません。これはzombatが投稿した回答の改訂版です(コメントを投稿するのに十分な評判がないため):

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

strlen($ replace)ではなく、strlen($ needle)に注意してください。Zombatの例は、needleと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;
}

これは「多すぎるphp」の修正された回答です。

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

1ではなく最後の2に注意してください。または関数形式で:

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

2つの関数の時間を計ったところ、最初の関数は一致が見つからない場合の2倍の速さでした。一致が見つかったときの速度は同じです。


str_replace_flexible(mixed $ s、mixed $ r、int $ offset、int $ limit)のように一般化しないでください。この関数は、$ offset(n番目)の一致で始まる$ limitオカレンスを置き換えます。
Adam Friedman

残念ながら、これは大文字と小文字を区別する置換にのみ適用されます。
andreszs 2015年

4
@Andrew stripos()が救助に:-)
Gras Double

76

どれが最速かと思ったので、すべてテストしました。

以下をご覧ください:

  • このページに提供されているすべての機能の包括的なリスト
  • 各寄与のベンチマークテスト(10,000実行を超える平均実行時間)
  • 各回答へのリンク(完全なコード用)

すべての機能は同じ設定でテストされました:

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

文字列内で最初に出現する文字列のみを置き換える関数:


文字列内で最後に出現する文字列のみを置き換える関数:


これのおかげで、通常はpreg_replaceを使用します。ほとんどの場合、将来の調整が必要な場合に最も柔軟性があるため、27%遅くなることは重要ではありません
zzapper

@oLinkWebDevelopmentベンチマークスクリプトの確認に興味があります。私はそれが役に立つことが証明できると思います。
Dave Morton

理由substr_replace();結果は単純で勝利 内部関数だからです。2つの同じ機能を実行する内部関数とユーザー定義関数は、内部関数が下位層で実行されるため、パフォーマンスが異なります。では、なぜpreg_match()でしょうか。正規表現は、文字列を複数回検索する国があるため、すべての内部文字列操作関数よりもほとんど遅くなります。
MAChitgarha

1
あなたの「勝者」(substr_replace($string, $replace, 0, strlen($search));)のベンチマークが単にstaticと書いていないことを願っています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

もっと明示的===falseis_bool(する代わりに、むしろそうしたいのですが、RegExpの狂気を回避したという理由だけで、私はこれをあきらめています。...そして、それは機能し、クリーンなソリューションです...
jave.web

簡単にカスタマイズできるpreg_ソリューションを好むことは、狂気ではなく個人的な好みです。 return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);正規表現を恐れない人のために読むのはかなり簡単です。大文字と小文字を区別しない検索が必要ですか?i終了パターン区切り文字の後に追加します。Unicode /マルチバイトのサポートが必要ですか?u終了パターン区切り文字の後に追加します。単語境界のサポートが必要ですか?\b検索文字列の両側に追加します。正規表現が不要な場合は、正規表現を使用しないでください。コース用の馬ですが、確かに狂気ではありません。
mickmackusa

3

最も簡単な方法は、正規表現を使用することです。

もう1つの方法は、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));
         }

$ thisがaaa対aaaaaaaaaのような文字を繰り返していると失敗します
クリスト

私はそれがあるべきだと思うsubstr($where,$b+strlen($this))、ではありませんsubstr($where,$b+1)。そして、私はそれsubstr_replaceがより速いと思います。
Titus

コードが改訂され、長い文字列でも機能するようになりました
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;

これは最初の答えと同じです。また、式として使用する前にa preg_quoteを実行する必要があります$find
EmilVikström12年

これは私が使用したものなので、私はそれを賛成票を投じました。最初の答えはDrupalとの衝突を引き起こしました。それはdrupalヘルパー関数を上書きしたに違いありません。だから私は関数の中にあるコードを取り、それをコードの他の部分とインラインで使用しました...
Dan Mantyla

このコードは、唯一の答えは、(それを言及しないようには欠けているページ上の冗長なアドバイスを提供しpreg_quote()、そのアドバイスが早く、かつ高いupvoted受け入れ答えで提供されているため、この後半重複答えは安全にページから削除することができます。。
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
あなたが私に尋ねれば、ちょっと肥大化しました。また、私がこのソリューションで最も「嫌い」なのはエラー処理です。不正な値を渡すと、スクリプトが壊れます。あなたはそれがプロフェッショナルに見えていると思いますが、そうではなく、エラーの代わりに通知または警告を生成します。でたらめをスキップし、代わりにfalseまたはnullを返し、このような関数でバックトレースを使用しない方が良いでしょう。最善の解決策は、出力が間違っている/予期しない場合にプログラマが何をすべきかを決定できることです。
コードビート2013

この用途@Erwinus E_USER_WARNINGで全体に、警告ないエラー。バックトレースは、最初に関数に無効なデータを渡しているコードを見つけるのに非常に役立ちます(本番環境のバグを追跡するために絶対に必要です)。/の$subject代わりに戻るか、またはエラーをスローすることに関しては、それは単に私のユースケースの個人的な選択でした。の機能を一致させるには、キャッチ可能な致命的なエラーを使用するのが最善の方法です(最初の2つの引数にクロージャを提供する場合と同様)。falsenullstr_replace()str_replace()
0b10011 2013

ああ、ごめんなさい、あなたが使っているE_USER_WARNINGに気づきませんでした。件名を返す際の問題は、関数の外で何か問題があったことを決して確認できないことです。とは言っても、もっと賢くすれば、機能は半分のサイズになる可能性があります(それは可能です)。第二に、コメントは、複雑なことを説明する場合は問題ありませんが、値を増やすなどの単純なことにはあまり役立ちません。全体的には不要だと思います。また、本番環境で警告を使用すると、デフォルトで実行時メッセージ(ログ)を抑制しないサーバーでこのコードを使用する場合、セキュリティ上の問題になる可能性があります。
コードビート2013

@Erwinusコメントに関しては、他の人と同じように言語を理解していない人もいるので、冗長でした。コメントを理解している人はコメントをいつでも削除できます。すべてのエッジケースで同じ最終結果を得るより良い方法がわかっている場合は、必ず回答を編集してください。
運用

TL; DRこのスニペットは肥大化しているので、正規表現関数で選択することは想像できません(スクロールは嫌いです)。行われた置換をカウントする場合は、そのためのパラメーターがありますpreg_replace()。さらに、preg_replace()/ regexは単語境界処理を提供します(望ましい場合)-非正規表現関数がエレガントに提供しないもの。
mickmackusa

2

私のテスト結果によると、私はkarim79が提供するregular_expressに投票したいと思います。(私は今それを投票するのに十分な評判がありません!)

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いないため、haystackに針が存在しない場合、出力が損傷します。
mickmackusa

ありがとう@mickmackusa、あなたは正しい。しかし、それは重要ではありません。このコードは、実装の効率を比較するためだけに簡略化されていると言いました。
ハンターWu

それがまさに私のポイントです。まったく同じプロセスを実行しないベンチマーク比較を行ってはなりません。りんごをオレンジの半分と比較しても意味がありません。完全な非正規表現アプローチを完全に実装すると、速度の違いがさらに大きくなります。
mickmackusa

ええと、ありがとう。しかし、私が欲しいのは、より良い実装を見つけることであり、より大きな違いを深めることではありません。
ハンターWu

2

zombatの答え(これが最良の答えだと思います)を拡張するために、$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

検索文字列が入力文字列のオフセット0にない場合、最初のsubstr_replace()スニペットは失敗します。失敗の証拠:3v4l.org/oIbRvそして、どちらのsubstr_replace()手法も、検索値が存在しない場合に入力文字列を損傷します。失敗の証拠:3v4l.org/HmEml (そして、すべてのrev呼び出しを伴うその最後の手法は、非常に複雑で複雑です/目には難しい。)
mickmackusa '20

2

人々が言っ​​たことを補足すると、文字列全体が配列であることを覚えておいてください:

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

$string[0] = "B";

echo $string;

「Borem ipsumlá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」に置き換えるだけではいいのではないですか?それらすべてを置き換えるのではなく?それが私たちがここで話していることだと思いました...
Swivel

私の悪いことに、それは最初の発生を置き換えるだけです。編集。
happyhardik 14

これと同じアドバイスが、Baによって3年近く前に(そしてを過度に呼び出すことなくstrpos())すでに提供されていました。ページに新しい値を追加しないため、反対票が投じられました。
mickmackusa

0

文字列にマルチバイト文字が含まれておらず、1つの文字だけを置き換えたい場合は、単純に使用できます 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年に 大きく支持され、受け入れられた回答は、すでにこの手法が適切に実行されていることを示しています。2番目の方法は、尋ねられた質問に答えず、すでに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")

追加のスペースがもう1つありますが、私の場合はバックグラウンドスクリプトの場合と同様に問題ありませんでした。


このテクニックは、2009年に toomuchphpによって提供されました。あなたの投稿が研究者に新しい価値を追加しないため、私は反対票を投じました。回答を投稿する前に、ソリューションがページに固有であり、ページに価値を追加していることを確認してください。
mickmackusa

-3

これが私の最初の答えです。正しく実行したいと思っています。この問題にstr_replace関数の4番目の引数を使用しないのはなぜですか。

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

count:渡された場合、これは実行された置換の数に設定されます。


編集:str_replaceの4番目のパラメーターは、実行された置換の数が割り当てられる変数であるため、この答えは間違っています。これは、4番目のパラメーターと5 番目のパラメーターを持つpreg_replaceと矛盾し$limitます&$count


4番目の引数は、str_replace()によって、行われた置換の数に設定されます。そのため、変数ではなく整数を渡すとエラーが発生します。
アーミンロス2014年

-6

(カウント値を指定することにより)最初または最初のカップルのみを置き換えるソリューションを見つけるのは簡単です。最後または最後の2つのインスタンスを置き換えるソリューションは多くありません。

たぶんstr_replace($ find、$ replace、$ subject、-3)のようなものが最後の3つのインスタンスを置き換えるはずです。

とにかく、単なる提案です。


4
2年前に回答が承認されたのに、なぜ提案で質問に回答するのですか?
mbinette 2012年

また、-4は入力パラメーターではなく出力パラメーターであるため、-3はパラメーターとして機能しません。クラッシュするコードを投稿するのではなく、提案するものをテストする方が良いでしょう。
Ghostwriter78 2015

ええ、これは間違っていますが、試してみると何故画面が表示されないのですか?通常のerror_reporting(E_ALL);を行いました。ini_set( "display_errors"、1); それでも空白の画面エラー。
Doug Cassidy 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.