LadaB
LadaB

Reputation: 81

Replace string and preserve original string's case in the replacement

I'm trying to do some sort of translator which would be able to keep text uppercase/lowercase.

Example:

Potato in the PLACE.
Potato in the pLAcE.
Potato in the place.
Potato in the Place.

I want to replace word place with garden while applying the original case in the replacement characters.

Potato in the GARDEN.
Potato in the gARdEn.
Potato in the garden.
Potato in the Garden.

It should also work with phrases.

Upvotes: 2

Views: 1160

Answers (5)

mickmackusa
mickmackusa

Reputation: 47900

To make a multibyte-safe, full word replacement, use word boundaries when making an initial match with preg_replace_callback(), then implement a "counter" variable to assist in synchronizing the replacement casing with the found word's casing.

This implementation avoids fumbling with excessive loops and multibyte function calls. The first regex will isolate full words. The second regex will read each letter of the match and simultaneously determine if it is an uppercase character or something else -- this dictates whether the corresponding replacement character should be made uppercase.

Code: (Demo)

$find = 'place';  // case is irrelevant to processing
$replace = 'garden'; // must be declared in all lowercase

echo preg_replace_callback(
         '#\b' . preg_quote($find) . '\b#ui',
         function ($word) use ($replace) {
             $i = 0;
             return preg_replace_callback(
                 '#(\p{Lu})|.#su',
                 function ($char) use ($replace, &$i) {
                     $newChar = mb_substr($replace, $i++, 1);
                     return isset($char[1]) ? mb_strtoupper($newChar) : $newChar;
                 },
                 $word[0]
             ) . mb_substr($replace, $i); // append remaining chars unaltered
         },
         'Place replacements in their proper plACe without being complacent; use word boundaries.'
     );

Output:

Garden replacements in their proper gaRDen without being complacent; use word boundaries.

Upvotes: 1

LadaB
LadaB

Reputation: 81

So I managed to create my own function in the end. Thanks for help and inspiration though.

function replaceWithCase($source, $replacement, $string, $pos = 0) {

    while (($pos = strpos(strtolower($string), strtolower($source), $pos))!== false) {
        $substr = mb_substr($string, $pos, strlen($source));
        $remaining = mb_substr($string, $pos + strlen($source));

        if (ctype_upper($substr)) {
            $string = substr_replace($string,strtoupper($replacement),$pos,strlen($source));
            continue;
        }

        $substrParts = preg_split('//u', $substr, null, PREG_SPLIT_NO_EMPTY);
        $replaceParts = preg_split('//u', $replacement, null, PREG_SPLIT_NO_EMPTY);
        $newWord = '';

        foreach ($replaceParts as $k => $rp) {
            if (array_key_exists($k,$substrParts))
                $newWord .= ctype_upper($substrParts[$k]) ? mb_strtoupper($rp) : mb_strtolower($rp);
            else
                $newWord .= $rp;  
        }
        $string = substr_replace($string,$newWord,$pos,strlen($source));
        $pos = $pos + strlen($source);
    }

    return $string;
}

echo replaceWithCase("place", "garden", "Potato is jumping all over the PLACE");
echo "<br>";
echo replaceWithCase("jumping", "running", "Potato is jumping all over the pLAcE");
echo "<br>";
echo replaceWithCase("jumping", "cry", "Potato is jumping all over the place");
echo "<br>";
echo replaceWithCase("all", "", "Potato is jumping all over the Place");
echo "<br>";
echo replaceWithCase(" ", ";", "Potato is jumping all over the Place", 10);
echo "<br>";

Output:

Potato is jumping all over the GARDEN
Potato is running all over the pLAcE
Potato is cry all over the place
Potato is jumping over the Place
Potato is jumping;all;over;the;Place

Upvotes: 1

Bahadur Singh Deol
Bahadur Singh Deol

Reputation: 821

Use "stripos" php function


$str = 'Potato is jumping all over the pLAcEs.';
$str_new = "garden";

$str1 = str_split($str_new);
$pos    = stripos($str,'places');
$string = substr($str,$pos,6);

 $extrct = str_split($string);


$var = '';

foreach ($extrct as $key => $value) {
    if(ctype_upper($value))
    {
        $var .= strtoupper($str1[$key]);        
    }else{
        $var .= strtolower($str1[$key]);        
    }
}



$new_string = str_replace($string, $var, $str);


echo $new_string; //Potato is jumping all over the gARdEn.

Upvotes: -1

Gauttam Kevadia
Gauttam Kevadia

Reputation: 11

function stringReplace($findStr, $replaceStr, $str)
{
    $isLowerStr = true;
    for($i=0; $i<strlen($findStr); $i++){
        if(ord($findStr[$i]) >= 97 && ord($findStr[$i])<=122){
            if(ord($replaceStr[$i]) >= 65 && ord($replaceStr[$i])<=96){
                $replaceStr[$i] = strtolower($replaceStr[$i]);
            }else{
                $replaceStr[$i] = $replaceStr[$i];
            }
        }else{
            $isLowerStr = false;
            $replaceStr[$i] = strtoupper($replaceStr[$i]);
        }
    }
    if($isLowerStr == false){
        if(strlen($replaceStr) > strlen($findStr)){
            for($i=0;$i<(strlen($replaceStr)-strlen($findStr));$i++){
                if(strtoupper($findStr) == $findStr){
                        $replaceStr[strlen($findStr)+$i] = strtoupper($replaceStr[strlen($findStr)+$i]);
                }else{
                    $replaceStr[strlen($findStr)+$i] = strtolower($replaceStr[strlen($findStr)+$i]);
                }
            }
        }
    }
    echo str_replace($findStr, $replaceStr, $str);die;
}
$findStr = 'Place';
$replaceStr = 'garden';
echo stringReplace($findStr, $replaceStr, 'Potato is jumping all over the '.$findStr.'.');

Upvotes: 1

Peter
Peter

Reputation: 9123

I've created a function that will replace the word for you and keep the cases.

function replaceWithCase($source, $replacement, $string) {
    // Does the string contain the source word?
    if (strpos($string, $source) === false) {
        return false;
    }

    // If everything is uppercase, return the replacement fully uppercase
    if (ctype_upper($source)) {
        return str_replace($source, strtoupper($replacement));
    }

    // Set an array to work with
    $characters = array();

    // Split the source into characters
    $sourceParts = explode('', $source);

    // Loop throug the characters and set the case
    foreach ($sourceParts as $k => $sp) {
        if (ctype_upper($sp)) {
            $characters[$k] = true;
        } else {
            $characters[$k] = false;
        }
    }

    // Split the replacement into characters
    $replacementParts = explode('', $replacement);

    // Loop through characters and compare their case type
    foreach ($replacementParts as $k => $rp) {
        if (array_key_exists($k, $characters) && $characters[$k] === true) {
            $newWord[] = strtoupper($rp);
        } else {
            $newWord[] = strtolower($rp);
        }
    }

    return substr_replace($source, implode('', $newWord), $string);
}

// usage
echo replaceWithCase('AppLes', 'bananas', 'Comparing AppLes to pears');

Note: it is untested and might need some tweaking

Upvotes: 2

Related Questions