I have a code in php which does search and replace matching contents in the given text.
$result = str_replace($searchData, $replaceData, $contentString);
In this $searchData is a array with what strings to be searched and $replaceData is a array with what strings it has to be replaced and $contentString is a text content. It is working fine. Now i want the match to be excluded if it is present inside backticks (`). The alternate function preg_replace is asking for search pattern as input where in my case it has to search for specific string in the array and replace its relevant match. How do i get this done in php ?
Sample Data:
$searchData = ['%browser%', '%os%', '%city%'];
$replaceData = ['Chrome', 'Windows', 'Delhi'];
$contentString = 'When i work in %city% i was given with a laptop with %os% installed and it came with %browser%. everyone used `same %browser%` in our office.';
echo str_replace($searchData, $replaceData, $contentString);
if i run the script i get all variables replaced with correct content however i want the last variable '%browser%
' should not be replaced as it is coming within back ticks. how to do this in php ?
CodePudding user response:
Basically, you can build an alternation of searched data and adding a branch that matches backticks enclosed parts and that forces the pattern to fail (*F)
and to not retry the substring (*SKIP)
.
$rep = array_combine($searchData, $replaceData);
$pattern = '~`[^`]*`(*SKIP)(*F)|' . implode('|', $searchData) . '~';
$result = preg_replace_callback($pattern, fn($m) => $rep[$m[0]], $str);
Notices:
each branch of the alternation starts with a literal character, and the pattern is only tested at positions where these characters exist. It's an optimisation.
it's possible to remove
%
s from the search array and to put them in factor into the pattern:%(browser|os|city)%
. In this case, the$rep
array key is the first capture group$m[1]
and not the whole match$m[0]
.Using an approach, with this kind of pattern
~`[^`]*`(*SKIP)(*F)|%(\w )%~
and with a callback function that checks if the key exists in the replacement array, may drive into problems with this kind of string:$str = '%notakey%akey%';
since the%
in the middle can't be matched two times. However, if you know in advance that this kind of substring is not possible, then this approach is more convenient and the pattern shorter.
CodePudding user response:
Simply use a small trick (two times string replacements) to do what you want:
- Find all strings which are enclosed by backticks
- Replace all such backticks by ^, and those @ inside the backticks by #
- Do your str_replace to perform the string arrays replacement
- Reverse the replacement in 2 above.
<?php
function getStringBetween($str, $start, $end)
{
$pos1 = strpos($str, $start);
$pos2 = strpos($str, $end, $pos1 1);
return substr($str, $pos1, $pos2-($pos1 1) 2);
}
$searchData = ['%browser%', '%os%', '%city%'];
$replaceData = ['Chrome', 'Windows', 'Delhi'];
$contentString = 'When i work in %city% i was given with a laptop with %os% installed and it came with %browser%. everyone used `same %browser%` in our office.';
$string=$contentString;
////////////////////////
$string1='`';
while (strpos($string, '`') !== false){
$string1= getStringBetween($string, '`', '`');
$string=str_replace($string1,str_replace('%','#',str_replace('`','^', $string1)), $string);
}
$string= str_replace($searchData, $replaceData, $string);
$string1='^';
while (strpos($string, '^') !== false){
$string1= getStringBetween($string, '^', '^');
$string=str_replace($string1,str_replace('#','%',str_replace('^','`', $string1)), $string);
}
///////////////////////
echo $string;
?>
I believe the above works, but if you want to use other regex to do the above please feel free to do so, I just want to demonstrate the concept