Replacing words in a text all with different replacement

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Replacing words in a text all with different replacement

Post 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?
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post 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().
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post 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
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post 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.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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;
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post 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.
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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.
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Post 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?
mrkite
Forum Contributor
Posts: 104
Joined: Tue Sep 11, 2007 4:19 am

Post by mrkite »

preg_replace_callback() with a callback function that returns random words.
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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:
mrkite
Forum Contributor
Posts: 104
Joined: Tue Sep 11, 2007 4:19 am

Post 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.
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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:
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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.
mrkite
Forum Contributor
Posts: 104
Joined: Tue Sep 11, 2007 4:19 am

Post 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);
User avatar
Stryks
Forum Regular
Posts: 746
Joined: Wed Jan 14, 2004 5:06 pm

Post 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)
Post Reply