I have an interesting problem where I am highlighting text from a keyword array using PHP's str_ireplace().
Let's say this is my array of keywords or phrases that I want to highlight from a sample text:
$keywords = array('eggs', 'green eggs');
And this is my sample text:
$text = 'Green eggs and ham.';
Here is how I am highlighting the text:
$counter = 0;
foreach ($keywords as $keyword) {
$text = str_ireplace($keyword, '<span class="highlight_'.($counter%5).'">'.$keyword.'</span>', $text);
$counter++;
}
The problem with this is that green eggs will never get a match because eggs has already been replaced in the text as:
Green <span class="highlight_0">eggs</span> and ham.
There may also be cases where there is partial overlaps such as:
$keywords = array('green eggs', 'eggs and');
What is a smart way to tackle this sort of issue?
Reverse the order:
$keywords = array('green eggs', 'eggs');
The simplest way is to do the longest strings first and move on to shorter ones after. Just make sure you don't double-over the same string (if it matters).
Maybe this is not the prettiest solution, but you could track the locations where your keywords occur, and then find where they overlap and adjust where you want to include the span tags
$keywords = array('eggs', 'n eggs a', 'eggs and','green eg');
$text = 'Green eggs and ham.';
$counter = 0;
$idx_array = array();
$idx_array_last = array();
foreach ($keywords as $keyword) {
$idx_array_first[$counter] = stripos($text, $keyword);
$idx_array_last[$counter] = $idx_array_first[$counter] + strlen($keyword);
$counter++;
}
//combine the overlapping indices
for ($i=0; $i<$counter; $i++) {
for ($j=$counter-1; $j>=$i+1; $j--) {
if (($idx_array_first[$i] <= $idx_array_first[$j] && $idx_array_first[$j] <= $idx_array_last[$i])
|| ($idx_array_last[$i] >= $idx_array_last[$j] && $idx_array_first[$i] <= $idx_array_last[$j])
|| ($idx_array_first[$j] <= $idx_array_first[$i] && $idx_array_last[$i] <= $idx_array_last[$j])) {
$idx_array_first[$i] = min($idx_array_first[$i],$idx_array_first[$j]);
$idx_array_last[$i] = max($idx_array_last[$i],$idx_array_last[$j]);
$counter--;
unset($idx_array_first[$j],$idx_array_last[$j]);
}
}
}
array_multisort($idx_array_first,$idx_array_last); //sort so that span tags are inserted at last indices first
for ($i=$counter-1; $i>=0; $i--) {
//add span tags at locations of indices
$textnew = substr($text,0,$idx_array_first[$i]).'<span class="highlight_'.$i.'">';
$textnew .=substr($text,$idx_array_first[$i],$idx_array_first[$i]+$idx_array_last[$i]);
$textnew .='</span>'.substr($text,$idx_array_last[$i]);
$text = $textnew;
}
Output is
<span class="highlight_0">Green eggs and</span> ham.
Related
I need to print every character of string with different color (not random colors) using only HTML and PHP. It almost works, but first letter from array is black. Do you know why?
<html>
<?php
$myString = ["s","t","r","i","n","g"];
$myColors = ["blue","green","yellow","brown","gray","pink"];
for ($i = 0; $i < count($myString); $i++) {
echo "$myString[$i] <span style='color:$myColors[$i]'</span>";
}
?>
</html>
Seems like you didn't close your span tag correctly, and putting your string inside your span will help coloring it.
<?php
$myString = ["s","t","r","i","n","g"];
$myColors = ["blue","green","yellow","brown","gray","pink"];
for ($i = 0; $i < count($myString); $i++) {
echo "<span style='color:$myColors[$i]'>$myString[$i]</span>";
}
This function will take the Phrase & Color list and will color each character from the list in the order specified. It will also skip punctuation & white spaces to stay within meaningful printable characters between '0' & 'z' in the ASCII table.
function ColoredPhrase(string $Phrase, string $ColorList){
$ColorList = explode(",", str_replace(" ", null, $ColorList));
$ColorCount = count($ColorList);
$ColorIndex = -1;
for($CharacterIndex = 0; $CharacterIndex < strlen($Phrase); $CharacterIndex++){
$Character = $Phrase[$CharacterIndex];
if($Character >= "0" && $Character <= "z"){
$ColorIndex = $ColorIndex < ($ColorCount - 1) ? $ColorIndex + 1 : 0;
$HTML[] = "<span style=\"color: {$ColorList[$ColorIndex]};\">{$Character}</span>";
}else{
$HTML[] = $Character;
}
}
return implode($HTML);
}
var_dump(ColoredPhrase("Hello, World!", "Violet, Blue, Sky, Green, Yellow, Orange, Red"));
The example will take a Phrase containing multiple characters and will color them through the rainbow color table, meaning, the function will generate equivalent HTML through PHP.
The character is not inside the colored span and you're missing a >:
echo "<span style='color:$myColors[$i]'>$myString[$i]</span>";
I think your HTML is broken. You need to include $myString[$i] inside the <span> element and close it properly.
$myString = ["s","t","r","i","n","g"];
$myColors = ["blue","green","yellow","brown","gray","pink"];
for ($i = 0; $i < count($myString); $i++) {
echo "<span style='color:$myColors[$i]'>$myString[$i]</span>";
}
<html>
<?php
$myString = ["s","t","r","i","n","g"];
$myColors = ["blue","green","yellow","brown","gray","pink"];
for ($i = 0; $i < count($myString); $i++) {
echo "<span style='color:$myColors[$i]'> $myString[$i] </span>";
}
?>
</html>
You can cycle through your array of colours (over and over) by calculating the targeted index with the modulus operator. You can target the visible characters in your string with a simple regex pattern.
I prefer to use printf()/sprintf() when text is concatenated with operations or several variables.
Code: (Demo)
$palette = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
$count = count($palette);
echo preg_replace_callback(
'~\PZ~u',
function($m) use($palette, $count) {
static $i = 0;
return sprintf('<span style="color:%s">%s</span>', $palette[$i++ % $count], $m[0]);
},
'Stack Overflow volunteerism'
);
You will notice that the letters are wrapped in span tag with specified colors. The spaces are not wrapped in tags because that would be needless markup bloat. \PZ is a unicode/multibyte-safe way of saying any non-whitespace character.
Here's a slight variation of the same technique:
echo preg_replace_callback(
'~\PZ~u',
function($m) {
static $i = -1;
static $palette = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
return sprintf(
'<span style="color:%s">%s</span>',
$palette[++$i] ?? $palette[$i = 0],
$m[0]
);
},
'Stack Overflow volunteerism'
);
I had a PHP string which contains English words. I want to extract all the possible words from the string, not by explode() by space as I have only a word. I mean extraction of words from a word.
Example: With the word "stackoverflow", I need to extract stack, over, flow, overflow all of them.
I am using pspell_check() for spell checking. I am currently getting the following combination.
--> sta
--> stac
--> stack
and so on.
So I found the only the words matching stack but I want to find the following words. Notice that I don't want the final word as I've already.
--> stack
--> over
--> flow
My Code:
$myword = "stackoverflow";
$word_length = strlen($myword);
$myword_prediction = $myword[0].$myword[1];
//(initial condition as words detection starts after 3rd index)
for ($i=2; $i<$word_length; $i++) {
$myword_prediction .= $myword[$i];
if (pspell_check(pspell_new("en"), $myword_prediction))
{
$array[] = $myword_prediction;
}
}
var_dump($array);
How about if you have an outer loop like this. The first time through you start at the first character of $myword. The second time through you start at the second character, and so on.
$myword = "stackoverflow";
$word_length = strlen($myword);
$startLetter = 0;
while($startLetter < $word_length-2 ){
$myword_prediction = $myword[$startLetter] . $myword[$startLetter +1];
for ($i=$startLetter; $i<$word_length; $i++) {
$myword_prediction .= $myword[$i];
if (pspell_check(pspell_new("en"), $myword_prediction)) {
$array[] = $myword_prediction;
}
}
$startLetter ++;
}
Well, you would need to get all substrings, and check each one:
function get_all_substrings($input){
$subs = array();
$length = strlen($input);
for($i=0; $i<$length; $i++){
for($j=$i; $j<$length; $j++){
$subs[] = substr($input, $i, $j);
}
}
return array_unique($subs);
}
$substrings = get_all_substrings("stackoverflow");
$pspell_link = pspell_new("en");
$words = array_filter($substrings, function($word) use ($pspell_link) {
return pspell_check($pspell_link, $word);
});
var_dump($words);
I have some html menu, the max width is 990px. now i want to print this menu from an array, and make these menu list(<li>) in one line. remove the padding space or each menu list, for a conserved calculation, how to limit the total words within 200 characters from a foreach? I have no good idea, in my code, group array into string then cut String length, this is not a good idea.
$a = array("item1","item2","item3" ... "item30");//'item' would be change to any other words.
foreach($a as $b){
$c.=$b.'|';
}
function limitString($string, $limit = 200) {
if(strlen($string) < $limit) {return $string;}
$regex = "/(.{1,$limit})\b/";
preg_match($regex, $string, $matches);
return $matches[1];
}
$d = explode('|',limitString($c));
foreach($d as $e){
echo '<li><a href="">'.$e.'</li>';
}
for example, if the 200th character in item20, it should be broken in item19, output 19 menu lists in this foreach.
You could use substr() as other comments say. To preserve words split, have look at this thread.
Anyway, there's a better way to play with string physical length, but it involves JavaScript.
All you have to do is to create invisible div containing string you want to measure, and then getting it's width and height. Why would you do that? Of course because each letter has different width, also various browsers on various OSes display fonts in little bit other way.
Try the following:
$array = array("item1","item2","item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10", "item11", "item12", "item13", "item14", "item15", "item16", "item17", "item18", "item19", "item20", "item21", "item22", "item23", "item24", "item25", "item26", "item27", "item28", "item29", "item30", "item31", "item32", "item33", "item34", "item35", "item36", "item37", "item38", "item39", "item40", "item41", "item42", "item43", "item44", "item45", "item46", "item47", "item48", "item49", "item50", "item51", "item52", "item53", "item54", "item55", "item56", "item57", "item58", "item59", "item60", "item61", "item62", "item63", "item64", "item65", "item66", "item67", "item68", "item69", "item70", "item71", "item72", "item73", "item74", "item75", "item76", "item77", "item78", "item79", "item80", "item81", "item82", "item83", "item84", "item85", "item86", "item87", "item88", "item89", "item90", "item91", "item92", "item93", "item94", "item95", "item96", "item97", "item98", "item99", "item100");
$max = 200;
$length = 0;
$menu_items = array();
foreach($array as $value){
$length += strlen($value);
if($length <= $max){
$menu_items[] = $value;
}else{
break;
}
}
// Outputing the menu:
echo '<ul><li>'. implode('</li><li>', $menu_items) .'</li></ul>';
Online demo.
You can check it at this part:
foreach($a as $b){
if(strlen($c)<200) $c.=$b.'|'; else return;
}
I wonder if anyone can help with a little problem I can't seem to fix - my
head is going round in circles at the moment...
Ok I have a .txt file with numerous lines of info - I am trying to match keywords
with those lines and display a certain number of the matching lines.
I put together this bit of script and whilst it works it only matches a line if the
words are in the same order as the search words.
At the moment as an example:
Search words:
red hat
Lines in .txt file:
this is my red hat
my hat is red
this hat is green
this is a red scarf
your red hat is nice
As the script is at the moment it will match and display lines 1, 5
However I would like it to match and display lines 1, 2, 5
Any order but all words must be present to match.
I have looked through loads of postings here and elsewhere and I understand that
what is needed is to explode the string and then search for each word in a loop but
I cannot get that to work, despite trying a few different ways as it just returns the
same line numerous times.
Any help would be appreciated before I lose what hair I have left :-)
Here is the code I have working at present - the search variable is already
set:
<?php
rawurldecode($search);
$search = preg_replace('/[^a-z0-9\s]|\n|\r/',' ',$search);
$search = strtolower($search);
$search = trim($search);
$lines = file('mytextfile.txt') or die("Can't open file");
shuffle($lines);
$counter = 0;
// Store true when the text is found
$found = false;
foreach($lines as $line)
{
if(strpos($line, $search) !== false AND $counter <= 4)
{
$found = true;
$line = '<img src=""> '.$line.'<br>';
echo $line;
$counter = $counter + 1;
}
}
// If the text was not found, show a message
if(!$found)
{
echo $noresultsmessage;
}
?>
Thanks in advance for any help - still learning :-)
Here's my code:
$searchTerms = explode(' ', $search);
$searchCount = count($searchTerms);
foreach($lines as $line)
{
if ($counter <= 4) {
$matchCount = 0;
foreach ($searchTerms as $searchWord) {
if (strpos($line, $searchWord) !== false ) {
$matchCount +=1;
} else {
//break out of foreach as no need to check the rest of the words if one wasn't found
continue;
}
}
if ($matchCount == $searchCount) {
$found = true;
$line = '<img src=""> '.$line.'<br>';
echo $line;
$counter = $counter + 1;
}
}
}
I am using the following code to pull some keywords and add them as tags in wordpress.
if (!is_array($keywords)) {
$count = 0;
$keywords = explode(',', $keywords);
}
foreach($keywords as $thetag) {
$count++;
wp_add_post_tags($post_id, $thetag);
if ($count > 3) break;
}
The code will fetch only 4 keywords, However on top of that, I want to pull ONLY if they are higher than 2 characters, so i dont get tags with 2 letters only.
Can someone help me.
strlen($string) will give you the lenght of the string:
if (!is_array($keywords)) {
$count = 0;
$keywords = explode(',', $keywords);
}
foreach($keywords as $thetag) {
$thetag = trim($thetag); // just so if the tags were "abc, de, fgh" then de won't be selected as a valid tag
if(strlen($thetag) > 2){
$count++;
wp_add_post_tags($post_id, $thetag);
}
if ($count > 3) break;
}
Use strlen to check the length.
int strlen ( string $string )
Returns the length of the given string.
if(strlen($thetag) > 2) {
$count++;
wp_add_post_tags($post_id, $thetag);
}