Page 1 of 2

Replacing words in a text all with different replacement

Posted: Wed Oct 10, 2007 1:31 pm
by kaisellgren
Hi,

Let's say I have a text like:

"Very nice day and very nice evening"

Code: Select all

$replacements = array(...);
$text = preg_replace('/\\b'.$keyword.'\\b/i',$replacements(rand(0,count($replacements)-1)),$text);
Now if $keyword is "nice" it could become like

"Very good day and good nice evening"
or
"Very comfortable day and comfortable nice evening"
or
"Very lovely day and lovely nice evening"

And as you can see it will replace both 'nice' words by the same replacement, but I want to have them both a random replacement. Do you see what I mean? How would I make it so?

Posted: Wed Oct 10, 2007 1:37 pm
by s.dot
An array of words. shuffle() them. str_replace() with arrays as parameters. From your example given, I don't see a need to use preg_replace().

Posted: Wed Oct 10, 2007 1:50 pm
by John Cartwright
scottayy wrote:An array of words. shuffle() them. str_replace() with arrays as parameters. From your example given, I don't see a need to use preg_replace().
String insensitivity and assuring that the word is not apart of another word are probably why

Posted: Wed Oct 10, 2007 2:01 pm
by s.dot
Jcart wrote:
scottayy wrote:An array of words. shuffle() them. str_replace() with arrays as parameters. From your example given, I don't see a need to use preg_replace().
String insensitivity and assuring that the word is not apart of another word are probably why
Even so, str_ireplace() (if the OP is using php5+) and including a space in the array of words. like array(' word ') would work wonderfully.

Personally, I don't use preg_replace() unless there's an actual regular expression being searched for.

Posted: Wed Oct 10, 2007 8:43 pm
by Stryks
I've had a go at this, and I cant see how str_replace would solve this issue.

My understanding is that the OP wants to change the keyword 'nice' to two separate values. So ... "It's a fine day in a fine way" might become "It's a good day in a glorious way."

However, passing an array of replacements to str_replace seems to throw a warning if search is a string and replacements an array. And if you pass an array of the same replacement value, it still replaces all instances with the first replacement.

Code: Select all

	$phrase = "I saw a ? and a ? run off with my ?";
	$insert = array('dog', 'cat', 'sausage');
	$markers = array_fill(0, count($insert), '?');
	
	echo str_replace($markers, $insert, $phrase);
produces
I saw a dog and a dog run off with my dog
I cant see a way of making str_replace make multiple replacements for the same search.

Or is there?

/*** EDIT

My code example is not really the best. It could be better achieved with vsprintf on "I saw a %s and a %s run off with my %s". But still ... it's not really a word replacement as the OP was looking for. Besides, I'd prefer to use ? instead of %s anyhow. :)

/*** EDIT #2

Actually ... I guess you could go ...

Code: Select all

$original = "It's a fine day in a fine way";
$working = str_replace('fine', '%s', $original, $changes);

$values = array('glorious', 'wonderful', 'marvelous', 'good', 'grand');	
$ins_keys = array_rand($values, $changes);
foreach($ins_keys as $id=>$key) $inserts[$id] = $values[$key]; 


$result = vsprintf($working, $inserts);
echo $result;
.... though somehow it seems more trouble than it's worth;

Posted: Wed Oct 10, 2007 9:19 pm
by John Cartwright
scottayy wrote:
Jcart wrote:
scottayy wrote:An array of words. shuffle() them. str_replace() with arrays as parameters. From your example given, I don't see a need to use preg_replace().
String insensitivity and assuring that the word is not apart of another word are probably why
Even so, str_ireplace() (if the OP is using php5+) and including a space in the array of words. like array(' word ') would work wonderfully.

Personally, I don't use preg_replace() unless there's an actual regular expression being searched for.
I wouldn't count on having spaces surrounding the words. For instance, punctuation.

Posted: Wed Oct 10, 2007 10:33 pm
by Stryks
Isn't that argument kind of moot if the function, regardless of spaces, doesn't actually achieve the desired result.

In fact, neither method seems to achieve the result desired by the original poster, if I understand correctly.

Posted: Thu Oct 11, 2007 12:06 am
by kaisellgren
Stryks wrote:Isn't that argument kind of moot if the function, regardless of spaces, doesn't actually achieve the desired result.

In fact, neither method seems to achieve the result desired by the original poster, if I understand correctly.
Your last script was the best so far. It takes a random word that it will use to replace the old words.

For some how I feel it is not completely random? Because for instance the second nice word barely never changes to 'glorious'. Also, I would like to replace words, not just text surrounded by a space.
That lady is fat, so I better not call her "nice".
How about using preg offset capture? First capturing matches' positions, and then actually replacing then afterwards? I could use substr() to replace the words since I have those positions, but I do not know how long the word is so that's a slight problem, isn't it?

Posted: Thu Oct 11, 2007 12:47 am
by mrkite
preg_replace_callback() with a callback function that returns random words.

Posted: Thu Oct 11, 2007 12:56 am
by Stryks
I'm forever looking for workarounds to preg functions. It just seems like using a sledgehammer on a picture hook. It can be done, but you might need a bigger picture to cover the hole.

I had a bit of a tinker ... and I dunno. The following does the job for the most part.

Code: Select all

/***
* Finds $needle in $haystack and replaces it with $replacement
* If haystack is array, multiple replacements are made for the same $needle
*/
function replaceText($needle, $haystack, $replace) {
	$r_count = substr_count($haystack, $needle);
	// Prepare the replacement array.  Fill array to contain enough replacements
        // or create array or replacements if string is passed
	if(is_array($replace)) { 
		if(count($replace) < $r_count) $replace = array_pad($replace, $r_count, '');
	} else $replace = array_fill(0, $r_count, $replace);
	// Make replacements and iterate through replacement array as we go
	while($r_loc = stripos($haystack, $needle)) { 
		$haystack = substr_replace($haystack, current($replace), $r_loc, strlen($needle)); 
		next($replace); 
	}
	return $haystack; 
}


$original = "It's a fine day in a fine way";

$values = array('glorious', 'wonderful', 'marvelous', 'good', 'grand');	
shuffle($values);

echo replaceText('fine', $original, $values);
Dont get me wrong, I'm not saying this is the best method (actually ... I've barely even tested this). I'm just saying that there are methods possible without the regex engine.

Cheers

/*** EDIT

hehehe ... faulty code ... will take another look if I get a chance

/*** NEXT EDIT

Yeah .... I did strlen(current($replace)) instead of strlen($needle). Fixed above.

/*** EDIT #3

Just removed some redundant code that might be confusing things

/*** EDIT #4

I hardcoded the replacement string ... fixed ...
... and I'm getting sick of editing. :lol:

Posted: Thu Oct 11, 2007 1:18 am
by mrkite
Stryks wrote:I'm forever looking for workarounds to preg functions. It just seems like using a sledgehammer on a picture hook. It can be done, but you might need a bigger picture to cover the hole.
Why? Regular expressions are in essence, extremely fast DFAs... much faster than any combination of strpos/substr could possibly be.

Posted: Thu Oct 11, 2007 1:25 am
by Stryks
I dunno really ... I went through a project and it seemed everywhere I turned I found the "if you dont need the power of regular expressions, you should use ..." notices.

So I just got into the habit of coding around those functions unless I need regular expressions. I'm a big fan of regular expressions, dont get me wrong. I use them a lot for more complex stuff.

As I said, it might not be the best way ... it's just a way. Plus it gives me something to tinker with on a lazy Thursday arvo .. :lol:

Posted: Thu Oct 11, 2007 2:43 am
by Stryks
I went and had a play with preg_replace_callback() and I couldnt really find a way to make it work the way I wanted without it feeling hackish.

I generally reserve callbacks for tasks that act directly on the item in question, as opposed to returning a random result from an unrelated array. I'd love to see a tidy version though, so if anyone has some code, post away.

Anyhow, before I put this to bed, heres a modified version I tinkered with briefly to search for whole words.

Code: Select all

$original = "It's a good day in a goodly way";

$values = array('glorious', 'wonderful', 'marvelous', 'good', 'grand');	
shuffle($values);

echo replaceText('good', $original, $values) . "<br><br>";
echo replaceText('good', $original, $values, true);

  
/***
* Finds $needle in $haystack and replaces it with $replacement
* If haystack is array, multiple replacements are made for the same $needle
*/
function replaceText($needle, $haystack, $replace, $wordsOnly = false) {
	if($wordsOnly) $realNeedle = " $needle "; else $realNeedle = $needle; 
	
	$r_count = substr_count($haystack, $realNeedle);
	// Prepare the replacement array.  Fill array to contain enough replacements or create array or replacements if string is passed
	if(is_array($replace)) { 
		if(count($replace) < $r_count) $replace = array_pad($replace, $r_count, '');
	} else $replace = array_fill(0, $r_count, $replace);
	// Make replacements and iterate through replacement array as we go
	while($r_loc = stripos($haystack, $realNeedle)) {
		if($wordsOnly) $r_loc += 1; 
		$haystack = substr_replace($haystack, current($replace), $r_loc, strlen($needle)); 
		next($replace); 
	}
	return $haystack; 
}
Cheers

/*** EDIT

Oh, and in answer to an earlier question, yeah there seems to be a question mark over the randomness of array_rand(). Thats why in later posts I randomise it with shuffle() before passing it to the function. Same result but with a better random spread.

Posted: Thu Oct 11, 2007 3:34 am
by mrkite
Stryks wrote:I went and had a play with preg_replace_callback() and I couldnt really find a way to make it work the way I wanted without it feeling hackish.

Code: Select all

$original="It's a good day in a goodly way\n";

$values=array('glorious','wonderful','marvelous','good','grand');
shuffle($values);

echo preg_replace_callback('{good}',
        create_function('',
                'global $values; return $values[array_rand($values)];'
        ),$original);

Posted: Thu Oct 11, 2007 3:49 am
by Stryks
Yeah ... thats pretty much what I wound up with.

I'm just not a fan of using global. Again, this is just a personal style thing. When I get to the point of using global I tend to stop and ask myself if I'm not going at things the wrong way.

Still, it's a legitimate function with a legitimate use .. and possibly one of the few instances that come to mind where it's use is warranted ... yet still it still makes me feel like I'm doing something wrong.

*shrugs* I'd probably use preg in production, though given the use here the speed difference should be negligible.

What interests me most is that they didn't build this functionality into str_replace(). In fact, they go so far as to say ...
If search is an array and replace is a string, then this replacement string is used for every value of search. The converse would not make sense, though.
I don't see why it wouldn't make sense. There is a legitimate use for multiple inserts on a single search.

Anyhow ... if a solution cant be pulled from all this, something is wrong.

Cheers 8)