Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
365 views
in Technique[技术] by (71.8m points)

php - exchanging values of a variable, by values of an array, but under condition

I have a code that compares the output with the values of the array, and only terminates the operation with words in the array:

First code(just a example)

$myVar = 'essa pizza é muito gostosa, que prato de bom sabor';
$myWords=array(
    array('sabor','gosto','delicia'),
    array('saborosa','gostosa','deliciosa'),
);

foreach($myWords as $words){
    shuffle($words); // randomize the subarray
    // pipe-together the words and return just one match
    if(preg_match('/K(?:'.implode('|',$words).')/',$myVar,$out)){
        // generate "replace_pair" from matched word and a random remaining subarray word
        // replace and preserve the new sentence
        $myVar=strtr($myVar,[$out[0]=>current(array_diff($words,$out))]);
    }
}
echo $myVar;

My Question:

I have a second code, which is not for rand/shuffle(I do not want rand, I want precision in substitutions, I always change column 0 through 1), is to always exchange the values:

// wrong output: $myVar = "minha irm? alanné é not aquela blnode, elere é a bom plperito";
$myVar = "my sister alannis is not that blonde, here is a good place";
$myWords=array(array("is","é"),
    array("on","no"),
    array("that","aquela"),
    //array("blonde","loira"),
    //array("not","n?o"),
    array("sister","irm?"), 
    array("my","minha"),
    //array("nothing","nada"),
    array("myth","mito"),
    array("he","ele"),
    array("good","bom"),
    array("ace","perito"),
   // array("here","aqui"), //if [here] it does not exist, it is not to do replacement from the line he=ele = "elere" non-existent word  
); 
$replacements = array_combine(array_column($myWords,0),array_column($myWords,1));
$myVar = strtr($myVar,$replacements);
echo $myVar;
// expected output:  minha irm? alannis é not aquela blonde, here é a bom place
//  avoid replace words slice!

expected output: minha irm? alannis é not aquela blonde, here é a bom place

    //  avoid replace words slice! always check if the word exists in the array before making the substitution.

alanné, blnode, elere, plperito

it examines whether the output will be of real words, which exist in the array myWords, this avoids typing errors like:

that 4 words is not an existent words, a writing error. how do you do that for the second code?

?? in short, the exchange must be made by a complete word / key, an existing word. and not create something strange using slices of keywords!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Unfortunately strtr() is the wrong tool for this job because it is "word boundary ignorant". To target whole words there is no simpler way that using a regex pattern with word boundaries.

Furthermore, to ensure that longer strings are match prior to shorter strings (strings that may exist inside other strings), you must sort $myWords by string length (descending / longest to shortest; using the multi-byte version only if necessary).

Once the array of words is sorted and converted to individual regex patterns, you can feed the arrays into the pattern and replace parameters of preg_replace().

Code (Demo)

$myVar = "my sister alannis is not that blonde, here is a good place";
$myWords=array(
    array("is","é"),
    array("on","no"),
    array("that","aquela"),
    array("sister","irm?"), 
    array("my","minha"),
    array("myth","mito"),
    array("he","ele"),
    array("good","bom"),
    array("ace","perito")
); 
usort($myWords,function($a,$b){return mb_strlen($b[0])<=>mb_strlen($a[0]);});  // sort subarrays by first column multibyte length
// remove mb_ if first column holds no multi-byte characters.  strlen() is much faster.

foreach($myWords as &$words){
    $words[0]='/'.$words[0].'/i';  // generate patterns using search word, word boundaries, and case-insensitivity
}

//var_export($myWords);
//var_export(array_column($myWords,0));
//var_export(array_column($myWords,1));

$myVar=preg_replace(array_column($myWords,0),array_column($myWords,1),$myVar);
echo $myVar;

Output:

minha irm? alannis é not aquela blonde, here é a bom place

What this doesn't do is appreciate the case of the matched substrings. I mean, my and My will both be replaced by minha.

To accommodate different casing, you will need to use preg_replace_callback().

Here is that consideration (which handles uppercase first letter words, not ALL CAPS words):

Code (Demo) <-- run this to see the original casing preserved after the replacement.

foreach($myWords as $words){
    $myVar=preg_replace_callback(
        $words[0],
        function($m)use($words){
            return ctype_upper(mb_substr($m[0],0,1))?
                mb_strtoupper(mb_substr($words[1],0,1)).mb_strtolower(mb_substr($words[1],1)):
                $words[1];
        },
        $myVar);
}
echo $myVar;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...