I have this script that reads the last 100 lines from a text file, I explode out each line into 3 chunks were separated by a space. The third chunk of data I want to check to see if it contains a word or phrase from one of the lines in another text file filters.txt. If a match is found, then I want to replace that word or phrase with some formatting so it stands out. Below is what I have but it does not work, the code that checks against the filters has no effect.
Can anyone please show me how I should do this.
Thanks in advance.
Here is a line from sample.log...
12:12:46 18-10-18 #:10656200,1A,29D09 ,RTC NOT ALERT , ,CHEMIST WAREHOUSE , , ,910 David Low Way ,MARCOOLA ,S59K7 , , ,
In the filters.txt file will be lines such as...
RTC
RTC INJURIES
RTC NOT ALERT
RTC HIGH MECH
In above exmaple when I print out the data I want the 'RTC NOT ALERT' to be highlighted so it stands out. So in brief, for every line in sample.log I want to see if any of the filters appear in it, if so I want it to be highlighted on output. I do this by changing the font styling with css.
<?php
// OPEN LOG FILE FOR READING
$file = array_reverse( file( 'sample.log' ));
// OPEN FILTERS LIST
$filters = file( 'filters.txt' );
// START MAIN LOOP (SHOWS LAST X LINES FROM TEXT FILE)
$limit = 100;
for ($i = 0; $i < $limit; $i++ ){
$data = explode(' ', $file[$i],3);
// CHECK FOR FILTERS AND HIGHLIGHT IF MATCHED
foreach ( $filters as $filter ){
if (strpos($data[2], $filter) !== false) {
$data[2] = str_replace($filter,"<font
class=\"highlight\">$filter</font>",$data[2]);
}
}
// PRINT LIST
print "
<div class=\"code1a\">
<font class=\"time\">$data[0] $data[1]</font><br>
$data[2]
</div>
";
}
?>
Though you didn't give any example from sample.log and filters.txt, I think you need to trim each $filter because of line-break.
Try:
<?php
// OPEN LOG FILE FOR READING
$file = array_reverse( file( 'sample.log' ) );
// OPEN FILTERS LIST
$filters = file( 'filters.txt', FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES );
// START MAIN LOOP (SHOWS LAST X LINES FROM TEXT FILE)
$limit = 100;
// BECAUSE $file STARTS WITH 0
$limit -= 1;
foreach ( $file as $line => $line_content ) {
if ( $line == $limit ) {
break;
}
$data = explode( ' ', $line_content, 3 );
$highlight = $data[ 2 ];
// CHECK FOR FILTERS AND HIGHLIGHT IF MATCHED
foreach ( $filters as $filter ) {
if ( strpos( $highlight, ',' . $filter . ' ,' ) !== false ) {
$highlight = str_replace( $filter, '<font class="highlight">' . $filter . '</font>', $highlight );
}
}
// PRINT LIST
echo '<div class="code1a">';
echo '<font class="time">', $data[ 0 ], ' ', $data[ 1 ], '</font>';
echo '<br />';
echo $highlight;
echo '</div>';
}
Related
I am looking for a way to insert an ad or text after X amount of words and after the closing tag of the paragraph the last word appears in.
So far, I have only been able to do this after the X amount of characters. The problem with this approach is that HTML characters are counted which gives inaccurate results.
function chars1($content) {
// only inject google ads if post is longer than 2500 characters
$enable_length1 = 2500;
// insert after the 210th character
$after_character1 = 2100;
if (is_single() && strlen($content) > $enable_length1) {
$before_content1 = substr($content, 0, $after_character1);
$after_content1 = substr($content, $after_character1);
$after_content1 = explode('</p>', $after_content1);
ob_start();
dynamic_sidebar('single-image-ads-1');
$text1 = ob_get_contents();
ob_end_clean();
array_splice($after_content1, 1, 0, $text1);
$after_content1 = implode('', $after_content1);
return $before_content1 . $after_content1;
} else {
return $content;
}
}
//add filter to WordPress with priority 49
add_filter('the_content', 'chars1',49);
Another approach I have tried is using:
strip_tags($content)
and counted the words using:
st_word_count()
The problem with this is that I have no way of returning the $content with the HTML tags
Depending on the size of the post, I will insert up to 5 ad units, with the functions I have above I would need to create a function for each ad. If there is a way to insert all 5 ads using one function that would be great.
Any help is appreciated.
Deciding what is a word or not can oftentimes be very hard. But if you're alright with an approximate solution, like defining a word as text between two whitespaces, I suggest you implement a simple function yourself.
This may be achieved by iterating over the characters of the string until 150 words are counted and then jumping to the end of the current paragraph. Insert an ad and then repeat until you've added sufficiently many.
Implementing this in your function might look like this
function chars1($content) {
// only inject google ads if post is longer than 2500 characters
$enable_length1 = 2500;
// Insert at the end of the paragraph every 300 words
$after_word1 = 300;
// Maximum of 5 ads
$max_ads = 5;
if (strlen($content) > $enable_length1) {
$len = strlen($content);
$i=0;
// Keep adding untill end of content or $max_ads number of ads has ben inserted
while($i<$len && $max_ads-->0) {
// Work our way untill the apropriate length
$word_cout = 0;
$in_tag = false;
while(++$i < $len && $word_cout < $after_word1) {
if(!$in_tag && ctype_space($content[$i])) {
// Whitespace
$word_cout++;
}
else if(!$in_tag && $content[$i] == '<') {
// Begin tag
$in_tag = true;
$word_cout++;
}
else if($in_tag && $content[$i] == '>') {
// End tag
$in_tag = false;
}
}
// Find the next '</p>'
$i = strpos($content, "</p>", $i);
if($i === false) {
// No more paragraph endings
break;
}
else {
// Add the length of </p>
$i += 4;
// Get ad as string
ob_start();
dynamic_sidebar('single-image-ads-1');
$ad = ob_get_contents();
ob_end_clean();
$content = substr($content, 0, $i) . $ad . substr($content, $i);
// Set the correct i
$i+= strlen($ad);
}
}
}
return $content;
}
With this approach, it's easy to add new rules.
I've just had to do this myself. This is how I did it. First explode the content on </p> tags. Loop over the resulting array, put the end </p> back onto the paragraph, do a count on the paragraph with the tags stripped and add it to the global count. Compare the global word count against our word positions. If it's greater, append the content and unset that word position. Stringify and return.
function insert_after_words( $content, $words_positions = array(), $content_to_insert = 'Insert Me' ) {
$total_words_count = 0;
// Explode content on paragraphs.
$content_exploded = explode( '</p>', $content );
foreach ( $content_exploded as $key => $content ) {
// Put the paragraph tags back.
$content_exploded[ $key ] .= '</p>';
$total_words_count += str_word_count( strip_tags( $content_exploded[ $key ] ) );
// Check the total word count against the word positoning.
foreach ( $words_positions as $words_key => $words_count ) {
if ( $total_words_count >= $words_count ) {
$content_exploded[ $key ] .= PHP_EOL . $content_to_insert;
unset( $words_positions[ $words_key ] );
}
}
}
// Stringify content.
return implode( '', $content_exploded );
}
i have a big string that has many lines of data as shown below(stream name and stream url). I want to put each line into any array then separate each array value to stream name and stream url then construct corresponding hyperlink for each stream line. could any one show me how this can be done?
Edit:
i put each line in to array but i couldnt split each line in to two parts1! my names array is empty! could any one tell me what is wrong ?
$stream_array = explode( "\n", $file_contents );
$names = array();
foreach( $stream_array as $stream ){
$split = explode( $stream, " " );
array_push( $names, $split[ 0 ] );
};
print_r($stream_array);
print_r($names);
i want to put stream names in to names array and stream urls into foo array after i put each line of $file_contents into an array .
for($i = 0; $i < count($foo[1]); $i++){
?>
<?php echo $i.") "; echo $names[0][$i] ;?> <br />
<?
}//end of for
example of big string that holds list of stream($file_contents):
name1 http://somesite.net/all/name1tv.m3u8
name2 http://somesite.net/all/name2tv.m3u8
name3 http://somesite.net/all/name3tv.m3u8
Its quite easy and there are PHP syntax that does most of it for you like so :-
// Get a file into an array.
// Each line will be added as a new occurance of the $lines array
$lines = file('filename.ext');
foreach ( $lines as $line ) {
list($name, $url) = explode(' ', $line);
echo $name . PHP_EOL;
echo $url . PHP_EOL;
// of course you will probably want to do something different here
// this just shows you whats going on.
}
The result would be :
name1
http://somesite.net/all/name1tv.m3u8
name2
http://somesite.net/all/name2tv.m3u8
name3
http://somesite.net/all/name3tv.m3u8
i solved it :-)
$stream_array = explode( "\n", $file_contents );
$names = array();
foreach( $stream_array as $stream ){
//$split = explode( $stream, " " );
$split = preg_split('/\s+/', $stream);
array_push( $names, $split[ 0 ] );
};
print_r($stream_array);
print_r($names);
I would like to scan a large piece of text using PHP and find all matches for a pattern, but then also 2 lines above the match and 2 lines below.
My text looks like this, but with some extra unnecessary text above and below this sample:
1
Description text
123.456.12
10.00
10.00
3
Different Description text
234.567.89
10.00
30.00
#Some footer text that is not needed and will change for each text file#
15
More description text
564.238.02
4.00
60.00
15
More description text
564.238.02
4.00
60.00
#Some footer text that is not needed and will change for each text file#
15
More description text
564.238.02
4.00
60.00
15
More description text
564.238.02
4.00
60.00
Using PHP, I am looking to match each number in bold (always same format - 3 numbers, dot, 3 numbers, dot, 2 numbers) but then also return the previous 2 lines and the next 2 lines and hopefully return an array so that I can use:
$contents[$i]["qty"] = "1";
$contents[$i]["description"] = "Description text";
$contents[$i]["price"] = "10.00";
$contents[$i]["total"] = "10.00";
etc...
Is this possible and would I use regex? Any help or advice would be greatly appreciated!
Thanks
ANSWERED BY vzwick
This is my final code that I used:
$items_array = array();
$counter = 0;
if (preg_match_all('/(\d+)\n\n(\w.*)\n\n(\d{3}\.\d{3}\.\d{2})\n\n(\d.*)\n\n(\d.*)/', $text_file, $matches)) {
$items_string = $matches[0];
foreach ($items_string as $value){
$item = explode("\n\n", $value);
$items_array[$counter]["qty"] = $item[0];
$items_array[$counter]["description"] = $item[1];
$items_array[$counter]["number"] = $item[2];
$items_array[$counter]["price"] = $item[3];
$items_array[$counter]["total"] = $item[4];
$counter++;
}
}
else
{
die("No matching patterns found");
}
print_r($items_array);
$filename = "yourfile.txt";
$fp = #fopen($filename, "r");
if (!$fp) die('Could not open file ' . $filename);
$i = 0; // element counter
$n = 0; // inner element counter
$field_names = array('qty', 'description', 'some_number', 'price', 'total');
$result_arr = array();
while (($line = fgets($fp)) !== false) {
$result_arr[$i][$field_names[$n]] = trim($line);
$n++;
if ($n % count($field_names) == 0) {
$i++;
$n = 0;
}
}
fclose($fp);
print_r($result_arr);
Edit: Well, regex then.
$filename = "yourfile.txt";
$file_contents = #file_get_contents($filename);
if (!$file_contents) die("Could not open file " . $filename . " or empty file");
if (preg_match_all('/(\d+)\n\n(\w.*)\n\n(\d{3}\.\d{3}\.\d{2})\n\n(\d.*)\n\n(\d.*)/', $file_contents, $matches)) {
print_r($matches[0]);
// do your matching to field names from here ..
}
else
{
die("No matching patterns found");
}
(.)+\n+(.)+\n+(\d{3}\.\d{3}\.\d{2})\n+(.)+\n+(.)+
It might be necessary to replace \n with \r\n. Make sure the regex is in a mode when the "." doesn't match with the new line character.
To reference groups by names, use named capturing group:
(?P<name>regex)
example of named capturing groups.
You could load the file in an array, and them use array_slice, to slice each 5 blocks of lines.
<?php
$file = file("myfile");
$finalArray = array();
for($i = 0; $i < sizeof($file); $i = $i+5)
{
$finalArray[] = array_slice($file, $i, 5);
}
print_r($finalArray);
?>
Hy everyone, I'm having trouble with properly nesting while loops to read from 2 arrays.
I have 2 files from which I read the content:
file1: item_list.txt
string1 \n
string2 \n
string3 \n
...
file2: item+info.txt
string3 \t info1 \t info2 \t info3
string1 \t info7 \t info1 \t info4
string5 \t info2 \t info3
string2 \t info2 \t info4 \t info1
(values are separated by new lines and tabs only, I added one space between characters here just to increase readability).
I read from files using fgetcsv() function, and each row from file is stored as an array into a variable $data. I created a while loop with condition (!feof($fp)) to read through the file until the last row. But I can't quite properly nest the second loop.
What I want to do with this:
read the first string found in file1, go to file2 and try to find that string. If there's a match, get the info data for that string (all of the data, or just one, doesn't matter). If there's no match, return message "no match". In either case, once the second loop has done it's thing, I need to read the second string in file1, and do the search in file2 again. Repeat this as long as there is something to read from the file1.
here are two versions of my code, they don't work, and I can't figure out why.
//opening the files
$fp = fopen("$DOCUMENT_ROOT/test/item_list.txt", "r"); #raw item list
$pf = fopen("$DOCUMENT_ROOT/test/item+info.txt", "r"); #item+info list
//read from first file
$line=0;
while (!feof($fp)){
$line++;
$data1 = fgetcsv($fp, "1000", "\n");
$item1= $data1[0];
echo "line: $line. item_list: ".$item1; //just to see on screen what's happening
print_r($data1); //same here, just to see what's going on
echo"<br />";
//searching for string in file2
$row=0;
while (!feof($pf)){
$row++;
$data2 = fgetcsv($pf, "1000", "\t");
$item2= $data2[0];
echo "line: $row. item+info: ".$item2; //just checking things on screen
print_r($data2); //here too
echo "<br />";
//conditioning
//equal strings
if ($string1== $string2)
echo $data2[1]."<br />";
break;
}
}
fclose($fp);
fclose($pf);
this used to work as long as the items in item_list.txt and item+info.txt are oredered
exactly the same (string1\nstring2\string3 ->
string1\tinfo1\nstring2\tinfo2\nstring3\tinfo3 - but that's never going to happen in my
case, it's impossible to order the items like that)
I tried to do it with foreach() statement do itterate through arrays, but the result is something that I can't make any sense out of.
while (!feof($fp)){
$data1 = fgetcsv($fp);
foreach ($data1 as $token1) {
while (!feof($pf)) {
$data2 = fgetcsv($pf);
foreach ($data2 as $value) {
explode ("\t", $value);
if ($token1 == $value[0])
echo $value[1];
}
break;
}
}
}
This should do it:
$file1 = file($DOCUMENT_ROOT . '/test/item_list.txt');
$file2 = file($DOCUMENT_ROOT . '/test/item+info.txt');
foreach ($file1 as $line)
{
$line = rtrim($line); // just in case ...
if ($line === '') continue;
foreach($file2 as $infoline)
{
$infoline = explode("\t", rtrim($infoline);
if ($line === $infoline[0])
{
array_shift($infoline);
echo $line . '<br /><br />' . implode('<br />', $infoline);
// $results[$line] = $infoline; // uncomment this if you need the search results stored for later use
break;
}
}
}
Here's a rough shot at it:
$filename1 = 'item_list.txt';
$filename2 = 'item+info.txt';
# Init the Raw Array
$content2raw = array();
# Get the File Contents for File 2
$file2 = file_get_contents( $filename2 );
# Split it into an Array by Line Breaks
$content2raw = preg_split( "/\n/" , $file2 , -1 , PREG_SPLIT_NO_EMPTY );
# Unset the variable holding the file contents
unset( $file2 );
# Init the Fixed Array
$content2 = array();
# Loop through the Raw Array
foreach( $content2raw as $l ){
// Each Line of Filename2
# Split the Line on Tabs
$t = preg_split( "/\s*\t\s*/" , $l , -1 );
# Set the Fixed Array, using the first element from the line as the key
$content2[ $t[0] ] = $t;
}
# Unset the Raw Array
unset( $content2raw );
# Get the File Contents from File 1
$file1 = file_get_contents( $filename1 );
# Split it into an Array by Line Breaks
$contents1 = preg_split( "/\n/" , $file1 , -1 , PREG_SPLIT_NO_EMPTY );
# Unset the variable holding the file contents
unset( $file1 );
# Loop through the Lines, using each line as the Key to look for
foreach( $content1 as $v ){
# Check whether a matching element exists in the array from File 2
if( !array_key_exists( $k , $content2 ) ){
// No Match Found
echo 'No Match';
}else{
// Match Found
echo 'Match Found';
var_dump( $content2[$v] );
}
}
Amendment, as per comment/feedback from #Bluewind
$filename1 = 'item_list.txt';
$filename2 = 'item+info.txt';
# Open and Split the file into an Array by Line
$content2raw = file( $filename2 );
# Init the Fixed Array
$content2 = array();
# Loop through the Raw Array
foreach( $content2raw as $l ){
// Each Line of Filename2
# Split the Line on Tabs
$t = preg_split( "/\s*\t\s*/" , $l , -1 );
# Set the Fixed Array, using the first element from the line as the key
$content2[ $t[0] ] = $t;
}
# Unset the Raw Array
unset( $content2raw );
# Open and Split the file into an Array by Line
$contents1 = file( $filename1 );
# Loop through the Lines, using each line as the Key to look for
foreach( $content1 as $v ){
# Check whether a matching element exists in the array from File 2
if( !array_key_exists( $k , $content2 ) ){
// No Match Found
echo 'No Match';
}else{
// Match Found
echo 'Match Found';
var_dump( $content2[$v] );
}
}
This is actually much less code than you seem to think. First, you read an info file and build a hash table out of it:
foreach(file("info_list") as $line) {
$line = explode("\t", trim($line));
$info[$line[0]] = $line;
}
then you iterate through the items file and look if there are matching entries in the hash:
foreach(file("item_list") as $line) {
$item = trim($line);
if(isset($info[$item]))
// we have some info for this item
else
// we have no info for this item
}
that's basically all about this
I'm trying to remove some excessive indention from a string, in this case it's SQL, so it can be put into a log file. So I need the find the smallest amount of indention (aka tabs) and remove it from the front of each line, but the following code ends up printing out exactly the same, any ideas?
In other words, I want to take the following (NOTE: StackOverflow editor converted my tabs to spaces, in the code, a tab simulates 4 spaces, but it really is a \t character)
SELECT
blah
FROM
table
WHERE
id=1
and convert it to
SELECT
blah
FROM
table
WHERE
id=1
here's the code I tried and fails
$sql = '
SELECT
blah
FROM
table
WHERE
id=1
';
// it's most likely idented SQL, remove any idention
$lines = explode("\n", $sql);
$space_count = array();
foreach ( $lines as $line )
{
preg_match('/^(\t+)/', $line, $matches);
$space_count[] = strlen($matches[0]);
}
$min_tab_count = min($space_count);
$place = 0;
foreach ( $lines as $line )
{
$lines[$place] = preg_replace('/^\t{'. $min_tab_count .'}/', '', $line);
$place++;
}
$sql = implode("\n", $lines);
print '<pre>'. $sql .'</pre>';
It seems the problem was
strlen($matches[0])
returns 0 and 1 for the first and last line, which isn't the 3 I actually wanted as the minimum, so a quick hack was to
trim the SQL
skip counting the length if it's less than 2
Not the most elegant solution, but it'll always work because tabs are usually in the 4+ count in this code. Here's the fixed code:
$sql = '
SELECT
blah
FROM
table
WHERE
id=1
';
// it's most likely idented SQL, remove any idention
$lines = explode("\n", $sql);
$space_count = array();
foreach ( $lines as $line )
{
preg_match('/^(\t+)/', $line, $matches);
if ( strlen($matches[0]) > 1 )
{
$space_count[] = strlen($matches[0]);
}
}
$min_tab_count = min($space_count);
$place = 0;
foreach ( $lines as $line )
{
$lines[$place] = preg_replace('/^\t{'. $min_tab_count .'}/', '', $line);
$place++;
}
$sql = implode("\n", $lines);
print $sql;
private function cleanIndentation($str) {
$content = '';
foreach(preg_split("/((\r?\n)|(\r\n?))/", trim($str)) as $line) {
$content .= " " . trim($line) . PHP_EOL;
}
return $content;
}