I have an array like
$array[]="This is a test";
$array[]="This is a TEST";
$array[]="TeSt this";
I need to make the string 'test' as bold like
$array[]="This is a <b>test</b>";
$array[]="This is a <b>TEST</b>";
$array[]="<b>TeSt</b> this";
I have tried with str_replace() but it is case sensitive,
Note:
I need to make the given string bold and keep as it is.
You can use array_walk PHP function to replace the string value within an array. Check below code
function my_str_replace(&$item){
$item = preg_replace("/test/i", '<b>$0</b>', $item);
}
$array[]="This is a test";
$array[]="This is a TEST";
$array[]="TeSt this";
array_walk($array, 'my_str_replace');
EDIT: Based on John WH Smith's comment
You can simply use $array = preg_replace("/test/i", '<b>$0</b>', $array); which would do the magic
If you're looking for patterns instead of fixed strings like "test", have a look at REGEXes and preg_replace :
$str = preg_replace("#(test|otherword)#i", "<b>$1</b>", $str);
More about REGEXes :
http://en.wikipedia.org/wiki/Regular_expression
http://www.regular-expressions.info/
http://uk.php.net/preg_replace
Edit : added "i" after the REGEX to remove case sensitivity.
You can use a function like the one I wrote below:
function wrap_text_with_tags( $haystack, $needle , $beginning_tag, $end_tag ) {
$needle_start = stripos($haystack, $needle);
$needle_end = $needle_start + strlen($needle);
$return_string = substr($haystack, 0, $needle_start) . $beginning_tag . $needle . $end_tag . substr($haystack, $needle_end);
return $return_string;
}
So you'd be able to call it as follows:
$original_string = 'Writing PHP code can be fun!';
$return_string = wrap_text_with_tags( $original_string , 'PHP' , "<strong>" ,"</strong>");
When returned the strings will look as follows:
Original String
Writing PHP code can be fun!
Modified Result
Writing PHP code can be fun!
This function only works on the FIRST instance of a string.
This is my solution. It also keeps all uppercase letters uppercase and all lowercase letters lowercase.
function wrapTextWithTags( $haystack, $needle , $tag ): string
{
$lowerHaystack = strtolower($haystack);
$lowerNeedle = strtolower($needle);
$start = stripos($lowerHaystack, $lowerNeedle);
$length = strlen($needle);
$textPart = substr($haystack, $start, $length);
$boldPart = "<" . $tag . ">" . $textPart . "</" . $tag . ">";
return str_replace($textPart, $boldPart, $haystack);
}
I find using a preg_replace() call to be the most appropriate tool for this task because:
it can affect all elements in the array without writing a loop,
it can replace more than one substring within a string,
adding a case-insensitive flag (i) is an easy and intuitive adjustment,
adding word boundaries (/b) on either side of the "needle" word will ensure that only whole words are replaced
when replacing the fullstring match, no parentheses / capture groups are necessary.
Code: (Demo)
$array = [
"This is a test",
"This is a TEST",
"Test this testy contest protest test!",
"TeSt this",
];
var_export(
preg_replace('/\btest\b/i', '<b>$0</b>', $array)
);
Output:
array (
0 => 'This is a <b>test</b>',
1 => 'This is a <b>TEST</b>',
2 => '<b>Test</b> this testy contest protest <b>test</b>!',
3 => '<b>TeSt</b> this',
)
Try str_ireplace. Case insensitive version of str_replace
Try this
Using str_ireplace
str_ireplace("test", "<b>test</b>", $array);
str_ireplace("TSET", "<b>TEST</b>", $array);
Related
I'm trying to replace a string with certain words, but I also want to replace it in order of the position in the array. For example, I want to replace "b c", before I try to replace "a b", without changing the position in the original string. By the way, the letters are suppose to represent actual words, and they are not supposed be part of another word. For example, the word "sun" is part of "sunflower", and the word "sunflower" cannot be replaced just because the word "sun" is in it.
$text = "a b c";
$replacement = array("a b" => "ab","b c" => "bc");
$search = array_map(function($v){
return preg_quote($v, "/");
}, array_keys($replacement));
echo $text = preg_replace_callback("/\b(" . implode("|", $search) . ")\b/", function($m)use($replacement){
return $replacement[$m[1]];
}, $text);
First Result
ab c
Second Result
I switched the position in the array around, thinking that it would affect the order of which the strings get replaced. Sadly, it doesn't work like that, and I got the same result.
$replacement = array("b c" => "bc","a b" => "ab");
ab c
At this point, I realize that it wasn't the position of the array that affects which part of the string that get replaced first, but the order of the part of strings that shows up on the original string that determines the order in which it is replaced by.
So, my question is, is there a way to somehow make it so that it can replace the string in order according to the order in which it is in the array, or in a different way? For example, I want to replace
"b c"
before I try to replace "a b" without changing the position of the original string. Is that doable? Thanks.
[EDIT]
The idea consists to cast the original text to an array (with one element at the beginning, the text). Array items at even index are splitted for each patterns. Since PREG_SPLIT_DELIM_CAPTURE option is used, delimiters have always an odd index and stay untouched once they are matched.
$text = 'a b c';
$rep = ['b c'=>'bc', 'a b'=>'ab', ];
$pats = array_map(function($i) {
return '~\b(' . preg_quote($i, '~') . ')\b~';
}, array_keys($rep));
$parts = (array)$text; // or $parts = [ $text ]; // it's the same
foreach ($pats as $pat) {
$temp = [];
foreach ($parts as $k=>$part) {
if ($k & 1)
$temp[] = $part;
else
$temp = array_merge(
$temp,
preg_split($pat, $part, -1, PREG_SPLIT_DELIM_CAPTURE)
);
}
$parts = $temp;
}
$result = '';
foreach ($parts as $k=>$part) {
$result .= ($k & 1) ? $rep[$part] : $part;
}
echo $result;
I changed your code to represent what (I think) you wanted:
$text = "a b c a b";
$replacement = array("b c" => "bc", "a b" => "ab");
$search = array_map(function($v){
return preg_quote($v, "/");
}, array_keys($replacement));
for($i = 0; $i < count($replacement); $i++) {
$regex = "/\b(" . $search[$i] . ")\b/";
echo $text = preg_replace_callback($regex, function($m)use($replacement){
return $replacement[$m[1]];
}, $text);
echo "<br>";
}
Basically, instead of relying on the regex to do that work I do a for loop to go through each replacement and create the regex. That way the order of the array matters. I also changed the initial $text to test if it worked
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Example:
"Hello iam a [start] string and iam very happy with [end] beeing a string...";
Now lets say i only want to change something in the substring between [start] and [end]. I know how to find out the str_pos and the lenght to the end but how can i search and replace something that only affects this substring?
What i want to do is: (for example)
Replace 'x' with 'y' within str positon 5 and 50
I know that there is substr_replace but thats not exactly what iam looking for.
Any ideas would be great, thanks a lot!
If I understand correctly you want to replace something within a substring while knowing it's position within it's parent string. Using substr() and preg_replace can easily do this:
Code:
$str = 'Hello iam a [start] string and iam very happy with [end] beeing a string...';
$begpos = 12; // begin position of [start]
$endpos = 56; // end position of [end]
$substr = substr($str, $begpos, $endpos-$begpos);
$subtit = preg_replace('/\iam/','I AM', $substr);
$newstr = substr_replace($str,$subtit,$begpos,$endpos-$begpos);
echo $newstr;
Result:
Hello iam a [start] string and I AM very happy with [end] beeing a string...
How about something like...
<?php
$string = 'my first word was first then it was the second word';
echo "$string\n";
echo str_pos_replace($string, 'word', 'second', 'first', 'second') . "\n";
function str_pos_replace($string, $start, $end, $find, $replace)
{
$pos1=strpos($string, $start);
$pos2=strpos($string, $end);
$subject=substr($string, $pos1, ($pos2-$pos1));
$replaced=str_replace($find, $replace, $subject);
return str_replace($subject, $replaced, $string);
}
Will print out something like:
my first word was first then it was the second word
my first word was second then it was the second word
here is an example pattern to find the letter 'a' between [start] and [end]
(.*\[start\]|\[end\].*)(*SKIP)(*F)|a
Demo
if is string
str_repalce(needle, replace, haystack)
if array
json_encode haystack
str_replace needle, replace, haystack)
json_decode haystack
Knowing the str position is noit realy of value to you when if you know what you are looking for you can just replace it with some thing else just remember the rule double quotes to use php vars "$haystack" and single quotes will render exactly as you type '$haystack'
this is by far the easyest way and best way to replace text or values in a string or in a array but please read the documentation of the functions before use so you understand how they work. Json_encode / Decode can be a tricky thing to master but once you understand it you can use it to trasform your arrays to strings and back again reall easy.
You could do it with a callback.
Regex:
"/(?xs)^( . {" . $start_pos . "} ) ( . {" . $end_pos . "} )/"
Inside the callback, run a new regex replacement on group 2 for xyz.
Return Catted original group 1 and and the replaced group 2.
Untested code:
$start_pos = 5;
$end_pos = 50;
$str = preg_replace_callback
(
"/(?xs)^( . {" . $start_pos . "} ) ( . {" . $end_pos . "} )/",
function( $matches )
{
$orig_grp1 = $matches[1];
$substr_replaced = preg_replace( '/xyz/', 'XYZ', $matches[2] );
return $orig_grp1 . $substr_replaced;
},
$str
);
So yeah, I was a bit bored, and this might be an overkill, but it does what you ask:
function replaceBetweenTags($needle, $replacement, $haystack, $keepTags = false, $startTag = "[start]", $endTag = "[end]") {
$startPattern = preg_quote($startTag);
$endPattern = preg_quote($endTag);
$pattern = '/'.$startPattern.'(.+)'.$endPattern.'/m';
if (!preg_match($pattern, $haystack)) return $haystack;
return preg_replace_callback($pattern, function($match) use ($startTag, $endTag, $keepTags, $needle, $replacement) {
$match = str_replace($needle, $replacement, $match[0]);
if ($keepTags) return $match;
return str_replace($startTag, "", str_replace($endTag, "", $match));
}, $haystack);
}
Usage is simple:
echo replaceBetweenTags("happy", "sad", "Hello iam a [start] string and iam very happy with [end] beeing a string..."
// Output: Hello iam a string and iam very sad with beeing a string...
// Unless you set $keepTags = true, in which case it'll preserve the tags.
It handles multiple tag pairs too. Things to note:
it's case sensitive
$keepTags = false might leave double spaces. You might want to str_replace those
Hope it helps.
We want to censor certain words on our site but each word has different censored output.
For example:
PHP => P*P, javascript => j*vascript
(However not always the second letter.)
So we want a simple "one star" censor system but with keeping the original caps. The datas coming from the database are uncensored so we need the fastest way that possible.
$data="Javascript and php are awesome!";
$word[]="PHP";
$censor[]="H";//the letter we want to replace
$word[]="javascript";
$censor[]="a"//but only once (j*v*script would look wierd)
//Of course if it needed we can use the full censored word in $censor variables
Expected value:
J*vascript and p*p are awesome!
Thanks for all the answers!
You can put your censored words in key-based array, and value of the array should be the position of what char is replaced with * (see $censor array example bellow).
$string = 'JavaSCRIPT and pHp are testing test-ground for TEST ŠĐČĆŽ ŠĐčćŽ!';
$censor = [
'php' => 2,
'javascript' => 2,
'test' => 3,
'šđčćž' => 4,
];
function stringCensorSlow($string, array $censor) {
foreach ($censor as $word => $position) {
while (($pos = mb_stripos($string, $word)) !== false) {
$string =
mb_substr($string, 0, $pos + $position - 1) .
'*' .
mb_substr($string, $pos + $position);
}
}
return $string;
}
function stringCensorFast($string, array $censor) {
$pattern = [];
foreach ($censor as $word => $position) {
$word = '~(' . mb_substr($word, 0, $position - 1) . ')' . mb_substr($word, $position - 1, 1) . '(' . mb_substr($word, $position) . ')~iu';
$pattern[$word] = '$1*$2';
}
return preg_replace(array_keys($pattern), array_values($pattern), $string);
}
Use example :
echo stringCensorSlow($string, $censor);
# J*vaSCRIPT and p*p are te*ting te*t-ground for TE*T ŠĐČ*Ž ŠĐč*Ž!
echo stringCensorFast($string, $censor) . "\n";
# J*vaSCRIPT and p*p are te*ting te*t-ground for TE*T ŠĐČ*Ž ŠĐč*Ž!
Speed test :
foreach (['stringCensorSlow', 'stringCensorFast'] as $func) {
$time = microtime(true);
for ($i = 0; $i < 10000; $i++) {
$func($string, $censor);
}
$time = microtime(true) - $time;
echo "{$func}() took $time\n";
}
output on my localhost was :
stringCensorSlow() took 1.9752140045166
stringCensorFast() took 0.11587309837341
Upgrade #1: added multibyte character safe.
Upgrade #2: added example for preg_replace, which is faster than mb_substr. Tnx to AbsoluteƵERØ
Upgrade #3: added speed test loop and result on my local PC machine.
Make an array of words and replacements. This should be your fastest option in terms of processing, but a little more methodical to setup. Remember when you're setting up your patterns to use the i modifier to make each pattern case insensitive. You could ultimately pull these from a database into the arrays. I've hard-coded the arrays here for the example.
<!DOCTYPE html>
<html>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<?php
$word_to_alter = array(
'!(j)a(v)a(script)(s|ing|ed)?!i',
'!(p)h(p)!i',
'!(m)y(sql)!i',
'!(p)(yth)o(n)!i',
'!(r)u(by)!i',
'!(ВЗЛ)О(М)!iu',
);
$alteration = array(
'$1*$2*$3$4',
'$1*$2',
'$1*$2',
'$1$2*$3',
'$1*$2',
'$1*$2',
);
$string = "Welcome to the world of programming. You can learn PHP, MySQL, Python, Ruby, and Javascript all at your own pace. If you know someone who uses javascripting in their daily routine you can ask them about becoming a programmer who writes JavaScripts. взлом прохладно";
$newstring = preg_replace($word_to_alter,$alteration,$string);
echo $newstring;
?>
</html>
Output
Welcome to the world of programming. You can learn P*P, M*SQL, Pyth*n,
R*by, and J*v*script all at your own pace. If you know someone who
uses j*v*scripting in their daily routine you can ask them about
becoming a programmer who writes J*v*Scripts. взл*м прохладно
Update
It works the same with UTF-8 characters, note that you have to specify a u modifier to make the pattern treated as UTF-8.
u (PCRE_UTF8)
This modifier turns on additional functionality of PCRE that is incompatible with Perl. Pattern strings are treated as UTF-8. This
modifier is available from PHP 4.1.0 or greater on Unix and from PHP
4.2.3 on win32. UTF-8 validity of the pattern is checked since PHP 4.3.5.
Why not just use a little helper function and pass it a word and the desired censor?
function censorWord($word, $censor) {
if(strpos($word, $censor)) {
return preg_replace("/$censor/",'*', $word, 1);
}
}
echo censorWord("Javascript", "a"); // returns J*avascript
echo censorWord("PHP", "H"); // returns P*P
Then you can check the word against your wordlist and if it is a word that should be censored, you can pass it to the function. Then, you also always have the original word as well as the censored one to play with or put back in your sentence.
This would also make it easy to change the number of letters censored by just changing the offset in the preg_replace. All you have to do is keep an array of words, explode the sentence on spaces or something, and then check in_array. If it is in the array, send it to censorWord().
Demo
And here's a more complete example doing exactly what you said in the OP.
function censorWord($word, $censor) {
if(strpos($word, $censor)) {
return preg_replace("/$censor/",'*', $word, 1);
}
}
$word_list = ['php','javascript'];
$data = "Javascript and php are awesome!";
$words = explode(" ", $data);
// pass each word by reference so it can be modified inside our array
foreach($words as &$word) {
if(in_array(strtolower($word), $word_list)) {
// this just passes the second letter of the word
// as the $censor argument
$word = censorWord($word, $word[1]);
}
}
echo implode(" ", $words); // returns J*vascript and p*p are awesome!
Another Demo
You could store a lowercase list of the censored words somewhere, and if you're okay with starring the second letter every time, do something like this:
if (in_array(strtolower($word), $censored_words)) {
$word = substr($word, 0, 1) . "*" . substr($word, 2);
}
If you want to change the first occurrence of a letter, you could do something like:
$censored_words = array('javascript' => 'a', 'php' => 'h', 'ruby' => 'b');
$lword = strtolower($word);
if (in_array($lword, array_keys($censored_words))) {
$ind = strpos($lword, $censored_words[$lword]);
$word = substr($word, 0, $ind) . "*" . substr($word, $ind + 1);
}
This is what I would do:
Create a simple database (text file) and make a "table" of all your censored words and expected censored results. E.G.:
PHP --- P*P
javascript --- j*vascript
HTML --- HT*L
Write PHP code to compare the database information to your simple censored file. You will have to use array explode to create an array of only words. Something like this:
/* Opening database of censored words */
$filename = "/files/censored_words.txt";
$file = fopen( $filename, "r" );
if( $file == false )
{
echo ( "Error in opening file" );
exit();
}
/* Creating an array of words from string*/
$data = explode(" ", $data); // What was "Javascript and PHP are awesome!" has
// become "Javascript", "and", "PHP", "are",
// "awesome!". This is useful.
If your script finds matching words, replace the word in your data with the censored word from your list. You would have to delimit the file first by \r\n and finally by ---. (Or whatever you choose for separating your table with.)
Hope this helped!
this is what I try to get:
My longest text to test When I search for e.g. My I should get My longest
I tried it with this function to get first the complete length of the input and then I search for the ' ' to cut it.
$length = strripos($text, $input) + strlen($input)+2;
$stringpos = strripos($text, ' ', $length);
$newstring = substr($text, 0, strpos($text, ' ', $length));
But this only works first time and then it cuts after the current input, means
My lon is My longest and not My longest text.
How I must change this to get the right result, always getting the next word. Maybe I need a break, but I cannot find the right solution.
UPDATE
Here is my workaround till I find a better solution. As I said working with array functions does not work, since part words should work. So I extended my previous idea a bit. Basic idea is to differ between first time and the next. I improved the code a bit.
function get_title($input, $text) {
$length = strripos($text, $input) + strlen($input);
$stringpos = stripos($text, ' ', $length);
// Find next ' '
$stringpos2 = stripos($text, ' ', $stringpos+1);
if (!$stringpos) {
$newstring = $text;
} else if ($stringpos2) {
$newstring = substr($text, 0, $stringpos2);
} }
Not pretty, but hey it seems to work ^^. Anyway maybe someone of you have a better solution.
You can try using explode
$string = explode(" ", "My longest text to test");
$key = array_search("My", $string);
echo $string[$key] , " " , $string[$key + 1] ;
You can take i to the next level using case insensitive with preg_match_all
$string = "My longest text to test in my school that is very close to mY village" ;
var_dump(__search("My",$string));
Output
array
0 => string 'My longest' (length=10)
1 => string 'my school' (length=9)
2 => string 'mY village' (length=10)
Function used
function __search($search,$string)
{
$result = array();
preg_match_all('/' . preg_quote($search) . '\s+\w+/i', $string, $result);
return $result[0];
}
There are simpler ways to do that. String functions are useful if you don't want to look for something specific, but cut out a pre-defined length of something. Else use a regular expression:
preg_match('/My\s+\w+/', $string, $result);
print $result[0];
Here the My looks for the literal first word. And \s+ for some spaces. While \w+ matches word characters.
This adds some new syntax to learn. But less brittle than workarounds and lengthier string function code to accomplish the same.
An easy method would be to split it on whitespace and grab the current array index plus the next one:
// Word to search for:
$findme = "text";
// Using preg_split() to split on any amount of whitespace
// lowercasing the words, to make the search case-insensitive
$words = preg_split('/\s+/', "My longest text to test");
// Find the word in the array with array_search()
// calling strtolower() with array_map() to search case-insensitively
$idx = array_search(strtolower($findme), array_map('strtolower', $words));
if ($idx !== FALSE) {
// If found, print the word and the following word from the array
// as long as the following one exists.
echo $words[$idx];
if (isset($words[$idx + 1])) {
echo " " . $words[$idx + 1];
}
}
// Prints:
// "text to"
Consider the following array which holds all US stock tickers, ordered by length:
$tickers = array('AAPL', 'AA', 'BRK.A', 'BRK.B', 'BAE', 'BA'); // etc...
I want to check a string for all possible matches. Tickers are written with or without a "$" concatenated to the front:
$string = "Check out $AAPL and BRK.A, BA and BAE.B - all going up!";
All tickers are to be labeled like: {TICKER:XX}. The expected output would be:
Check out {TICKER:AAPL} and {TICKER:BRK.A} and BAE.B - all going up!
So tickers should be checked against the $tickers array and matched both if they are followed by a space or a comma. Until now, I have been using the following:
preg_replace('/\$([a-zA-Z.]+)/', ' {TICKER:$1} ', $string);
so I didn't have to check against the $tickers array. It was assumed that all tickers started with "$", but this only appears to be the convention in about 80% of the cases. Hence, the need for an updated filter.
My question being: is there a simple way to adjust the regex to comply with the new requirement or do I need to write a new function, as I was planning first:
function match_tickers($string) {
foreach ($tickers as $ticker) {
// preg_replace with $
// preg_replace without $
}
}
Or can this be done in one go?
Just make the leading dollar sign optional, using ? (zero or 1 matches). Then you can check for legal trailing characters using the same technique. A better way to go about it would be to explode your input string and check/replace each substring against the ticker collection, then reconstruct the input string.
function match_tickers($string) {
$aray = explode( " ", $string );
foreach ($aray as $word) {
// extract any ticker symbol
$symbol = preg_replace( '/^\$?([A-Za-z]?\.?[A-Za-z])\W*$/', '$1', $word );
if (in_array($symbol,$tickers)) { // symbol, replace it
array_push( $replacements, preg_replace( '/^\$?([A-Za-z]?\.?[A-Za-z])(\W*)$/', '{TICKER:$1}$2', $word ) );
}
else { // not a symbol, just output it normally
array_push( $replacements, $word );
}
}
return implode( " ", $replacements );
}
I think just a slight change to your regex should do the trick:
\$?([a-zA-Z.]+)
i added "?" in front of the "$", which means that it can appear 0 or 1 times
You can use a single foreach loop on your array to replace the ticker items in your string.
$tickers = array('AAPL', 'AA', 'BRK.A', 'BRK.B', 'BAE', 'BA');
$string = 'Check out $AAPL and BRK.A, BA and BAE.B - all going up!';
foreach ($tickers as $ticker) {
$string = preg_replace('/(\$?)\b('.$ticker.')\b(?!\.[A-Z])/', '{TICKER:$2}', $string);
}
echo $string;
will output
Check out {TICKER:AAPL} and {TICKER:BRK.A}, {TICKER:BA} and BAE.B -
all going up!
Adding ? after the $ sign will also accept words, i.e. 'out'
preg_replace accepts array as a pattern, so if you change your $tickers array to:
$tickers = array('/AAPL/', '/AA/', '/BRK.A/', '/BRK.B/', '/BAE/', '/BA/');
then this should do the trick:
preg_replace($tickers, ' {TICKER:$1} ', $string);
This is according to http://php.net/manual/en/function.preg-replace.php