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;
}
}
}
Related
I've been pulling my hair for the couple last hours, I can't figure this out, all I'm trying to do is to take 1 line from list.txt file then search for a match in source.txt file, here is my code
<?php
//Let's open the file
$list = #fopen("files/list.txt", "r");
$source = #fopen("files/source.txt", "r");
//I'm calculating number of lines in list.txt
$no_of_lines = count(file('files/list.txt'));
//I created 2 loops
//The first loop is to repeat the process based on the total number of lines in list.txt
//the second loop is the extract only 1 entry from the list.txt and search for a match in source.txt
for ($x=1; $x <= $no_of_lines ; $x++) {
for ($i=1; $i <= 1 ; $i++) {
$getLine = fgets($list);
$matches = array();
if ($source)
{
while (!feof($source))
{
$buffer = fgets($source);
if(strpos($source, $getLine) !== FALSE)
$matches[] = $buffer;
}
fclose($source);
}
}
}
//show results:
print_r($matches);
+source.txt has numbers from 1 to 100, each number in a separate line.
+list.txt has these numbers:
5
20000
1000000
87456
Current Error: Warning: strpos() expects parameter 1 to be string, resource given in C:\laragon\www\SearchFind\test2.php on line 26
I've tried many stackexchange solutions but nothing worked out.
There's no need to count the lines in list.txt first. Just loop calling fgets() until you get to the end of the file.
You need to initialize the $matches array before the loop. Otherwise you clear it out before searching for each number, and the final value will just be the matches for the last number in list.txt.
You need to reopen source.txt each time through the outer loop so you can read from it again.
You should be searching $buffer in the strpos() call, not $source (that's the reason for the error you're getting).
Don't use while (!feof($source)). Use while ($buffer = fgets($source)). fgets() returns FALSE when you get to the end of the file.
You need to use rtrim($getLine) and `rtrim($buffer) to remove the newline at the end of the line, so you're just searching for the number.
for ($i=1; $i <= 1 ; $i++) serves no purpose at all. It just loops one time, which is the same as not looping at all.
If you want to match the whole line, not just look for a substring, use === to compare $getLine and $buffer, not strpos().
<?php
//Let's open the file
$list = fopen("list.txt", "r");
$matches = array();
if ($list) {
while ($getLine = fgets($list)) {
$getLine = rtrim($getLine); // remove newline
$source = fopen("source.txt", "r");
if ($source)
{
while ($buffer = fgets($source)) {
$buffer = rtrim($buffer);
if($buffer === $getLine)
$matches[] = $buffer;
}
fclose($source);
}
}
}
//show results:
print_r($matches);
Just like the title of this post says, I would to be able to check if every letter of a word is found in another word. So far these are the lines of codes that I was able to come up with:
<?php
$DBword = $_POST['DBword'];
$inputWords = $_POST['inputWords'];
$inputCount = str_word_count($inputWords,1);
echo "<b>THE WORD:</b>"."<br/>".$DBword."<br/><br/>";
echo "<b>WORDS ENTERED:</b><br/>";
foreach($inputCount as $outputWords)
{
echo $outputWords."<br/>";
}
foreach($inputCount as $countWords)
{
for($i=0; $i<strlen($countWords); $i++)
{$count = strpos( "$DBword", $countWords[$i]);}
if($count === false)
{
$score++;
}
}
echo "<b><br/>TOTAL SCORE: </b>";
echo $score;
?>
My point in having the foreach with the $outputWords is to just output the letters entered.
As for the other foreach that has $countWords, I am using it to really check if all letters in the word entered are found in the $DBword. I am using the for loop to check every letter.
So far, I am not getting the output that I want and I just ran out of ideas. Any ideas please?
function contains_letters($word1, $word2) {
for ($i = 0; $i < strlen($word1); $i++)
if (strpos($word2, $word1{$i}) === false)
return false;
return true;
}
//example usage
if (contains_letters($_POST['inputWords'], $_POST['DBword']))
echo "All the letters were found.";
If this check should be case-insensitive (i.e. 'A' counts as a usage of 'a'), change strpos to stripos.
Since you are overwriting $count in the for loop for each letter in $countWords, $count will contain the position of the last letter of $countWord only. Also, I am not sure why you increase score when the letter wasn't found.
In any case, you are making your life more difficult than necessary.
PHP has a function for counting chars in a string:
return count_chars($dbWord, 3) === count_chars($inputWord, 3);
will return true if the same letters are found in both strings.
Example to find all the words having exactly the same letters:
$dbWord = count_chars('foobar', 3);
$inputWords = 'barf boo oof raboof boarfo xyz';
print_r(
array_filter(
str_word_count($inputWords, 1),
function($inputWord) use ($dbWord) {
return count_chars($inputWord, 3) === $dbWord;
}
)
);
will output "raboof" and "boarfo" only.
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);
}
Sorry for the long title.
Wanted it to be as descriptive as possible.
Disclaimer : Could find some "find the differences" code here and elsewhere on Stackoverflow, but not quite the functionality I was looking for.
I'll be using these terminoligy later on:
'userguess' : a word that will be entered by the user
'solution' : the secret word that needs to be guessed.
What I need to create
A word guessing game where:
The user enters a word (I'll make sure through Javascript/jQuery that
the entered word contains the same number of letters as the word to
be guessed).
A PHP function then checks the 'userguess' and highlights (in green)
the letters in that word which are in the correct place, and
highlights (in red) the letters that are not yet in the right place,
but do show up somewhere else in the word. The letters that don't
show up in the 'solution' are left black.
Pitfall Scenario : - Let's say the 'solution' is 'aabbc' and the user guesses 'abaac'
In the above scenario this would result in : (green)a(/green)(red)b(/red)(red)a(/red)(black)a(/black)(green)c(/green)
Notice how the last "a" is black cause 'userguess' has 3 a's but 'solution' only has 2
What I have so far
Code is working more or less, but I've got a feeling it can be 10 times more lean and mean.
I'm filling up 2 new Arrays (one for solution and one for userguess) as I go along to prevent the pitfall (see above) from messing things up.
function checkWord($toCheck) {
global $solution; // $solution word is defined outside the scope of this function
$goodpos = array(); // array that saves the indexes of all the right letters in the RIGHT position
$badpos = array(); // array that saves the indexes of all the right letters in the WRONG position
$newToCheck = array(); // array that changes overtime to help us with the Pitfall (see above)
$newSolution = array();// array that changes overtime to help us with the Pitfall (see above)
// check for all the right letters in the RIGHT position in entire string first
for ($i = 0, $j = strlen($toCheck); $i < $j; $i++) {
if ($toCheck[$i] == $solution[$i]) {
$goodpos[] = $i;
$newSolution[$i] = "*"; // RIGHT letters in RIGHT position are 'deleted' from solution
} else {
$newToCheck[] = $toCheck[$i];
$newSolution[$i] = $solution[$i];
}
}
// go over the NEW word to check for letters that are not in the right position but show up elsewhere in the word
for ($i = 0, $j = count($newSolution); $i <= $j; $i++) {
if (!(in_array($newToCheck[$i], $newSolution))) {
$badpos[] = $i;
$newSolution[$i] = "*";
}
}
// use the two helper arrays above 'goodpos' and 'badpos' to color the characters
for ($i = 0, $j = strlen($toCheck), $k = 0; $i < $j; $i++) {
if (in_array($i,$goodpos)) {
$colored .= "<span class='green'>";
$colored .= $toCheck[$i];
$colored .= "</span>";
} else if (in_array($i,$badpos)) {
$colored .= "<span class='red'>";
$colored .= $toCheck[$i];
$colored .= "</span>";
} else {
$colored .= $toCheck[$i];
}
}
// output to user
$output = '<div id="feedbackHash">';
$output .= '<h2>Solution was : ' . $solution . '</h2>';
$output .= '<h2>Color corrected: ' . $colored . '</h2>';
$output .= 'Correct letters in the right position : ' . count($goodpos) . '<br>';
$output .= 'Correct letters in the wrong position : ' . count($badpos) . '<br>';
$output .= '</div>';
return $output;
} // checkWord
Nice question. I'd probably do it slightly differently to you :) (I guess that's what you were hoping for!)
You can find my complete solution function here http://ideone.com/8ojAG - but I'm going to break it down step by step too.
Firstly, please try and avoid using global. There's no reason why you can't define your function as:
function checkWord($toCheck, $solution) {
You can pass the solution in and avoid potential nasties later on.
I'd start by splitting both the user guess, and the solution into arrays, and have another array to store my output in.
$toCheck = str_split($toCheck, 1);
$solution = str_split($solution, 1);
$out = array();
At each stage of the process, I'd remove the characters that have been identified as correct or incorrect from the users guess or the solution, so I don't need to flag them in any way, and the remaining stages of the function run more efficiently.
So to check for matches.
foreach ($toCheck as $pos => $char) {
if ($char == $solution[$pos]) {
$out[$pos] = "<span class=\"green\">$char</span>";
unset($toCheck[$pos], $solution[$pos]);
}
}
So for your example guess/solution, $out now contains a green 'a' at position 0, and a green c at position 4. Both the guess and the solution no longer have these indices, and will not be checked again.
A similar process for checking letters that are present, but in the wrong place.
foreach ($toCheck as $pos => $char) {
if (false !== $solPos = array_search($char, $solution)) {
$out[$pos] = "<span class=\"red\">$char</span>";
unset($toCheck[$pos], $solution[$solPos]);
}
}
In this case we are searching for the guessed letter in the solution, and removing it if it is found. We don't need to count the number of occurrences because the letters are removed as we go.
Finally the only letters remaining in the users guess, are ones that are not present at all in the solution, and since we maintained the numbered indices throughout, we can simply merge the leftover letters back in.
$out += $toCheck;
Almost there. $out has everything we need, but it's not in the correct order. Even though the indices are numeric, they are not ordered. We finish up with:
ksort($out);
return implode($out);
The result from this is:
"<span class="green">a</span><span class="red">b</span><span class="red">a</span>a<span class="green">c</span>"
Here try this, See In Action
Example output:
<?php
echo checkWord('aabbc','abaac').PHP_EOL;
echo checkWord('funday','sunday').PHP_EOL;
echo checkWord('flipper','ripple').PHP_EOL;
echo checkWord('monkey','kenney').PHP_EOL;
function checkWord($guess, $solution){
$arr1 = str_split($solution);
$arr2 = str_split($guess);
$arr1_c = array_count_values($arr1);
$arr2_c = array_count_values($arr2);
$out = '';
foreach($arr2 as $key=>$value){
$arr1_c[$value]=(isset($arr1_c[$value])?$arr1_c[$value]-1:0);
$arr2_c[$value]=(isset($arr2_c[$value])?$arr2_c[$value]-1:0);
if(isset($arr2[$key]) && isset($arr1[$key]) && $arr1[$key] == $arr2[$key]){
$out .='<span style="color:green;">'.$arr2[$key].'</span>';
}elseif(in_array($value,$arr1) && $arr2_c[$value] >= 0 && $arr1_c[$value] >= 0){
$out .='<span style="color:red;">'.$arr2[$key].'</span>';
}else{
$out .='<span style="color:black;">'.$arr2[$key].'</span>';
}
}
return $out;
}
?>
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.