I'm looking for a solution on how to print only relevant sections from a log file where I want to display the code and its contents which spans multiple lines.
The log file is structured like this:
----- start -----
CODE: 1111
DESC: this is some descriptions
which spans multiple lines.
----- end -----
----- start -----
CODE: 2222
DESC: this is some information
for another code.
----- end -----
----- start -----
CODE: 1111
DESC: here is some more info
for the code again.
----- end -----
What I like to achieve if to parse the log file using C, to generate a result similar to this.
----- start -----
CODE: 1111
DESC: this is some descriptions
which spans multiple lines.
----- end -----
----- start -----
CODE: 1111
DESC: here is some more info
for the code again.
----- end -----
I have tried various while loops reading the file into a buffer and compared the strings with strstr() etc. but haven't find any logic with if statements that works for me. I have tried to explore the use of fseek(), fgetpos(), fsetpos() etc. I have searched forums and blogs for answer that can help me forward with little success.
If anyone reading this has a solution to share or any pointer on how I should tackle this, or places to find such information would be much appreciated.
CodePudding user response:
Forget about the "---start---" to begin with. Just look for the target that means an interesting section is beginning. Then output until the "---end---" is encountered.
Here's a sample that works for the sample input you provided. Just uses fgets()
until it finds the target string, sets a flag and outputs the preamble (that is given) and loops until it clears the flag. If the current line is neither the start nor the finish, output (or not) is controlled by the flag.
You can adapt this to your particular needs with passing logfile names and target string(s) through command line parameters.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
int main() {
char buf[ 1024 ];
FILE *ifp = fopen( "log.txt", "rt" );
if( ifp == NULL )
exit( EXIT_FAILURE );
char *target = "CODE: 1111";
char *start = "----- start -----";
char *end = "----- end -----";
char *blankrow = "";
bool inRegion = false;
while( fgets( buf, sizeof buf, ifp ) ) {
if( inRegion && strncmp( buf, end, strlen( end ) ) == 0 ) {
inRegion = false;
printf( "%s", buf );
} else if( strncmp( buf, target, strlen( target ) ) == 0 ) {
inRegion = true;
printf( "%s%s\n%s", blankrow, start, buf );
blankrow = "\n";
} else if( inRegion ) {
printf( "%s", buf );
}
}
fclose( ifp );
return 0;
}
CodePudding user response:
In order to solve the problem, you can read until you encounter the line
----- start -----
then read the next line to see if it contains the code that you are looking for. If it does, then you output the log entry, and if it does not, then you read and ignore the rest of the log entry.
Since the end of the log entry is clearly marked with the line
----- end -----
you can compare every line inside the log entry with that string.
These steps should be repeated until you encounter end-of-file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
const char start_marker[] = "----- start -----";
const char end_marker[] = "----- end -----";
const char target_string[] = "CODE: 1111";
bool read_exactly_one_line( char buffer[], int buffer_size, FILE *fp );
int main( void )
{
//const size_t start_marker_length = sizeof start_marker - 1;
//const size_t end_marker_length = sizeof end_marker - 1;
char line[4096];
bool should_print;
//read log entries until end-of-file encountered
for (;;) //infinite loop, equivalent to "while(1)"
{
//read until start marker encountered
for (;;)
{
if ( ! read_exactly_one_line( line, sizeof line, stdin ) )
{
//we are unable to read any further lines due to
//end-of-file, so wie have finished
exit( EXIT_SUCCESS );
}
//determine whether line is empty
if ( line[0] == '\0' )
continue;
//determine whether line is a start marker
if ( strcmp( line, start_marker ) == 0 )
break;
//line is neither empty nor a start marker, which
//should not occur
fprintf( stderr, "Parse error: Unexpected input!\n" );
exit( EXIT_FAILURE );
}
//read the line with the code
if ( ! read_exactly_one_line( line, sizeof line, stdin ) )
{
fprintf( stderr, "Error: Encountered end-of-file inside log entry!\n" );
exit( EXIT_FAILURE );
}
//determine whether the line contains the target string
if ( strcmp( line, target_string ) == 0 )
{
//we should print the log entry
should_print = true;
//also print the part that we have already read
printf( "%s\n%s\n", start_marker, line );
}
else
{
//do not print the remainder of the log entry
should_print = false;
}
//read until end marker
for (;;)
{
if ( ! read_exactly_one_line( line, sizeof line, stdin ) )
{
fprintf( stderr, "Error: Encountered end-of-file inside log entry!\n" );
exit( EXIT_FAILURE );
}
//print the line, if appropriate
if ( should_print )
printf( "%s\n", line );
//determine whether line is the end marker
if ( strcmp( line, end_marker ) == 0 )
{
//add one line of spacing between next log entry
if ( should_print )
printf( "\n" );
//break out of inner infinite loop
break;
}
}
}
}
//This function will read exactly one line and remove the newline
//character, if it exists. On success, it will return true. If this
//function is unable to read any further lines due to end-of-file,
//it returns false. If it fails for any other reason, it will not
//return, but will print an error message and call "exit" instead.
bool read_exactly_one_line( char buffer[], int buffer_size, FILE *fp )
{
char *p;
if ( fgets( buffer, buffer_size, fp ) == NULL )
{
if ( ferror( fp ) )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
return false;
}
//make sure that line was not too long for input buffer
p = strchr( buffer, '\n' );
if ( p == NULL )
{
//a missing newline character is ok on end of file
if ( !feof(fp) )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character
*p = '\0';
}
return true;
}
With the input posted in the question, this program has the following output:
----- start -----
CODE: 1111
DESC: this is some descriptions
which spans multiple lines.
----- end -----
----- start -----
CODE: 1111
DESC: here is some more info
for the code again.
----- end -----