How can I read different lines of a text file with fgets? - c

For example, if a .txt has
Hello
There.
written in it, no matter how bigger N is in fgets(str, N, file), it will only store "Hello" in str, because it stops when it finds a '\n' character.
So, how could I read the whole file if, for example, I wanted to find a specific word in it?

So, how could I read the whole file
In order to read the whole file into a memory buffer, you could use the function fread. After turning the input into a string by appending a terminating null character, you could then use the function strstr to search the input for a certain word.
Here is a program which does this and searches the input for the word targetword:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( void )
{
FILE *fp;
char buffer[1000];
size_t read;
//open input file
fp = fopen( "input.txt", "rt" );
if ( fp == NULL )
{
fprintf( stderr, "ERROR: Unable to open input file!\n" );
exit( EXIT_FAILURE );
}
//read entire file into buffer
read = fread( buffer, 1, sizeof buffer, fp );
//verify that buffer was not too small
if ( read == sizeof buffer )
{
fprintf( stderr, "ERROR: Memory buffer is too small to contain entire input!\n" );
exit( EXIT_FAILURE );
}
//add terminating null character to make input a valid
//null-terminated string
buffer[read] = '\0';
//search input for target word
if ( strstr( buffer, "targetword" ) != NULL )
printf( "Found word!\n" );
else
printf( "Did not find word!\n" );
fclose( fp );
}
However, instead of reading the entire file at once (which could require a very large memory buffer), it is more common to read one line at a time in a loop, and in every loop iteration, you check whether the current line contains the word you are looking for. That way, the memory buffer only has to be large enough to store one line of input at once, instead of the entire input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
int main( void )
{
FILE *fp;
char line[100];
bool found = false;
//open input file
fp = fopen( "input.txt", "rt" );
if ( fp == NULL )
{
fprintf( stderr, "ERROR: Unable to open input file!\n" );
exit( EXIT_FAILURE );
}
//read one line per loop iteration
while ( fgets( line, sizeof line, fp ) != NULL )
{
//verify that line was not too long to fit into buffer
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "line too long to fit buffer!\n" );
exit( EXIT_FAILURE );
}
//search for target word
if ( strstr( line, "targetword" ) != NULL )
{
found = true;
break;
}
}
if ( found )
printf( "Found word!\n" );
else
printf( "Did not find word!\n" );
fclose( fp );
}
However, both solutions have several possible issues:
If the target word targetword is part of another word, for example thetargetword, then it will state that it found the target word. I'm not sure if this is what you want or if you want the target word to appear by itself.
If the target word is syllabified so that, for example, target-\n appears in one line and word in the next line, then the program won't be able to find the word.
The search is case-sensitive, so it will only find targetword, but not Targetword or TARGETWORD.
All of these issues can be solved, if necessary, but would require additional work.

Related

Detect if line is the last line of a file, using only one loop?

I am writing a text file parser in C.
I would like to read each line of a text file using fgets, except for the very last line, which I would like to skip.
Also, there is no telling how many characters will be in the file or in the last line, but assume my parser only cares about the first LINEMAXLEN characters in each line.
Currently, the only way I can think to do this is by running two loops, something like the following:
char line[ LINEMAXLEN+1u ];
unsigned int nlines;
unsigned int i;
nlines = 0u;
while ( fgets (line, LINEMAXLEN, file) != NULL )
nlines += 1u;
i = 0u;
while ( fgets (line, LINEMAXLEN, file) != NULL ) {
if ( i >= nlines - 1u )
break;
//...parse the line
i += 1u;
}
But surely, there's got to be a smarter way to do it in only one loop, no?
Instead of using two loops, it would be more efficient to always read two lines in advance and to only process a line once the next line has been sucessfully read. That way, the last line will not be processed.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define LINEMAXLEN 30
//forward function declarations
void process_line( const char *line );
bool read_start_of_line_and_discard_rest( char buffer[], int buffer_size, FILE *fp );
int main( void )
{
FILE *fp;
char lines[2][LINEMAXLEN];
//This index specifies which index in the array "lines"
//represents the newest line. The other index is the
//index of the previous line.
int newest_index = 0;
//attempt to open file
fp = fopen( "input.txt", "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//read first line
if ( !read_start_of_line_and_discard_rest( lines[newest_index], LINEMAXLEN, fp ) )
{
fprintf( stderr, "Error reading first line!\n" );
exit( EXIT_FAILURE );
}
//process one line per loop iteration
for (;;)
{
//swap the index, so that the newest line is now the
//previous line
newest_index = !newest_index;
//read the new line
if ( !read_start_of_line_and_discard_rest( lines[newest_index], LINEMAXLEN, fp ) )
{
//we have reached end-of-file, so we don't process the
//previous line, because that line is the last line
break;
}
//since reading in a new line succeeded, we can be sure that
//the previous line is not the last line, so we can process
//the previous line
//process the previous line
process_line( lines[!newest_index] );
}
//cleanup
fclose( fp );
}
//This function will process a line after it has been read
//from the input file. For now, it will only print it.
void process_line( const char *line )
{
printf( "Processing line: %s\n", line );
}
//This function will read exactly one line of input 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.
//If the line is too long to fit in the buffer, it will discard
//the rest of the line and report success.
bool read_start_of_line_and_discard_rest( char buffer[], int buffer_size, FILE *fp )
{
char *p;
//attempt to read one line from the stream
if ( fgets( buffer, buffer_size, fp ) == NULL )
{
if ( ferror( fp ) )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
return false;
}
//determine whether line was too long for input buffer
p = strchr( buffer, '\n' );
if ( p == NULL )
{
int c;
//discard remainder of line
do
{
c = getchar();
} while ( c != EOF && c != '\n' );
}
else
{
//remove newline character by overwriting it with a null
//character
*p = '\0';
}
return true;
}
For the input
This is line1.
This is line2 which has an additional length longer than 30 characters.
This is line3.
This is line4.
this program has the following output:
Processing line: This is line1.
Processing line: This is line2 which has an ad
Processing line: This is line3.
As you can see, all lines except the last line are being processed, and only the first LINEMAXLEN-1 (30-1 in my example) characters of each line are being processed/stored. The remaining characters are being discarded.
Only LINEMAXLEN-1 instead of LINEMAXLEN characters from each line are being processed/stored because one character is required to store the terminating null character.
This is quite simple to do in a single loop if we use alternating buffers [as others have mentioned].
In the loop below we read a line into the "current" buffer. If not the first line, we process the previous line in the "other" buffer.
By alternating the index into a buffer pool of two buffers, we avoid unnecessary copying.
This introduces a delay in the processing of the buffer. On the last iteration, the last line will be in the current buffer, but it will not be processed.
#define LINEMAXLEN 1000 // line length of buffer
#define NBUF 2 // number of buffers
char lines[NBUF][LINEMAXLEN]; // buffer pool
int previdx = -1; // index of bufs for _previous_ line
int curidx = 0; // index of bufs for _current_ line
char *buf; // pointer to line buffer to process
// read all lines into alternating line buffers
for (; fgets(lines[curidx],LINEMAXLEN,stdin) != NULL;
previdx = curidx, curidx = (curidx + 1) % NBUF) {
// process _previous_ line ...
if (previdx >= 0) {
buf = lines[previdx];
// process line ...
}
}
fgets() will not modify the buffer at all when it reaches EOF, so just read lines until fgets() returns NULL. The last line read will be retained:
#include <stdio.h>
int main( int argc, char **argv )
{
char line[ 1024 ];
FILE *f = fopen( argv[ 1 ], "r" );
if ( NULL == f )
{
return( 1 );
}
for ( ;; )
{
char *p = fgets( line, sizeof( line ), f );
if ( NULL == p )
{
break;
}
}
printf( "last line: %s\n", line );
return( 0 );
}
This relies on the required behavior of fgets():
The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned.
Robust code should check for errors with ferror().
Working that into your text processing is left as an exercise... ;-)

Printing sections of a text file with C

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.
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;
}
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 )
{
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 we 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 -----

Non-portable pointer conversion

I am writing a program that takes strings from two files and combines them to make a third file with the combined string.
#define BUF 255
int main( void)
{
FILE *usernames; FILE *passwords; FILE *final_file;
char *user_str, *pass_str;
int ct, ck;
usernames = fopen( ".\\info_files\\usernames.txt", "r" );
passwords = fopen( ".\\info_files\\passwords.txt", "r" );
final_file = fopen( ".\\info_files\\usernamesPasswords.txt", "w" );
if ( (usernames == NULL) || (passwords == NULL) || (final_file == NULL)
)
{
printf( "failed to open one of the files" );
}
while ( (fgets( user_str, BUF, usernames) != EOF ) && ( fgets( pass_str, BUF, passwords) != EOF))
{
fprintf( final_file, "%-25s %s\n", user_str, pass_str );
}
fclose( usernames );
fclose( passwords );
fclose( final_file );
return 0;
}
This is what's giving me trouble. I have no idea what is causing this to crash.
This is edited from what was first posted.
#BLUEPIXY has given you the correct code - Here's the explanation of where you went wrong in your code:-
char *fgets(char *str, int n, FILE *stream)
reads a line from the specified stream and stores it into the string pointed to by str. It stops when either (n-1) characters are read, the newline character is read, or the end-of-file is reached, whichever comes first.
Retun value of fgets
On success: the function returns the same str parameter
If the End-of-File is encountered and no characters have been read,
the contents of str remain unchanged and a null pointer is returned
If an error occurs, a null pointer is returned.
Source:- C Tutorial Point

Reading file input into a char array in c

I am in an introductory programming class in C, and I am having some problem with our final project for the semester. We have to take an input file that will have polynomial equations on separate lines, and we have to then take the derivative of each of those equations. I am trying to read the equations into a character array so that I can then process that array to take the derivative. My logic currently calls for the program to read in one line at a time so that I can then run the corresponding array through my functions and take the derivative. I am, however, struggling to figure out how to do this, as we do not know the length of equations that will be tested.
My main function looks like this currently, and the code to execute what is described above would need to go inside the do loop before function one is called. All three functions are of type void.
int main(void)
{
char input [40], output [40];
do
{
function1( &input);
function2 (&input, &output);
function3(&output);
}while(!feof(ifp))
}
Thanks for your help.
the following code will get you started.
It is up to you to implement the calculateDerative() function
at the end of C programming course, the following code
should be something you can do in your sleep
#include <stdio.h>
#include <stdlib.h> // exit(), EXIT_FAILURE
#define MAX_INPUT_LEN (40)
#define MAX_OUTPUT_LEN (40)
//void function1( char * pInput );
//void function2( char * pInput, char * pOutput );
//void function3( char * pOutput );
void calculateDerivative( char* pInput, char* pOutput );
int main(void)
{
FILE *ofp = NULL;
FILE *ifp = NULL;
if( NULL == (ifp = fopen( "inputFileName", "r" ) ) )
{ // then fopen failed
perror( "fopen for read failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
if( NULL == (ofp = fopen( "outputFileName", "w" ) ) )
{ // then fopen failed
perror( "fopen for write failed" );
fclose( ifp );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
char input [MAX_INPUT_LEN], output [MAX_OUTPUT_LEN];
while( fgets( input, sizeof(input), ifp ) ) // stay in loop until NULL returned
{
//function1( input ); // already have input available
//function2 (input, output ); // process from input to output
calculateDerivative( input, output );
//function3( output) ; // write output to output file
if( 1 != fwrite( output, strlen(output), 1, ofp ) )
{ // then fwrite failed
perror( "fwrite failed" );
fclose( ifp );
fclose( ofp );
exit( EXIT_FAILURE )
}
// implied else, fwrite successful
} // end while
fclose( ifp );
fclose( ofp );
return (0);
} // end function: main

Replace string with another

I am just not sure why my replaceWord isn't going in to the file at all i have used all the commented out and so on and so forth. I am just trying to replace with with the text received from the command line argument. I know i might be far off I was just looking for a relatively easy way to do it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( int argc, char *argv[] )
{
if ( argc != 4 ) /* argc should be 2 for correct execution */
{
/* We print argv[0] assuming it is the program name */
printf( "usage: %s filename\n", argv[0] );
}
else
{
// We assume argv[1] is a filename to open
char* wordReplace = argv[1];
char* replaceWord = argv[2];
FILE *file = fopen( argv[3], "r" );
/* fopen returns 0, the NULL pointer, on failure */
if ( file == 0 )
{
printf( "Could not open file\n" );
}
else
{
char string[100];
int len = 0;
/* read one character at a time from file, stopping at EOF, which
indicates the end of the file. Note that the idiom of "assign
to a variable, check the value" used below works because
the assignment statement evaluates to the value assigned. */
while ( (fscanf( file, "%s", string ) ) != EOF )
{
len = strlen(string);
printf( "%s\n", string );
if(strcmp(string, wordReplace) == 0){
//fseek (file, (-strlen(string) + 1), 1);
//fputc(*replaceWord,file);
//replaceWord++;
//strcpy(string, replaceWord);
fprintf(file,"%s",replaceWord);
fputs(replaceWord, file);
printf("\n%d\n", len);
}
}
fclose( file );
}
}
printf("\n");
return 0;
}
You've opened the file in r ie read mode and trying to write to it.
Also after correcting that, note that, the replaced word and word to be replaced have to be of the same size, if you want to replace the file in place. Else you will end up overwriting other data. And you need to use functions like fseek to reposition the internal file pointer as fp would have moved ahead after fscanf

Resources