Find most frequent string in a binary file C - c

The purpose of this program is to ask user input for how many exams will be entered into a binary file. When we get user input, e.g. 5, the do while loop asks for input of the name of the course in which the exam was taken and the grade that was received. This gets written in the exams.bin and opened for reading and finding the occurrence of a user inputted keyword for an exam which grade average should be calculated. It finds the grades for that specific course, adds them up and divides them with the number of students that took the exam (the number of recurrence of the name of course). It then prints out a float. And this all works in the code below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char courseName[30];
int grade;
} exam;
int main()
{
exam firstExam, secondExam;
int examNumber, i = 1, j = 1, gradeSum = 0, studentNumber = 0, mostFreq = 0;
float gradeAverage;
char subjectName[30];
char mostFrequent[30];
FILE *pointerBinExam = NULL;
char fileName[13] = "exams.bin";
pointerBinExam = fopen(fileName, "w+b");
if(pointerBinExam == NULL)
{
printf("File doesn't exist!");
exit(EXIT_FAILURE);
}
printf("How many exams will you input: ");
scanf("%d", &examNumber);
fflush(stdin);
do{
printf("\nCourse name: ");
fgets(firstExam.courseName, 30, stdin);
printf("\nGrade: ");
scanf("%d", &firstExam.grade);
fwrite(&firstExam, sizeof(exam), 1, pointerBinExam);
fflush(stdin);
i++;
}while(i < examNumber+1);
rewind(pointerBinExam);
printf("\nInput the name of the course you wish to calculate grade average: ");
fgets(subjectName, 30, stdin);
do{
fread(&secondExam, sizeof(exam), 1, pointerBinExam);
if (strncmp(subjectName, secondExam.courseName, 30) == 0){
gradeSum += secondExam.grade;
studentNumber += 1;
}
j++;
}while(j < examNumber+1);
gradeAverage = (float)gradeSum/studentNumber;
printf("\nNumber of students who took the exam: %d\n", studentNumber);
printf("\nGrade average: %f", gradeAverage);
studentNumber = 0;
rewind(pointerBinExam);
strcpy(mostFrequent, secondExam.courseName);
rewind(pointerBinExam);
do{
fread(&secondExam, sizeof(exam), 1, pointerBinExam);
if (strncmp(mostFrequent, secondExam.courseName, 30) == 0){
mostFreq += 1;
studentNumber += 1;
}
j++;
}while(j < examNumber+1);**
fclose(pointerBinExam);
return 0;
}
The last part of the code, the new do while loop is not a good approach for my problem. My goal is to put the pointer at the beginning of the binary file and read it again. It should find the most frequent course name in the binary. How should I go about doing this? Is there a command I'm unaware of, or should this be handled with a 2D array that sorts the strings in the binary. I'm clueless and only beginning to understand binaries, this is a test assignment I need to practice. Any tips would be highly appreciated!

To figure out the most frequent course if the binary file is constructed of records in form of exam you need to read the file from the beginning to the end and then have some kind of counter for each course.
This can be done in many ways, e.g. encode each course name to a unique integer and then count ++1 every time you find the same course in the file or you could have a list of courses encountered then search the list - if you find the same course add to counter or add new node in the list with counter = 1

Related

Having problems with 2D char arrays

So I've got an assignment where my program asks the brand (10 letters), model (10 letters), age (1986 - 2019) and cost (positive real number) of 10 cars and then wants the program to check which car is the oldest and to print out it's brand and model. I don't have a problem with the first part but with the second part.
The code is:
//First part
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define C 10
#define M 11
int main(void)
{
char brand[C][M];
char model[C][M];
int year[C];
float cost[C];
int i, len1, len2, min;
for(i=0; i<C; i++){
printf("Car %d\n", i+1);
do{
printf("Brand: ");
scanf("%s", brand[i]);
len1 = strlen(brand[i]);
} while(len1<0 || len1>10);
do{
printf("Model: ");
scanf("%s", model[i]);
len2 = strlen(model[i]);
} while(len2<0 || len2>10);
do{
printf("Year: ");
scanf("%d", &year[i]);
} while(year[i]<1986 || year[i]>2019);
do{
printf("Cost: ");
scanf("%d", &cost[i]);
} while(cost[i]<=0);
}
//Second part
year[0] = min;
for(i=0; i<10; i++)
if(year[i] < min){
min = year[i];
printf("\nThe oldest car is %s %s\n", brand[i], model[i]);
}
For some reason it either prints out gibberish in the place of brand[i] or if I lose the columns of the if statement prints out all the car brands and their models, where I only want the oldest one.
Aside from scanf not being recommended there are some problems with this code, first when you read the brand and model you do:
do{
printf("Brand: ");
scanf("%s", brand[i]);
len1 = strlen(brand[i]);
} while(len1<0 || len1>10);
The problem here is that you first write the string to brand[i] and then check if it's too long, but you have already written it into the array so if the string is longer than your space you already have a buffer overflow. Limit the size you can read with scanf using scanf("%10s, brand[i]) or better yet use fgets(brand[i], sizeof(brand[i]), stdin).
Next in the second part you use min without initializing it, and you overwrite the content of year[0] with it. You probably wanted something like:
min = 2020; // or a number that will be bigger than all your cars anyway
int older = 0;
i = 0;
for(i=0; i<C; i++){ // Use C here, you have it might as well use it instead of magic numbers
if(year[i] < min){
older = i;
min = year[i];
}
}
printf("\nThe oldest car is %s %s\n", brand[older], model[older]);
but bare in mind that this solution will print multiple cars if they are the oldest ones and have the same year

Partial array output includes garbage

Most of my experience is limited to SQL scripting for DBA functions. I am a security specialist and provide help to others on those topics, but I am learning C to aid in those other endeavors. I've been reading books, writing small programs, and expanding the difficulty level as I go. This is the first time I've had to reach out for help. I apologize if this has been asked, but I did search first and didn't find anything.
So far, my programs have always returned only the valid data from partially filled arrays. This particular one is not behaving the same even though I'm using the same for statement I have previously used with success. At this point I must have tunnel vision because I cannot seem to see where this is failing.
If there are fewer than 20 inputs, the printf output displays the remaining values with garbage. It would be greatly appreciated if someone could provide some guidance on what I'm overlooking. Thank you in advance.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct grade
{
int id;
int percent;
};
#define maxCount 100
int main()
{
int *grade;
struct grade gradeBook[maxCount];
int count = 0;
char YN;
int i;
for(i = 0; i < maxCount; i++)
{
printf("Enter ID: ");
scanf("%d", &gradeBook[i].id);
printf("Enter grade from 0-100: ");
scanf("%d", &gradeBook[i].percent);
count++;
// Prompt to continue, break if done
printf("Do you want to Continue? (Y/N)");
scanf(" %c", &YN);
if(YN == 'n' || YN == 'N')
{
break;
}
}
void sort(struct grade gradeBook[],int cnt)
{
int i, j;
struct grade temp;
for (i = 0; i < (cnt - 1); i++)
{
for (j = (i + 1); j < cnt; j++)
{
if(gradeBook[j].id < gradeBook[i].id)
{
temp = gradeBook[j];
gradeBook[j] = gradeBook[i];
gradeBook[i] = temp;
}
}
}
}
printf("Grades entered and ordered by ID: \n");
for (i = 0; i < count; i++)
{
printf("\nID:%d, Grade: %3d\n", gradeBook[i].id,gradeBook[i].percent);
}
return 0;
}
If there are fewer than 20 inputs, the printf output displays the remaining values with garbage
What else did you expect?
If you have fewer than 20 inputs, then the remaining inputs have not been given any value. You say "partial array input" but you literally asked the computer to loop over the entire array.
It's really not clear what else you expected to happen here.
Perhaps loop to count the second time instead.

How i cannot ignore 0(zero) as a grade?

int main(){
char students_number[30], students_grade[30];
char *number, *value;
int flag=0, students, i, grade, a=0, b=0, c=0, d=0, f=0;
float sum=0;
while(flag==0) // This while loop exist just because to run program until the number of students will be given correct..
{
printf("Please enter the number of students (It must be between 1-100): ");
scanf("%s",&students_number); // This scanf gets the number of students as an array instead of integer because the number which was given needs to be checked..
students = strtol(students_number, &number, 10); // strtol is a function of stdlib.h and checks the variable is whether int or not for this program..
if(students<=100 && students>0)
{
for(i=1;i<=students;i++)
{
printf("Please enter %d. student's grade (in integer form):",i);
scanf("%s",&students_grade);// This scanf gets the number of students as an array instead of integer because the number which was given needs to be checked..
grade = strtol(students_grade, &value, 10); // This line checks the grade which was given is integer or not by using strtol which is in the stdlib.h..
if(grade<0 || grade>100 || grade=='\0')
{
printf("The grade of the student was given incorrect!\n");
i--; // To make the for loop which is on the 25th line work again until the grade will be given correct..
}
else
{
if(grade<=50 && grade>=0) // This if and else if commands work for to count how many f,d,c,b and a are exist..
f++;
else if(grade<=60 && grade>=51)
d++;
else if(grade<=73 && grade>=61)
c++;
else if(grade<=85 && grade>=74)
b++;
else if(grade<=100 && grade>=86)
a++;
sum += grade;
}
}
sum /= students; // This command divides the sum of the grades to number of the students to get the average results in the class..
printf("\nThe average result of the class is %.2f..\n",sum);
printf("\nThe letter form of the all results are:\nNumber of F: %d\nNumber of D: %d\nNumber of C: %d\nNumber of B: %d\nNumber of A: %d\n",f,d,c,b,a);
flag = 1; // As it was mentioned before, this commands exist to break the while loop because the program was done successfully..
}
else // This if command controls the number of students wheter was given right or not..
{
printf("Please enter a proper number of students.\n");
flag = 0;
}
}
return 0;
}
Hello, this is my first question. I had to create a program which calculates the average of the results. But when i enter 0(zero) as a grade then it doesn't allow it just because i tried to exclude the every types except int type.
How can i make this correct?
You can use scanf to read a number and check that scanf done correctly its work:
from man scanf:
Return Value
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
So you can check that you've read an integer with scanf, without writing if (value == '\0'), which prevents you to read 0 grades...
for(i=1;i<=students;i++)
{
int ok;
printf("Please enter %d. student's grade (in integer form):",i);
/* read line from input. Why using fgets instead of scanf directly? See http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html*/
if (NULL == fgets(students_grade, sizeof students_grade, stdin))
{
perror("fgets");
exit(1);
}
/* **try** to parse what have been read */
ok = sscanf(students_grade, "%d", &value);
/* check that scanf has done its work */
if (1 != ok)
{
printf("The grade of the student was given incorrect!\n");
i--; // To make the for loop which is on the 25th line work again until the grade will be given correct..
}
else
{
if(grade<=50 && grade>=0) // This if and else if commands work for to count how many f,d,c,b and a are exist..
f++;
/* ... */
}
I also advice you to read this article: http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html.

Retrieving User Input for Arrays in C

Beginner programming including arrays and I'm having trouble just getting user input for the arrays. The printf functions I've included are just to check whether my arrays are working, the larger program I'm writing just needs to use these two arrays.
The input for the char array seems to work fine, I've tried a couple of different methods. However, the int array doesn't seem to work using the same diversity of methods I've used successfully with the char array. Not sure what I'm missing. Below is the code and the output when I run the program:
int main()
{
char grades[5]; // create array to store letter grades
int hours[5]; // array to store hours
puts("Please enter letter grades:"); // Input letter grades using fgets
fgets(grades, 5, stdin);
printf("Letter grade for course 3 is %c.\n", grades[2]);
int x = 0;
puts("Please enter course hours:\n");
for (x = 0; x < 5; x++)
{
scanf("%d", &hours[x]);
}
printf("Course hours for course 2 are: %d.\n", hours[1]);
return 0;
}
Output of this code:
Please enter letter grades:
ABCDF <- user input
Letter grade for course 3 is C.
Please enter course hours:
Course hours for course 2 are: -858993460.
Press any key to continue . . .
fgets(grades, 5, stdin); captures ABCD of the input leaving F in the input stream. scanf("%d", &hours[x]); can't parse an int from F though it tries five times. Each failure leaves the F in the input stream.
Make buffers large enough for typical input.
Use fgets for all input. Parse with sscanf or others. Use the return of sscanf to make sure the parsing was successful.
#include <stdio.h>
int main( void)
{
char grades[50] = ""; // create array to store letter grades
char line[50] = "";
int hours[5] = { 0}; // array to store hours
int result = 0;
puts("Please enter letter grades:"); // Input letter grades using fgets
fgets(grades, sizeof grades, stdin);
printf("Letter grade for course 3 is %c.\n", grades[2]);
int x = 0;
for (x = 0; x < 5; x++)
{
do {
printf("Please enter course %d hours:\n", x + 1);
fgets ( line, sizeof line, stdin);
result = sscanf( line, "%d", &hours[x]);
if ( result == EOF) {
printf ( "EOF\n");
return 0;
}
} while ( result != 1);
}
printf("Course hours for course 2 are: %d.\n", hours[1]);
return 0;
}

keeping score in a guessing game without limits in C

I'm practicing C programming by making a simple guessing game. I found the problem in some notes and it is as follows:
"Write an application which plays a number guessing game: the program picks a random number between one and one hundred. The user then guesses the number and the program tells the user whether the number is higher or lower than their guess, until the user guesses correctly. Once the user has guessed the number correctly, the program should tell the user how many guesses it took, and offer to play the game again. The guesses become the score of the player. Minimum guesses become the highest score."
I coded the game and it works properly. I made a separate function to save the highest score (least guesses) in an external file and it works. The problem is that it rewrites the highest score every time. I thought of initializing the highest score to 1, but the number of guesses cannot be less than that. Note that the number of guesses is not limited.
How can I resolve this issue?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int readScore(void);
void saveScore(int score);
int main(){
int menu=0;
char name[16];
int counter=0;
int rand_num, score;
int guess=0;
FILE *fscore;
while(menu != 4){
printf("\n\n****** NUMBER GUESSING GAME MENU v1.0 ******\n");
printf("\t1.Create Player Name.\n\t2.Play.\n\t3.View Score.\n\t4.Quit.\n");
printf("***************************************\n");
printf("Your Choice: ");
scanf("%d", &menu);
if(menu == 1){
printf("Enter a player name: ");
scanf("%15s", name);
}
else if(menu == 2){
srand(time(NULL));
rand_num = 1 + rand()%100;
while(guess != rand_num){
printf("\nGuess the number picked: ");
scanf("%d", &guess);
if(guess > rand_num){
printf("\nNumber is smaller than your guess.");
}
else if(guess < rand_num){
printf("\nNumber is greater than your guess.");
}
counter++;
}
printf("\nYour guess is correct!!\n");
printf("Number of guesses: %d\n", counter);
if(counter < score){
saveScore(counter);
}
}
else if(menu == 3){
int score = readScore();
printf("\nThe best score is: %d\n", score);
}
}
return 0;
}
int readScore(void){
// Read the score from file
FILE *fscore = fopen("score.txt", "r");
int score=1;
fscanf(fscore, "%d", &score);
fclose(fscore);
return score;
}
void saveScore(int score){
// Save the score to file
FILE *fscore = fopen("score.txt", "w+");
fprintf(fscore, "%d", score);
fclose(fscore);
return 0;
}
Test the return value of fopen()
FILE *fscore = fopen("score.txt", "r");
if (fscore == NULL) /* file does not exist
** no games have been played */
return -1;
Then, in the main program, test for a result of -1
else if(menu == 3){
int score = readScore();
if (score < 0) printf("No games have been played.\n");
else printf("\nThe best score is: %d\n", score);
}
Here! you have created a variable score and it has no defined value and may contain large garbage value lets say 123456!! Hence counter is always less than score!! So your program will save the new high score!!
Now your program will run a new cycle!! but still it won't have your highscore in the int score! Cuz you haven't declared what score value is!! Thus it again uses score as 123456!! Even if your new score is more than your last highscore!! It will replace your last highscore.....
To do so:
1. Assign your (int) score with the last highscore while running main
int main(){
....//all your declarations;
FILE* fscore;
fscore = fopen("score.txt", "r");
if(fscore!=NULL){
fscanf(fscore, "%d", &score);
}
else if(fscore == NULL){ //if you haven't got any score in score.txt
score = //lets_put_a_large_random_value;
}
fclose(fscore);
// Now all the remaining codes begins....
}
I've also found some minor bugs in your programs:-
i. If your program choose menu == 1, the program will terminate after taking user name
ii. void savescore(int score); doesn't need a return value..
iii. You are declaring same int score twice in main() function
Hope Your programs work fine now!!

Resources