fgets doesn't move file pointer - c

I have a question which maybe fairly simple. I have a file input.txt which is:
cat input.txt
testsuite1
test1
summary information of test
FAIL
testsuite2
test1
summary info ya
PASS
I am writing a program just to read each of these strings into variables and do further processing. What is the best way to do it? I am currently doing:
main() {
FILE *fp;
char testsuite[100],testname[100],summary[100],result[100];
fp = fopen("input.txt", "r");
while(1) {
if(fgets(testsuite,99,fp) == NULL)
{
ferror(fp);
break;
}
if(fgets(testname,99,fp) == NULL)
{
ferror(fp);
break;
}
if(fgets(summary,99,fp) == NULL)
{
ferror(fp);
break;
}
if(fgets(result,99,fp) == NULL)
{
ferror(fp);
break;
}
printf("testsuite: %s testname:%s summary:%s result:%s \n",testsuite,testname,summary,result);
}
fclose(fp);
}
Is there a better way to do it? The problem I am currently facing is that, if input.txt contains even one blank line, the blank like is read into a variable. Whats the best way to avoid it?
Thanks!

You should write your own function that skips empty lines (e.g. called getline()) and use it instead of fgets():
char *getline(char *buf, int size, FILE *fp)
{
char *result;
do {
result = fgets(buf, size, fp);
} while( result != NULL && buf[0] == '\n' );
return result;
}
You could now refine that function to also skip lines that consist of blanks only or whatever you need.

You can remove all blank lines of your files before your loop while.
After the open you parse the whole file and remove the blank ;).
But it don't seems like the best way to do it.
Overwise you can check after each fget if your variables are empty, and in this case, fget again.
Hope that will be helpfull.

If you do this, you will not be able to use the strings you read once loop quits, since each loop overwrites each string in the buffer. However, you could store your strings in a struct array:
typedef struct {
testsuite[100];
testname[100];
summary[100];
result[100];
}test;
test test_array[2];
int main(){
int iIndex=0;
FILE* fpPtr=NULL;
fpPtr = fopen("input.txt", "r");
if(fpPtr==NULL){ //<--- it is very important to check if fopen fails
perror("fopen");
}
for(iIndex=0; iIndex<2; iIndex++){ // 2 because it is the number of elements in test_array
if(fgets(test_array[i].testsuite,99,fp) == NULL)
{
ferror(fp);
break;
}
if(fgets(test_array[i].testname,99,fp) == NULL)
{
ferror(fp);
break;
}
if(fgets(test_array[i].summary,99,fp) == NULL)
{
ferror(fp);
break;
}
if(fgets(test_array[i].result,99,fp) == NULL)
{
ferror(fp);
break;
}
}
}
You can detect blank lines by checking for a newline character at position 0:
fgets(cBuffer, sizeof(cBuffer), fpPtr);
//<-- if the file was created on windows, check for '\r' instead, since a new line in windows is \r\n
if(cBuffer[0]=='\n')
{
printf("blank line"\n);
}

Related

Comparing a user input string to a line from a text file in c creates infinite loop

I'm trying to get better at coding in general and C in particular, and am coding a small text adventure game. I read a string input by the user i.e LOOK room and compare it to a txt file with the list of commands for that particular section.
As I am reading from the text file I have a counter which keeps track of which line is being read, when the match is made I convert the line number to a character and concatenate it to "outside.txt" so that when the correct command is input it will read from the correct file i.e LOOK room would load text from 1outside.txt etc.
However, when inputting anything it just loops on "I dont understand" forever. Any explanation as to why or constructive comments on my code are appreciated, especially if I am misunderstanding how files and/or strings in c.
int mansionOutside(void)
{
int stop = 1;
char choice[25];
char word_match[25];
char text_line[73];
char line1[25];
char temp[2];
int counter;
FILE *fptr;
fptr = fopen("mansion_commands.txt", "r");
if (fptr == NULL)
{
printf("ERROR!");
}
else
{
while (stop == 1)
{
printf("\n");
fgets(choice, sizeof choice, stdin);
while (fgets (line1, 25, fptr)!= NULL)
{
if (strcmp(line1, choice) == 0)
{
printf("%s\n", line1);
stop = 0;
break;
}
else
{
counter++;
printf("%s + %s\n", line1, choice);
}
}
if (stop == 1)
{
printf("I dont understand\n");
counter = 1;
}
}
fclose(fptr);
counter = counter + '0';
temp[0] = counter;
temp[1] = '\0';
strncat(word_match, temp , 1);
strcat(word_match, ".txt");
fptr = fopen(word_match, "r");
if (fptr == NULL)
{
printf("ERROR!\n");
}
else
{
printf("Debugging : File opened Successfully\n");
while (fgets (text_line, 72, fptr) != NULL)
{
printf("%s", text_line);
//delay(2);
}
}
}
}
EDIT : Took in suggestions for improvements to avoid Buffer overflows such as using > fgets , but I think there is something I have missed. Now If I input anything contained in the file, it works fine. If however I input something wrong, then something correct on re-prompt, It skips the inner while loop all together and goes straight to "I don't understand".
The following is what happens when my input is LOOK room, and then LOOK mansion.

Replacing specific text of a file in C

Alright, so basically what I have to do is change all the numbers of a text file to dollar sign, I know how to scan for the specific character but I am stuck on how to replace that specific character with dollar sign. I don't want to use fseek or any library commands, how do I proceed and why isn't my code working?
#include<stdio.h>
main()
{
FILE* fptr;
char filename[50];
char string[100];
int i;
printf("Enter the name of the file to be opened: ");
scanf("%s",filename);
fptr=fopen(filename,"w");
if(fptr==NULL)
{
printf("Error occurred, try again.");
return 0;
}
fgets(string,"%s",fptr);
do
{
if(string[i]>='1' && string[i]<='9')
{
string[i]='$';
}
}
while(i!=100);
fclose(fptr);
}
There are basically two approaches at first glance, the first is to use fseek() and the second to read the file in its entirety and replace the characters to your criteria and finally write that in one shot. You can choose either of the approaches depending on your need. For large file you should prefer the former and for small file you can prefer the latter.
Here's an example code of the former:
#include <stdio.h>
int main() {
// Open the file
FILE *fptr = fopen("input.txt", "r+");
if (!fptr) {
printf("Error occurred, try again.");
return -1;
}
int c;
// Iterate through all characters in a file
while ((c = getc(fptr)) != EOF) {
// Check if this current character is a digit?
if (c >= '0' && c <= '9') {
// Go one character back
if (fseek(fptr, -1, SEEK_CUR) != 0) {
fprintf(stderr, "Error while going one char back\n");
return -1;
}
// Replace the character with a '$'
if (fputc('$', fptr) == EOF) {
fprintf(stderr, "Error while trying to replace\n");
return -1;
}
}
}
// Flush the changes to the disk
if (fflush(fptr) != 0) {
fprintf(stderr, "Error while flushing to disk\n");
return -1;
}
// Close the file
fclose(fptr);
return 0;
}

Segmentation fault (core dumped) due to fgets - I think

but I keep getting this error when I run this program. I think it's because of the fgets function. I tried initializing the input variable to NULL to see if that'll help, but it didn't. I also have a hunch that I might need to malloc to solve the problem. But your help is highly appreciated.
int main(int argc, char* argv[])
{
char* input = NULL;
// ensure one and only one command line argument
if (argc != 2)
{
printf("Usage: %s [name of document]\n", argv[0]);
return 1;
}
// open a new document for writing
FILE* fp = fopen(argv[1], "w");
// check for successful open
if(fp == NULL)
{
printf("Could not create %s\n", argv[1]);
return 2;
}
// get text from user and save to file
while(true)
{
// get text from user
printf("Enter a new line of text (or \"quit\"):\n");
fgets(input, 50, stdin);
// if user wants to quit
if (input != NULL && strcmp(input, "quit") == 0)
{
free(input);
break;
}
// if user wants to enter text
else if (input != NULL)
{
fputs(input, fp);
fputs("\n", fp);
printf("CHA-CHING!\n\n");
free(input);
}
}
// close the file and end successfuly
fclose(fp);
return 0;
}
You never malloc-ed input, so yeah, fgets is dereferencing the NULL pointer as its buffer, and that's going to die. Either change input to a stack array (and remove the free for it) or actually call malloc to allocate memory so input isn't pointing to NULL.
Their are some problems in your code.
You have not allocated memory to input character pointer. Hence you can't store characters in it, hence you get segmentation fault.
Also you are freeing more than once, which is incorrect.
So, a code, with the above modification would be something like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
char* input = malloc(sizeof(char) * 50);
// ensure one and only one command line argument
if (argc != 2)
{
printf("Usage: %s [name of document]\n", argv[0]);
return 1;
}
// open a new document for writing
FILE* fp = fopen(argv[1], "w");
// check for successful open
if(fp == NULL)
{
printf("Could not create %s\n", argv[1]);
return 2;
}
// get text from user and save to file
while(1)
{
// get text from user
printf("Enter a new line of text (or \"quit\"):\n");
fgets(input, 50, stdin);
// if user wants to quit
if (input != NULL && strcmp(input, "quit\n") == 0)
{
free(input);
break;
}
// if user wants to enter text
else if (input != NULL)
{
fputs(input, fp);
fputs("\n", fp);
printf("CHA-CHING!\n\n");
// free(input);
}
}
// close the file and end successfuly
fclose(fp);
return 0;
}
Hope it helps your problem.
Cheers.
While you can use malloc() here, it is not really necessary. You can #define a reasonable maximum line length, and declare a character array to hold the input. If you do this, you can remove the frees from your code.
You also have an issue with the way that you are using fgets(). The trailing \n is kept by fgets(), but your comparisons are ignoring this. Consequently, input is never equal to "quit", and is certainly never NULL. I have included some code that removes the trailing newline after reading into input; the code also clears any remaining characters from the input stream, which is possible in the event that the user enters more than MAXLINE - 1 characters. The test for text input is then simply if (input[0]). Alternatively, you could change your tests to take into account the extra '\n' character.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAXLINE 1000
int main(int argc, char* argv[])
{
char input[MAXLINE];
char *ch; // used to remove newline
char c; // used to clear input stream
// ensure one and only one command line argument
if (argc != 2)
{
printf("Usage: %s [name of document]\n", argv[0]);
return 1;
}
// open a new document for writing
FILE* fp = fopen(argv[1], "w");
// check for successful open
if(fp == NULL)
{
printf("Could not create %s\n", argv[1]);
return 2;
}
// get text from user and save to file
while(true)
{
// get text from user
printf("Enter a new line of text (or \"quit\"):\n");
fgets(input, MAXLINE, stdin);
// remove trailing newline
ch = input;
while (*ch != '\n' && *ch != '\0') {
++ch;
}
if (*ch) {
*ch = '\0';
} else { // remove any extra characters in input stream
while ((c = getchar()) != '\n' && c != EOF)
continue;
}
// if user wants to quit
if (strcmp(input, "quit") == 0)
{
break;
}
// if user wants to enter text
else if (input[0])
{
fputs(input, fp);
fputs("\n", fp);
printf("CHA-CHING!\n\n");
}
}
// close the file and end successfuly
fclose(fp);
return 0;
}
I think it's because of the fgets function.
Yes: passing NULL pointer to fgets makes no sense, isn't allowed, and will cause a crash.
I might need to malloc to solve the problem.
You need to pass a pointer to a suitable buffer for fgets to read input into. Whether that buffer is malloced, a local or a global array, is irrelevant.
TL;DR: think about what you are doing.

Check to see if file input line is empty

I have a text file in the following format:
Some information here
Some more information here
I want to check to see if the inputted line is blank (line 2 above). I've tried various things but none of them seem to be working, there's obviously something simple that I am missing here.
void myFunc(char* file_path) {
FILE* file;
char buff[BUFFER_SIZE];
file = fopen(file_name, "r");
bool flag = false;
while(fgets(buff, BUFFER_SIZE, file) != NULL) {
if(buff[0] == '\n') {
flag = true;
}
}
}
I've tried strlen(buff) == 0, strcmp(buff, ""), buff[0] == '\0' and many other things but I still can't seem to be getting this to work properly.
It's possible that the second line has more than just the newline character.
You can use a helper function to test that out.
void printDebug(char* line)
{
char* cp = line;
for ( ; *cp != '\0'; ++cp )
{
printf("%d ", (int)(*cp));
}
printf("\n");
}
By examining the integer values of the characters printed, you can tell whether the line has more than one character, and what those characters are.

getc() for passed in input and file reading in C

I have to develop a program in C that can have two kinds of inputs.
By feeding it a string ( I am assuming like this filename < String1234455678, please correct me if I am wrong).
By reading data from some file(s).
I have to do some checks regarding the characters that are in it and store them in an array. But I want to learn how to use the getc() from stdin first.
My first question is, can I use getc() in both cases?
I wanted to loop through every single character in the feed line/file, and I assume the code would look something like this:
char Array1[];
char charHolder;
//If the file/feed has chars (!NULL), execute
if ((charHolder = getchar())!=NULL){
//Do something
//Do some more
//Finally append to Array1
Array1[] = charHolder;
}
There might be some issues with the code above. I wanted to know if that kind of inserting is valid in C (with no index specified, which it will just push the value at the end of the array). Also, I read from http://beej.us/guide/bgc/output/html/multipage/getc.html that getc(stdin) and getchar() are exactly equivalent. I just want to double check that this is indeed true and either function will work with both my cases where I have to read data (from a file and feeding my program a string).
Also, I was wondering how I can achieve reading characters from multiple files. Say if my program was to be executed as programName file1 file2.
Thank you for your time and help!
Cheers!
Edit 1:
I also wanted to know how to check when the chars end from a file/string feed. Should I use the EOF for both cases?
Example:
while ((charHolder = getchar()) != EOF){
//code
}
Here is a sample:
#include <stdio.h>
void do_read(FILE * file, int abort_on_newline) {
char ch;
while (1) {
ch = getc(file);
if (ch == EOF) {
break;
}
if (abort_on_newline && ch == '\n') {
break;
}
printf("%c", ch);
}
}
int main(int argc, char * argv[])
{
int i = 1;
FILE * fp = NULL;
if (1 == argc) {
// read input string from stdin, abort on new line (in case of interactive input)
do_read (stdin, 1);
}
else {
// cycle through all files in command line arguments and read them
for (i=1; i < argc; i++) {
if ((fp = fopen(argv[i], "r")) == NULL) {
printf("Failed to open file.\n");
}
else {
do_read(fp,0);
fclose(fp);
}
}
}
return 0;
}
Use it like this:
To read from stdin: echo youstring | youprogram, or just start
yourprogram to get input from user
To read from file(s) yourprogram yourfile1 yourfile2 ...
Yes your can use getc in both cases, yes you should check for EOF in both cases, except for interactiv input. In case of binary files you also need to use feof function to check for EOF. See code above to read from multiple files.

Resources