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;
}
Related
im trying to implement the number guessing game. The game itself works fine. But now I want to add a function which safes the score of the last game (Number of trys and guessed number) in the leaderboard.txt file.
I haven't finished the saveScore method yet, I don't need help with implementing the rest. I wan't to read the data from the file, add the new line and sort it from least to most trys. But I only wan't to save the top 10 of all time.
I need some help to get the following code running. One problem is the code doesn´t even terminate.
I think all the problems are within the saveScore method. (line 18 -25)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define max_length 300
void saveScore(int guess, int randNumber) {
FILE *datei;
datei = fopen("leaderboard.txt", "wb");
char lines[max_length];
char leaderboard[10][max_length];
int line = 0;
while (fgets(leaderboard[line], sizeof(lines), datei) != NULL){
line++;
}
leaderboard[line][max_length] = ("%d, %d", guess, randNumber);
fwrite(leaderboard, sizeof(char), sizeof(leaderboard), datei);
fclose(datei);
}
void startGame(){
int guess = 1;
int randNumber;
int uInput;
randNumber = (rand()%100)+1;
printf("%d", randNumber);
do {
printf("Guess the number between 1-100: \n");
scanf("%d", &uInput);
if (uInput < randNumber){
printf("the number you are looking for is higher.\n");
}
else if (uInput > randNumber){
printf("the number you are looking for is lower.\n");
}
else {
printf("Jackpot it was your %d. try.", guess);
}
guess++;
} while (randNumber != uInput);
saveScore(guess, randNumber);
char playAgain = 'j';
printf("Try Again (j/n): \n");
scanf(" %c", &playAgain);
if (playAgain == 'j') {
startGame();
}
printf("Thank you for playing.");
}
int main() {
srand(time(NULL));
startGame();
return 0;
}
I appreciate any help.
Best Enno
This loop has the problem of being able to overflow leaderboard if line ever reaches 10.
while (fgets(leaderboard[line], sizeof(lines), datei) != NULL){
line++;
}
This line
leaderboard[line][max_length] = ("%d, %d", guess, randNumber);
has a few problems. [max_length] would be one past the end of the buffer, but with that said, it is not needed and the assignment as a whole is incorrect. To perform string interpolation, use a function such as sprintf.
Aside from the fact that you only open the file for writing, the primary problem is that
fwrite(leaderboard, sizeof(char), sizeof(leaderboard), datei);
will write the entire contents of leaderboard to the file. This includes the garbage values that exist towards the end of each array, after each string. fgets will then read those garbage values later.
You should stick to reading and writing binary or text, but do not mix them. If you use fgets, use fputs (or similar) to write the text. Conversely, if you use fwrite, use fread to read the binary data.
Here is a basic, cursory snippet using text functions, where we:
open the file for reading
read our lines into the array
close the file
add our new score to the array
sort our array using qsort
open the file for writing
write our lines
close the file
The trick here is leaderboard has an additional slot, so that there is always room for our newest score. After sorting, we only write at most MAX_ENTRIES entries to the file, meaning if the array is full we ignore the worst score.
#define MAX_ENTRIES 10
#define MAX_LENGTH 300
#define SAVE_FILE "leaderboard.txt"
int compare(const void *ap, const void *bp) {
const char (*a)[MAX_LENGTH] = ap;
const char (*b)[MAX_LENGTH] = bp;
int av, bv;
sscanf(*a, "%d", &av);
sscanf(*b, "%d", &bv);
return (av > bv) - (av < bv);
}
void saveScore(int guess, int randNumber) {
char leaderboard[MAX_ENTRIES + 1][MAX_LENGTH] = { 0 };
size_t entries = 0;
FILE *file = fopen(SAVE_FILE, "r");
if (file) {
while (entries < MAX_ENTRIES &&
fgets(leaderboard[entries], sizeof *leaderboard, file))
entries++;
fclose(file);
}
sprintf(leaderboard[entries], "%d %d\n", guess, randNumber);
entries++;
qsort(leaderboard, entries, sizeof *leaderboard, compare);
file = fopen(SAVE_FILE, "w");
if (file) {
for (size_t i = 0; i < entries && i < MAX_ENTRIES; i++)
fputs(leaderboard[i], file);
fclose(file);
}
}
My program loads/saves double value into a text file using union.
I think I am having a buffer overflow, when I use fflush(stdin) it works, but I can't. The problem maybe somewhere else though. The code jumps without letting me enter a file to load from ( 2.000000 is autoinserted ).
Ps. Yes I know that tab[i] would be better than *(tab+i) but spare me, I just have to do it like this.
output :
Enter double value:8794.061758
Enter path to file you wish to save:valleykingstopwantreachspellshipcontinue
File saved
Enter a path to file you wish to load: 2.000000 ----- This value is inserted automatically ( I didnt write it)
Process finished with exit code 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
union double_to_char_t
{
double d;
char bytes[8];
};
int save_double(const union double_to_char_t *dtc, const char *filename)
{
if(dtc == NULL || filename == NULL) return 1;
FILE * f=fopen(filename,"w");
if(f == NULL) return 2;
int result = fwrite(dtc, sizeof(dtc), 1, f);
if( result != 1)
{
fclose(f);
return 3;
}
fclose(f);
return 0;
}
int load_double(union double_to_char_t *dtc, const char *filename)
{
if( dtc == NULL || filename == NULL) return 1;
FILE * f = fopen(filename,"r");
if( f == NULL ) return 2;
int result = fread(dtc, sizeof(dtc), 1, f);
if( result != 1)
{
fclose(f);
return 3;
}
fclose(f);
return 0;
}
int main()
{
union double_to_char_t unia;
char temp1[40] = {0};
char temp2[40] = {0};
char *input1 = temp1;
char *input2 = temp2;
printf("Enter double value: ");
if(scanf(" %lf",&(unia.d))!=1)
{
printf("Incorrect input");
exit(1);
}
printf("Enter path to file you wish to save:");
fgets(input1, 40, stdin);
int wynik = save_double(&unia,input1);
if(wynik > 0)
{
printf("Couldn't create file\n");
exit(5);
}
else
{
printf("File saved\n");
}
printf("Enter a path to file you wish to load: ");
fgets(input2, 40, stdin);
// scanf(" %39[^\n]", input2);
int score = load_double(&unia,input2);
if(score > 0 && score < 3)
{
printf("Couldn't open file\n");
exit(4);
}
if(score == 3)
{
printf("File corrupted\n");
exit(6);
}
printf("%f",unia.d);
return 0;
}
You used %39[^\n] to read the filename, then typed a 40 character filename. The first 39 characters were put into input1, and the last character was left in the input stream.
When you then tried to get the filename for input2, it read that last character. That's why it didn't wait for you to type anything -- there was still input available after the first filename was read.
So you saved to valleykingstopwantreachspellshipcontinu and loaded from e.
You need to ensure that your input buffer is longer than any potential input. If you use fgets() instead of scanf(), you can test whether the input ends with a newline. If not, you can report that the input was too long, read characters until the newline, then ask for input again. (The non-standard fflush(stdin) discards everything up to the next newline, which is why adding that fixed the problem.)
if (!fgets(input1, 40, stdin)) {
printf("input error\n");
exit(1);
}
if (input1[strlen(input1)-1] != '\n') { // check for trailing newline
printf("filename too long\n);
exit(3);
}
input1[strlen(input1)-1] = '\0'; // remove it before using as filename
You could also use the getline() function. This isn't standard C, but it's POSIX and widely implemented. It allocates the input buffer dynamically so any length can be accomodated.
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).
I need to read in a file that contains text, and then a double for that text. It is simply to get the mean and standard deviation for the set of numbers, so the text that comes before is irrelevant. For example, my input file looks a little like:
preface 7.0000
chapter_1 9.0000
chapter_2 12.0000
chapter_3 10.0000
etc..
In this case, it is finding the mean and std dev for the chapters of a book. I have the section of code below, but I'm not quite sure how to "ignore" the text, and only grab the doubles. At the moment this code prints out zeros and only exits the loop when it exceeds the array limit, which I set as a constant to 20 at the beginning of the program.
FILE *ifp;
char *mode = "r";
ifp = fopen("table.txt", mode);
double values[array_limit];
int i;
double sample;
if (ifp==NULL)
{
printf("cannot read file \n");
}
else
{
i = 0;
do
{
fscanf(ifp, "%lf", &sample);
if (!feof(ifp))
{
values[i] = sample;
printf("%.4lf \n", values[i]);
i++;
if (i>=array_limit) //prevents program from trying read past array size limit//
{
printf("No more space\n");
break;
}
}
else
{
printf("read complete\n");
printf("lines = %d\n", i);
}
}while (!feof(ifp));
fclose(ifp);
}
I think you could use fscanf(ifp, "%*[^ ] %lf", &sample) for reading from your file. The * says to ignore that particular match, the [] specifices a list of characters to match and the ^ indicates to match all characters except those in [].
Or possibly (a bit simpler) fscanf(ifp, "%*s %lf", &sample).
You have two major problems -- you're using feof which is pretty much always wrong, and you're not checking the return value of fscanf, which it what tells you whether you got a value or not (or whether you got to the eof).
So what you want is something like
while ((found = fscanf(ifp, "%lf", &values[i])) != EOF) { /* loop until eof */
if (found) {
/* got a value, so count it */
if (++i >= ARRAY_LIMIT) {
printf("no more space\n");
break;
}
} else {
/* something other than a value on input, so skip over it */
fscanf(ifp, "%*c%*[^-+.0-9]");
}
}
When reading in from a file, it's often best to use fgets to read one line at a time, then extract the parts you are interested in using sscanf:
#include <stdlib.h>
#include <stdio.h>
#define ARRAY_LIMIT 10
#define LINE_LENGTH 128
int main()
{
double values[ARRAY_LIMIT];
int i, count = 0;
double sample;
FILE *ifp = fopen("table.txt", "r");
if (ifp==NULL)
{
printf("cannot read file \n");
return 1;
}
char buff[LINE_LENGTH];
while (fgets(buff, LINE_LENGTH, ifp) != NULL)
{
if (sscanf(buff, "%*s %lf", &sample) != 1) break;
values[count++] = sample;
if (count == ARRAY_LIMIT) {
printf("No more space\n");
break;
}
}
fclose(ifp);
for (i = 0; i < count; ++i) {
printf("%d: %f\n", i, values[i]);
}
return 0;
}
fgets returns NULL if it encounters the end of the file, or if a read error has occurred. Otherwise, it reads one line of the file into the character buffer buff.
The asterisk %*s in the sscanf means that the first part of the line is discarded. The second part is written to the variable sample. I am checking the return value of sscanf, which indicates how many values have been read successfully.
The loop breaks when the end of the file is reached or the count reaches the size of the array.
#include <stdio.h>
int main() {
FILE *f;
unsigned int num[80];
int i=0;
int rv;
int num_values;
f=fopen("values.txt","r");
if (f==NULL){
printf("file doesnt exist?!\n");
return 1;
}
while (i < 80) {
rv = fscanf(f, "%x", &num[i]);
if (rv != 1)
break;
i++;
}
fclose(f);
num_values = i;
if (i >= 80)
{
printf("Warning: Stopped reading input due to input too long.\n");
}
else if (rv != EOF)
{
printf("Warning: Stopped reading input due to bad value.\n");
}
else
{
printf("Reached end of input.\n");
}
printf("Successfully read %d values:\n", num_values);
for (i = 0; i < num_values; i++)
{
printf("\t%x\n", num[i]);
}
return 0
}
Got the code above from another question, but having probelms editing it to suit my requirements, everytime i change it seems to error everywhere.
Its only a simple change aswell.
Example input from the file would be
12345678
87654321
1234567811
12345678
When reading in from the file using this method take tthe whole of every line when i only want it to take the first 8 hex numbers and save them.
I tryed using a fgets to get the line and than scanf to format it but the errors just get out of hand.
The idea i had in my head but just not sure how to implement is
1. Open file
fgets current line
scanf to format current line
Getting first 8 digits
Saving them to the array
loop till end of file (rather than <80 like it is in the code above)
Im a newbee to c and to used to java.
Try
char buffer[201];
while (fgets(buffer, 200, f) != NULL)
{
if (strlen(buffer) > 7)
{
buffer[8] = 0;
sscanf(buffer, "%x", &num[i++]);
}
else
{
/* report that the line is error ?! */
}
}