PHP - 将字符串替换为正常,仅反转一次

I need to replace each word only once, and vice versa. To do taht, I used a code that does not work, and I can not find an answer to my question.

Input:

hello w1 w2 w12 new1 new12 new2

Expected output:

hello new1 new2 w12 w1 new12 w2

I need words / phrases into the text to replace.

w1 replace with new1

w12 unchanged

w2 replace with new2

new1 replace with w1

new12 unchanged

new2 replace with w2

Of course my text in Persian

My code is:

$string="hello w1 w2 w12 new1 new12 new2";

$fword= array("w1","w2");
$lword= array("new1","new2");

$cnt=0;
$string=str_replace($fword,$lword,$string,$cnt);
$string=str_replace($lword,$fword,$string,$cnt);
echo "<h2>Change in string: $cnt <br> New String: $string </h2>";

But it is wrong

I also use this code:

$string="hello w1 w2 w12 new1 new12 new2";

$fword= array("w1","w2","new1","new2");
$lword= array("new1","new2","w1","w2");

$cnt=0;
$string=str_replace($fword,$lword,$string,$cnt);
echo "<h2>Change in string: $cnt <br> New String: $string </h2>";

You can tokenize your string with strtok.

Then check the tokens in a reverse loop, and if the truncated token is in the allowed words list, replace it (you can have a mapping array like ["W1" => "E1", ...]). If such a word was already replaced, just go further.

I know this is lengthy one, but i have tried myself best to get it done.

PHP code demo

<?php
ini_set("display_errors", 1);

echo $string="hello w1 w2 w12 new1 new12 new2";
$fword= array("w1","w2","new1","new2");
$lword= array("new1","new2","w1","w2");

//---------Working----------->
$replacement=  array_combine($fword, $lword);
$totrimOffsets=array();
$indexes=findIndexes($fword);
$string=preg_replace("/\~\~{1,}/", "~~", $string);
$newString=replace();
//--------------------------->
echo PHP_EOL;
echo $newString;
function findIndexes($array)
{
    global $totrimOffsets,$string,$replacement;
    $indexes=array();
    foreach($array as $element)
    {
        preg_match_all("/\b$element\b/i", $string,$matches,PREG_OFFSET_CAPTURE);
        if(isset($matches[0]) && count($matches[0])>0)
        {
            foreach($matches[0] as $matchData)
            {
                $indexes[$element][]=array("element"=>$element,"offset"=>$matchData[1],"length"=>  strlen($element));
                $totrimOffsets[]=$matchData[1].",".($matchData[1]+strlen($element)-1).",".$element.",".$replacement[$element];
                $string=  substr_replace($string, getSpecialChars(strlen($element)), $matchData[1],strlen($element));
            }
        }
    }
    sort($totrimOffsets,SORT_NUMERIC);
    return $indexes;
}
function replace()
{
    global $string,$totrimOffsets,$indexes;
    $stringArray=explode("~~",$string);
    $newString="";
    foreach($stringArray as $key => $value)
    {
        $newString.=$value;
        if(isset($totrimOffsets[$key]))
        {
            $newString.=explode(",",$totrimOffsets[$key])[3];
        }
    }
    return $newString;
}
function getSpecialChars($length)
{
    $dummyString="";
    for($x=0;$x<$length;$x++)
    {
        $dummyString.="~";
    }
    return $dummyString;
}

You should use preg_replace. Tell it to check for space or string beginning or end (^|.*\s)and (\s.*|$) to avaid replacing partial matches.

$string="hello w1 w2 w12 new1 new12 new2";

$replacements = array(
    "w1" => "new1",
    "w2" => "new2",
    "new1" => "w1",
    "new2" => "w2"
);

foreach ($replacements as $from=>$to) {
    $string = preg_replace(
                '/(^|.*\s)'.preg_quote($from).'(\s.*|$)/',
                '\1'.preg_quote($to).'\2', 
                $string);
}

echo $string;

if only the first occurrenc should be replaced you can also give this function a limit of 1 as a the 4th parameter.

Update: Detailed explanation

(^|.*\s): First match group: String begins, or start of string followed by a space.

preg_quote($from): Your string to replace. It is quoted to support allkind of characters. preg_quote() will escape all characters to not interfere with regular expression control codes. Also takes care of Unicode characters.

(\s.*|$): Second match group: End of string or space followed by rest of the string.

'\1'.preg_quote($to).'\2': The replacement. First group + new string + second group.

Update 2:

Got rid of unneccessary group in code and added escaping to be more general applyable for all kinds of inputs.