Manipulating files through C to make a top 5 leaderboard - c

I'm making a connect 4 game as apart of a project and I want to add extra functionality to it, however I'm struggling.
The code is a function I've written that takes a struct (lb leaderboard) and attempts to read the file for that difficulty of all its inputs into a lb array size of 6 which include the difficulty of the bot, turn count and the name of the player, then organizes it in order of turn count and reprints the top 5 back into the file.
I set the first element of the old array to the passed by struct of leaderboard data for the organise function bit.
However, when i set it up with template inputs each separated with a new line like such:
0 7 James
0 13 Jimmy
0 8 Joshua
0 6 Charly
0 9 Jack
It prints this to the file:
0 7 James
0 13 Jimmy
0 8 Joshua
0 6 Charly
(0 9 Jack 1982289408 0 ) same line
The struct looks like this as well{
int turn_count
int diff
char name[20]}
I'm really new to coding so any help is appreciated!
lb old[6];
lb temp;
FILE *file;
if (leaderboard.diff == 0){
file = fopen("leaderboard0.txt", "r+");}
else if (leaderboard.diff == 1){
file = fopen("leaderboard1.txt", "r+");}
else if (leaderboard.diff == 2){
file = fopen("leaderboard2.txt", "r+");}
fseek( file, 1, SEEK_SET );
old[0] = leaderboard;
for (int i = 1; i < 6; i++){
fscanf(file, "%i %i %s", old[i].diff, old[i].turn_count, old[i].name);}
for (int i = 0; i < 6; i++){
for (int j = i + 1; j < 6; j++)
if (old[i].turn_count > old[j].turn_count)
{
temp = old[i];
old[i] = old[j];
old[j] = temp;
}}
for (int i = 0; i < 5; i++){
fprintf(file, "%i %i %s\n", old[i].diff, old[i].turn_count, old[i].name);
fclose(file);}
}

Welcome to StackOverflow, JJewson
First of all, I strongly recommend to you to properly indent your code. One error will be obvious only by that:
lb leaderboard = { ... }; /* was missing from your source code */
lb old[6]; /* you should rename this */
lb temp;
FILE *file;
/* somthing that opens a block was missing from your example source */ {
if (leaderboard.diff == 0) {
file = fopen("leaderboard0.txt", "r+");
} else if (leaderboard.diff == 1) {
file = fopen("leaderboard1.txt", "r+");
} else if (leaderboard.diff == 2) {
file = fopen("leaderboard2.txt", "r+");
}
fseek( file, 1, SEEK_SET );
old[0] = leaderboard;
for (int i = 1; i < 6; i++) {
fscanf(file, "%i %i %s", old[i].diff, old[i].turn_count, old[i].name);
}
for (int i = 0; i < 6; i++) {
for (int j = i + 1; j < 6; j++) {
if (old[i].turn_count > old[j].turn_count) {
temp = old[i];
old[i] = old[j];
old[j] = temp;
}
}
}
for (int i = 0; i < 5; i++) {
fprintf(file, "%i %i %s\n", old[i].diff, old[i].turn_count, old[i].name);
fclose(file); /* this is the obvious error, it should go below this loop */
}
fclose(file); /* here it should be */
}
It would be probably best to rename leaderboard to leader and after that to rename old to leaderboard.
I already mentioned the need to move the fclose call outside of the loop. You close the file after your first write.
I don't understand your first use of fseek( file, 1, SEEK_SET );: Why do you skip the first character of the file? (File offsets start at 0 not 1). Also: After a successfull fopen, the file pointer always points to the beginning of the file.
After you read your leaderboard, you should fclose the file, and reopen it for (over-)writing. (Better keep a backup, if the leader board is important). So: Open "r" for reading, read, close, open "w" for writing, write, close.
You don't do any error checking in your code. You should fix that and inform the user on error and try to handle the error as gracefully as possible. (I.e. do not loose your leaderboard if writing fails.)
Concerning the added part 1982289408 0: Make sure, your new leader (struct `leaderboardĀ“) is properly initialized).
Your algorithm has a slight problem: A new player, who was exactly as good as a previous one, will move that previous one out of the leader board. To fix that, load the file into old[0..4] and put new leader into old[5] (instead of putting him in old[0] and the file content in old[1..5])
Minor imporovement: The sorting loop: for (int i = 0; i < 6; i++) for (int j = i + 1; j < 6; j++)-> thefor iloop only needs to run from 0 .. 4, sofor (int i = 0; i < 5; i++)` would suffice.

Related

How to read multiple files with the same prefix in C?

I have a large number of files with prefix info_ _ _ _.txt (eg. info0921,info1231,info0426).
The last four digit is a date with format "mmdd".
I need to write a C program to read all those files, store data into array and do some calculation with it.
I tried to do something like that:
for(i = 0; i < Number_of_files; i++){
sprintf(filename, "info%d.txt", i+1);}
However, it doesn't work well because "i" does not fit the date format.
You have two way :
You try to open all the file that could match your pattern (so approximatively 366 possibility)
You open the directory where your files are, list all the entry, and for each entry, you see if that's match your pattern.
For the first one, something like
for(i = 1; i <= 12; i++) {
for(j = 1; j < 31; j++) {
sprintf(filename, "info%02d%02d.txt", i, j);
if (!file = fopen(filename, "r")) {
if (errno != ENOENT) {
// Real error, log ?
}
} else {
// Yay ! Can use "file" now
Ā  fclose(file);
file = NULL;
}
}
}
will do.
For the last one, we need to know if you're on Linux-like or Windows.

Array dynamically allocated by file size is too large

I am working on a class assignment and need some help with dynamically allocated arrays. I am using file_size to try to pull the file size from 3 files to allocate the array to that size, then I need to write and sort the data in the array. My issue right now is with sizing of the array; right now my output (ignoring sorting) is:
1
3
7
9
0
0
0
0
2
4
8
0
0
0
5
6
10
0
0
0
0
0
0
As you can see it is being padded with extra 0s. Here are the input files:
inputFile1:
1
3
7
9
inputFile2:
2
4
8
inputFile3:
5
6
10
0
I need some help figuring out what's going on with this and where the issue is. I want to get rid of those extra 0s, and I'm not even sure where they are coming from. Help with the sorting would also be appreciated.
file_size:
long file_size(FILE *inputFile)
{
if(inputFile == NULL)
return -1;
long pos = ftell(inputFile);
fseek(inputFile, 0, SEEK_END);
long size = ftell(inputFile);
fseek(inputFile, pos, SEEK_SET);
return size;
}
Main:
int main(void)
{
FILE *file0 = fopen("list0.txt", "r");
FILE *file1 = fopen("list1.txt", "r");
FILE *file2 = fopen("list2.txt", "r");
FILE *output = fopen("hw3.out", "w");
long size0 = file_size(file0);
long size1 = file_size(file1);
long size2 = file_size(file2);
long totalSize = size0 + size1 + size2;
int *numbers = malloc(totalSize * sizeof(int));
int i;
int index = 0;
for(i = 0; i < file_size(file0); i++)
{
if(!feof(file0))
{
fscanf(file0, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < file_size(file1); i++)
{
if(!feof(file1))
{
fscanf(file1, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < file_size(file2); i++)
{
if(!feof(file2))
{
fscanf(file2, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < totalSize; i++)
{
fprintf(output, "%i\n", numbers[i]);
}
fclose(file0);
fclose(file1);
fclose(file2);
fclose(output);
free(numbers);
return 0;
}
Your input files have several lines, each of which has the textual representation of a number. Your file size function however is counting the total number of bytes in the file. These are not the same.
While you can still use the file size to allocate space (you'll just get more than you need), you need to instead check the return value of scanf to see if a number was read. If not, you jump out of the loop.
int index = 0;
while (fscanf(file0, "%i", &numbers[index]) == 1) {
index++;
}
while (fscanf(file1, "%i", &numbers[index]) == 1) {
index++;
}
while (fscanf(file2, "%i", &numbers[index]) == 1) {
index++;
}
for(i = 0; i < index; i++)
{
fprintf(output, "%i\n", numbers[i]);
}
The size calculation includes the carriage return "\n" character at the end of the file. Therefore, you are allocating space for 8 integers for the first file, 6 integers for the second file and 10 integers for the third file (10, because the number "10" has two digits).
The correct allocation strategy would be not to count the bytes in the files, but the lines (which actually contain numbers, thus allowing you to skip empty lines).
But that's just too much trouble. Instead, consider just allocating 1000 bytes, read in until you run out, then re-alloc into a larger buffer.

File display error C

I am trying to display a file with in the command prompt window, but the problem is that the output has random 0s in it, however, it always has the right formatting and right amount of rows and spaces. The problem is not the file. If you have any ideas, I'll greatly appreacite it. I have no leads at the moment.
void one (void)
{
FILE *fptr;
fptr = fopen("C:\z.txt","r+");
int j, h;
printf("How many elements?");
scanf("%d",&h);
if (fptr==NULL)
{
printf("Error!");
exit(1);
}
//fread(a, sizeof(struct book),1,fptr);
for (j = 0 ; j < h ; j++)
{
//fprintf(stdout, "%s\t%s\t%s\t%.2f\t%.2f\t%.2f\t%d\t%d\t%.2f\t\n",a[j].company,a[j].model,a[j].colour,a[j].cost,a[j].price,a[j].value,a[j].stock,a[j].sold,a[j].profit);
fscanf(fptr,"%s\t%s\t%s\t%.2f\t%.2f\t%.2f\t%d\t%d\t%.2f\t\n",
a[j].company,
a[j].model,
a[j].colour,
&a[j].cost,
&a[j].price,
&a[j].value,
&a[j].stock,
&a[j].sold,
&a[j].profit
);
}
fclose(fptr);
//getch();
for (j = 0 ; j < h ; j++)
{
printf("%s\t%s\t%s\t%.2f\t%.2f\t%.2f\t%d\t%d\t%.2f\t\n",
a[j].company,
a[j].model,
a[j].colour,
a[j].cost,
a[j].price,
a[j].value,
a[j].stock,
a[j].sold,
a[j].profit
);
}
}

How can I merge N files

I have N files, in each lines of every file there are unique words and the word count, for example
file1 file2 file3
the 2 black 3 red 4
apple 4 tree 2 crab 6
snake 3 mantle 8 puppet 1
How can I merge these files in sorted order into a one file, thanks in advance, really need an idea.
void *doMerge(void* arg){
int i,j,p = 0, k = 0;
char*min = malloc(sizeof(char)*256);
FILE *f = fopen("fileFinal","w+");
char **StringBuffer = (char **)malloc(sizeof(char*)*R);
int *IntBuffer = malloc(sizeof(int)*R);
for(i=0; i < R; i++){
rewind(temp2[i]);
StringBuffer[i] = (char *)malloc(sizeof(char)*256);
if(fscanf(temp2[i],"%s %d",StringBuffer[i],&IntBuffer[i]) == 1)
printf("String is %s and int is %d\n",StringBuffer[i],IntBuffer[i]);
}
while(1){
strcpy(min, StringBuffer[0]);
for(j=0 ; j < R-1; j++){
if(strcmp(StringBuffer[j+1],StringBuffer[j]) <= 0){
strcpy(min,StringBuffer[j+1]);
p = j+1;
}
}fprintf(f,"%s %d\n",min, p);
if(fscanf(temp2[p],"%s %d",StringBuffer[p],&IntBuffer[p]) == 1){}
k++;
}
};
while(1)
{
strcpy(min, StringBuffer[0]);
for(j=0 ; j < R-1; j++){
if(strcmp(StringBuffer[j+1],StringBuffer[j]) <= 0)
{
strcpy(min,StringBuffer[j+1]);
p = j+1;
}
}
fprintf(f,"%s %d\n",min, p);
if(fscanf(temp2[p],"%s %d",StringBuffer[p],&IntBuffer[p]) == 1)
{}
k++;
}
That while loop has a number of different problems
1) p needs to be initialized to 0 before the for loop
2) the strcmp should compare min to StringBuffer[j], which also means that
2a) the for loop should be for(j=1;j<R;j++), and
2b) strcpy(min,StringBuffer[j+1]); should be strcpy(min,StringBuffer[j]);, and
2c) the line p=j+1 should be p=j
3) k is not used for anything and should be removed
But the most important thing is the fscanf. You should be checking that fscanf returns 2. If not then the file is done, and you need to keep track of the fact that the file is done, and that file should not be checked in the for loop any more.

Read last line of file first in C

I currently have a file that appends new entries to the current file. I would like to fetch the 5 most recent entries. How can I read the last line first in C? I would like to use the fgets command to read in line by line if that's possible.
Thank you for your help!
Edit:
For instance:
Original File:
The cat is fast.
Dogs are cool.
I like pie.
Desired Output:
I like pie.
Dogs are cool.
The cat is fast.
while(fgets(buffer,sizeof(buffer),fp); //go on scanning lines
//Now `buffer` holds the last line of `fp`
#include <stdio.h>
#define N 5 //number of recent entry
int main(void){
long entry[N+1];//+1 for end of file
int i, index = 0;
FILE *fp = fopen("entry.txt", "r");
char line[128];
for(i=0;i<N+1;++i)
entry[i] = -1L;//initialize to invalid value
do{ //path I : store file position
entry[index++] = ftell(fp);
if(index == N+1)
index = 0;
}while(EOF!=fscanf(fp, "%*[^\n]%*c"));
if(--index < 0)//one back index
index += N+1;
entry[index] = -1L;//for end of file
for(i = 0; i < N; ++i){//get N entry
if(--index < 0)
index += N+1;
if(entry[index] < 0L)
break;//when number of entry < N
fseek(fp, entry[index], SEEK_SET);
fgets(line, sizeof line, fp);
fputs(line, stdout);
}
fclose(fp);
return 0;
}

Resources