C checking file for string from scanf - c

So I give the user the option to introduce a code for a car like this:
typedef struct
{
char name[50];
char category[50];
int code;
int price;
} car;
car c;
FILE *f;
f = fopen("Cars.txt", "r+");
printf("Enter the code of the car:");
if (f != NULL) {
scanf("%d", &c.code);
char *code;
code = (char *)malloc(sizeof(c.code) +1 );
sprintf(code, "%d", &c.code);
while (checkCode(code, f) == 0)
{
printf("The code for this car already exists, try again:");
scanf("%d", &c.code);
char *code;
code = (char *)malloc(sizeof(c.code) + 1);
sprintf(code, "%d", &c.code);
}
}
And this is my checkCode function:
int checkCode(char *code, FILE *f) {
char line[1024];
while (fgets(line, sizeof(line), f) != NULL)
{
if (strstr(line, code) != NULL)
{
return 0;
}
}
return 1;
}
It doesn't work. Even though I introduce a code already entered on one of the file's lines the function returns 1. My guess is the problem is here:
while (fgets(line, sizeof(line), f) != NULL)
I am new to C. Can anyone please explain what problem I have. Thank you all for your time!

There are several problems in your code:
you allocate space for the converted string with an inconsistent size: the number of characters for the number conversion cannot be computed as sizeof(c.code) + 1, you should instead use a local array with a fixed size.
you pass the address of the code instead of its value.
you do not rewind the stream pointer to scan the file from the beginning.
you should also seek to the end of the stream to write the new entry.
Here is a corrected version:
FILE *f;
f = fopen("Cars.txt", "r+");
if (f != NULL) {
char code[32];
printf("Enter the code of the car:");
scanf("%d", &c.code);
snprintf(code, sizeof code, "%d", c.code);
rewind(f);
while (checkCode(code, f) == 0) {
printf("The code for this car already exists, try again:");
scanf("%d", &c.code);
snprintf(code, sizeof code, "%d", c.code);
rewind(f);
}
fseek(f, 0L, SEEK_END);
}
It would actually be more reliable to read the response as a string and convert with sscanf():
FILE *f = fopen("Cars.txt", "r+");
if (f != NULL) {
char code[32];
printf("Enter the code of the car:");
for (;;) {
if (fgets(code, sizeof code, stdin) == NULL) {
printf("unexpected end of file\n");
return -1;
}
if (sscanf(code, "%d", &c.code) != 1) {
printf("invalid input, try again:");
continue;
}
snprintf(code, sizeof code, "%d", c.code);
rewind(f);
if (checkCode(code, f) == 0) {
printf("The code %s is already used, try again:", code);
continue;
}
break; /* the code in c.code is OK */
}
fseek(f, 0L, SEEK_END);
}

One problem I see is that the file is not rewound after one interation of the while loop.
On the second time the check is run, the file pointer will still be at the end of the file, so it will act as if there are no codes in the file.
Another tip:
use do...while here, since you want to ask for input at least once. This will reduce code duplication. Something like this:
char code[256];
do
{
scanf("%d", &c.code);
sprintf(code, "%d", &c.code);
} while (checkCode(code, f) == 0);
(Note that the error message for wrong input should be moved to the checkCode() function)

I don't really like the fact that you have the prompt for the code in two places in the code. If you want to go back to beginning when the user gives an unacceptable code, do just that. That might also avoid doing the memory allocation in two places, making it less likely to forget to free one of the buffers. A mock-up:
do {
int code = ask_for_code();
if (look_for_code(code) == 0) {
printf("sorry, that code already exists");
} else {
do_something_with_the_code();
break;
}
} while(1);
Then ask_for_code() would contain your prompt and scanf, and look_for_code approximately matches your checkCode().
Change the loop condition to a status flag or something like that if you don't like seemingly neverending loops.
Also this:
code = (char *)malloc(sizeof(c.code) +1 );
sprintf(code, "%d", &c.code);
allocates a buffer the size of an int (+1), so probably about 5 bytes. That's enough for a number up to 9999, but anything past that will overflow the buffer. A 32-bit integer should fit in 12 bytes (with the tailing zero), but in any case, you might want to use snprintf there:
char code[12];
snprintf(code, 12, "%d", c.code);
That printf should also take the number itself (c.code), not its address (&c.code).

Related

How to use for loop to count number

I would like to write a program that can continuously + 1 to ID once user registered a user. I use for loop to design it, but I don't know how to break the loop after registered a user. Please help me.
Expected Data in idtest.txt:
SID001:USER1
SID002:USER2
SID003:USER3
.
.
and so on
Actual Data in idtest.txt:
SID001:USER1
SID002:USER1
SID003:USER1
.
until SID00999:USER1
My Code:
int main()
{
FILE* f;
char data[1024], user[50];
int num = 0, c, buf;
printf("Enter User: ");
scanf("%s", user);
f = fopen("idtest.txt", "a+");
while (fgets(data, sizeof data, f));
for (buf = 1; buf <= 999; buf++)
{
c = fgetc(f);
if (c == EOF)
{
fprintf(f, "SID00%d:%s\n", num, user);
}
else if (num >= 1)
{
fprintf(f, "SID00%d:%s\n", num, user);
}
else if (num >= 9)
{
fprintf(f, "SID0%d:%s\n", num, user);
}
else if (num >= 99)
{
fprintf(f, "SID%d:%s\n", num, user);
}
break;
}
return 0;
}
There are multiple problems in your code:
you do not test for errors
as coded, num is always 0: the program cannot produce the file contents you posted.
it makes no sense to try an read the next byte with fgetc(f) since the previous loop reached the end of the file already. This call returns EOF, which explains why you always write 2 leading zeroes.
you cannot mix reading and writing operations without an intervening seek operation, it has undefined behavior. Your code manages to write to the file, but it might behave differently with a different compiler / library / operating system...
you can simplify the output code using %03d to format numbers with leading zeroes.
naming an int variable buf is very confusing.
If you intend for the program to register one user at a time, you should read the file and count the number of lines, then write a single entry at the end.
Here is a modified version:
#include <stdio.h>
int main() {
char user[50];
char data[1024];
FILE *f;
int num;
printf("Enter User: ");
if (scanf("%49s", user) != 1) {
fprintf(stderr, "invalid or missing input\n");
return 1;
}
f = fopen("idtest.txt", "a+");
if (f == NULL) {
fprintf(stderr, "cannot open file idtest.txt\n");
return 1;
}
/* count the number of users */
num = 1;
rewind(f);
while (fgets(data, sizeof data, f)) {
num++;
}
/* switch to writing mode */
// No need to seek because the above loop hit the end of file.
// which is a special case where the next call can be a write operation
//fseek(f, 0L, SEEK_CUR);
fprintf(f, "SID%03d:%s\n", num, user);
fclose(f);
return 0;
}

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.

C: How to input only int data from a text file of unknown size

void inputData()
{
FILE* fp = fopen("input_001.txt", "r");
if(fp == NULL)
{
exit(FILE_FAILED_TO_OPEN);
}
fseek(fp, 0, SEEK_END); //set position to end of file
int size = ftell(fp);
if(size == 0)
{
exit(PARSING_ERROR_EMPTY_FILE);
}
int i;
char *input;
char *errPtr = NULL;
char *data;
fgets(input, 64, fp);
for(i = 0; i < size; i++)
{
strtol(input[i], *errPtr, 10);
//testing the output
printf("%d\n", input[i]);
}
fclose(fp);
if(fclose(fp) != 0)
{
exit(FILE_FAILED_TO_CLOSE);
}
}
I am trying to input data from a text file of unknown size into an array and keep only the integers. The format of the text file is one number per line and there can be any number of lines. I have included my attempt at trying to input the data, but my coding was not going so well. I want to use fgets() and then strtol() to convert the lines I get from fgets() into integers and put them in an array so I can work with that data. Any help is appreciated!
You haven't allocated any space for input to point to. I saw your
earlier version had a malloc; you can use that, or just a char
array.
Did you mean to use data? Because you're not, yet.
fgets reads at most one line at a time, so you need to put your reads
in a loop.
You appear to be converting the string to a number multiple times. For
instance, if the first line were "12345", this code would get 12345,
then 2345, 345, etc. This was presumably not your intention.
You're incrementing i up to size. size is the file size and might
be quite large, but you only read a maximum of 64 characters into the
buffer. (Or would have, if space had been allocated.)
In short, this code is very confused and I recommend starting over from
scratch. Decide whether you want to read the entire file at once, or one
line at a time; I recommend the latter, it takes less memory and is
simpler. If you want to store them in an array, you can do that with
malloc and then realloc as needed to grow the array dynamically.
do not use strtol, use atoi instead.
and use a loop to read the lines.
just a quick answer:
int count = 0;
while( fgets (input, 64, fp) != NULL )
{
count++;
}
fseek(fp, 0, SEEK_SET);
int* arr = new int[count];
count = 0;
while( fgets (input, 64, fp) != NULL )
{
int a =atoi(input);
arr[count++] = a;
}
If you don't know how many lines are in the file, you have a few options:
Loop through the file, counting the number of lines (call it nlines). Then declare the array int numbers[nlines].
Use a linked list instead of an array to store the numbers.
Dynamically allocate blocks of an array.
As for reading the integer data itself, something like this would work if you decide to go with the first option:
void inputData(const char* fname, const unsigned int nlines)
{
FILE* fp = fopen(fname, "r");
if (fp == NULL) {
exit(EXIT_FAILURE);
}
int numbers[nlines];
double d = 0;
fscanf(fp, "%lf", &d);
int i = 0;
while (!feof(fp)) {
numbers[i++] = (int)floor(d);
fscanf(fp, "%lf", &d);
}
fclose(fp);
}

C programming Reading a specific line of a text file

So i've been given an exercise to work on: Have the user input a number and the program will display the line of text associated with that line for example
Password
abcdefg
Star_wars
jedi
Weapon
Planet
long
nail
car
fast
cover
machine
My_little
Alone
Love
Ghast
Input 3: Output: Star_wars
Now i have been given a program to solve this, however it uses the function getline() , which doesn't complie on DEV C++.
#include <stdio.h>
int main(void)
{
int end = 1, bytes = 512, loop = 0, line = 0;
char *str = NULL;
FILE *fd = fopen("Student passwords.txt", "r");
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
printf("Enter the line number to read : ");
scanf("%d", &line);
do {
getline(&str, &bytes, fd);
loop++;
if (loop == line)
end = 0;
}while(end);
printf("\nLine-%d: %s\n", line, str);
fclose(fd);
}
All i need is to know how to do this, in a simple program without the use of getline()
Thanks
Edit: I also don't want to download software to make this work
use fgets instead of getline.
#include <stdio.h>
int main(void){
int end, loop, line;
char str[512];
FILE *fd = fopen("data.txt", "r");
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
printf("Enter the line number to read : ");
scanf("%d", &line);
for(end = loop = 0;loop<line;++loop){
if(0==fgets(str, sizeof(str), fd)){//include '\n'
end = 1;//can't input (EOF)
break;
}
}
if(!end)
printf("\nLine-%d: %s\n", line, str);
fclose(fd);
return 0;
}
You have wrote:
char *str = NULL;
and you used it without initializing:
getline(&str, &bytes, fd);
first you must initialize it:
char *str=(char*)malloc(SIZEOFSTR);
you can add this part in your program instead of your do-while loop. You will be using fscanf() whose arguments are the file pointer, specifier of data type and the variable you want to store.
printf("Enter the line number to read : ");
scanf("%d", &line);
while(line--) {
fscanf(fd,"%s",str);
}
printf("\nLine-%d:%s\n",line,str);

How to read in a text file of tab-separated integers in C?

I have a file of simply tab-separated integers (a .txt file) and I wish to read them in with just C, line by line. So, say each line has 5 integers. How can I accomplish this?
My first attempt was as follows. It was just to read in a single integer, but even that didn't work:
FILE *fp;
char blah[255];
int *some_int;
fp = fopen("test.txt", "rt");
while (fgets(blah, 255, fp) != NULL)
{
sscanf(blah, "%d", some_int);
printf("%d\n", *some_int);
}
Here's a way no one else suggested, that doesn't use fscanf so you can have sane error handling:
char buffer[BUFSIZE];
size_t size = 5;
int *data = malloc(size * sizeof *line);
if(line == NULL) error();
while(fgets(buffer, sizeof buffer, fp)
{
size_t i = 0;
char *next = buffer;
while(*next && *next != '\n')
{
data[i++] = strtol(next, &next, 0);
// check for errors
}
}
Basically, instead of trying to use *scanf's "%d" to read characters, use the function it (probably) calls to do the conversion: strtol. Where *scanf goes through the string to match the format string but doesn't let you "save your place" in between function calls, strtol does, which is what you need to read an arbitrary number of integers.
I haven't written all your code for you - you have to do the hard error handling. Possible errors include:
i == size, in which case you can try to make data bigger with realloc. Alternately, you could loop through the buffer and count how many numbers there are beforehand, then allocate that many so you don't need to reallocate later.
fgets didn't read the entire line (check that the last character before '\0' is '\n'). In this case you'll probably want to refill the buffer and keep reading numbers. Be careful in this case - you'll likely need to go back and recalculate the last number - fgets might have cut it off. (This is one disadvantage to using fgets.)
Erroneous input - handle however you like.
#include <stdio.h>
int main(){
FILE *fp;
int scanned = 0;
int some_ints[5];
fp = fopen("test.txt", "r");
while ((scanned = fscanf(fp, "%d %d %d %d %d", some_ints, some_ints+1, some_ints+2, some_ints+3, some_ints+4)) != EOF) {
if(scanned ==5){
printf("%d %d %d %d %d\n", some_ints[0], some_ints[1], some_ints[2], some_ints[3], some_ints[4]);
}
else {
printf("Whoops! Input format is incorrect!\n");
break;
}
}
}
I'd do something like this:
int storedVals[MAX_STORED_VALS];
int bf;
int ii=0;
while (!feof(fp) && ii<MAX_STORED_VALS) {
if (fscanf(fp," %d",&bf)) {
storedVals[ii++]=bf;
}
}
fscanf automatically does white space trimming. So as long as there's a space in your scan string, it'll get rid of zero or more \t (tabs) and \n (newlines) to find the next integer. Of course, this doesn't do much by way of error correction.

Resources