我目前正在为C编程的初学者课程创建一个非常简单的程序,我被困住了。我希望能够打印保存在文本文件中的5个最佳分数,但现在它只显示前5行而不是最佳得分线。历史部分工作正常并打印所有内容,但我无法理解如何打印5个最佳分数,也许它与排除外部文本文件中的信息有关?对不起,如果这篇文章是愚蠢的,但我需要问某个地方(我真的很基本)。
以下是我认为无法正常运行的代码的一部分,它适用于代码块:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
char name [40]; // Here every integer and character etc. is introduced as global.
int menu(char *name);
int startPractice(int choice);
int startTest(int choice, char *name);
char randomOperator();
int getRandomNumber(int min, int max);
int calc(int a, int b, char c);
int rand(void);
int showHistory();
int saveHistory(char *name, float average);
int showTop5();
struct gamer{
char *username;
float average;
};
int main()
{
printf("Hello and welcome to the program!\n"); // This is the introduction
printf("\nWhat is your name? ");
scanf("%s", &name);
printf("\nWelcome %s! \n\nWhat would you like to do? \n" , name);
menu(&name); // This line calls the main menu.
return 0;
}
int compare(const void *s1, const void *s2)
{
struct gamer *g1 = (struct gamer *)s1;
struct gamer *g2 = (struct gamer *)s2;/* same gender so sort by id */
return g1->average - g2->average;
}
int menu(char *name) { // This is the main menu and whenever this function is called the program starts from here.
int qanswer; // This variable is set so it can be used in this body, it is not a global integer.
printf("\n1. Do practices \n2. Do a test \n3. Quit \n4. Test history \n5. Top 5 \n");
scanf("%d", &qanswer);
switch (qanswer) { // Switch cases for a more compressed code, here only one case will be activated and that is decided by the value of "qanswer".
case 1:
printf("\nNow, you can choose to do practices on: \n\n1. Additions\n2. Subtractions\n3. Addition and Subtractions: ");
printf("\n\nEnter your choice: ");
scanf("%d" , &qanswer);
startPractice(qanswer);
break; //This breaks the code at any chosen case.
case 2:
printf("\nYou will be given 15 questions to solve as the test!\n\n");
printf("\nYou can choose to do the test on: \n\n1. Additions\n2. Subtractions\n3. Addition and Subtractions: ");
printf("\n\nEnter your choice: ");
scanf("%d", &qanswer);
startTest(qanswer, name);
break;
case 3:
printf("\nExiting program... \n");
exit(0); // Shuts down the program.
break;
case 4:
printf("Test history\n");
showHistory();
break;
case 5:
printf("Top 5 test results:\n");
showTop5();
break;
default: // if the user enter anything except 1,2,3 this line of code will be returned.
printf("Your input wasn't valid. Please try again...\n\n");
menu(name); // This calls the main menu again
}
return 0;
}
int startPractice(int choice) { // If practice is chosen then this function will be called.
int a, b, answer1;
char c;
int i;
for (i = 1; i <= 10; i++) { // The for loop runs this code until i=10 and increases by 1 each time. Therefore this code will run 10 times fully.
a = getRandomNumber(1, 30); // This calls the function for a random number.
b = getRandomNumber(1, 30);
if (choice == 1) { // If choice is set to "1" c will be t to + otherwise -.
c = '+';
} else if (choice == 2) {
c = '-';
} else {
c = randomOperator();
}
printf("%d. %d %c %d = ", i, a, c, b);
scanf("%d", &answer1);
while (answer1 != calc(a, b, c)){
printf("Try again! ");
scanf("%d", &answer1);
}
printf("Very good!\n\n");
}
printf("Practice is complete\n");
printf("\nNow what would you like to do?\n");
menu(name);
return 0;
}
int startTest(int choice, char *name) {
int a, b, answer1;
char c;
int counter = 0;
float average;
int i;
for (i = 1; i <= 15; i++) {
a = getRandomNumber(1, 30);
b = getRandomNumber(1, 30);
if (choice == 1) {
c = '+';
} else if (choice == 2) {
c = '-';
} else {
c = randomOperator();
}
printf("%d. %d %c %d = ", i, a, c, b);
scanf("%d", &answer1);
if (answer1 == calc(a, b, c)){
counter++;
}
printf("\n\n");
}
printf("The test is complete\n");
average = (float) counter/(float)15; // This calculates the average score as a float with the counter / 15.
printf("You scored %d out of 15 which is and average of %d %%\n", counter, (int)average*100); // This line takes the value of average and multiply it by 100 to show the percentage.
saveHistory("Test", average);
printf("\nNow what would you like to do?\n");
menu(name); // This function calls the main menu again.
return 0;
}
int calc(int a, int b, char c) { // This function is used to define the equation as (a + b) or (a - b).
switch(c) {
case '+':
return a+b;
case '-':
return a-b;
}
}
char randomOperator() { //This code is used to decide if + or - is going to be used.
switch(getRandomNumber(0,1)) { //Switch statement when you need two or more ways to return a value.
case 0:
return '+'; //If the random number is 0 return is + otherwise it is -.
case 1:
return '-';
}
return '+';
}
int getRandomNumber(int min, int max) { // This simply decides a random value of min 0 and max 1.
return rand() % (max + 1 - min) + min;
}
int showHistory(){
FILE *f;
char c;
f=fopen("test.txt","rt");
if (f) {
while((c=fgetc(f))!=EOF){
printf("%c",c);
}
fclose(f);
printf("\nWhat would you like to do now?\n");
} else {
printf("There is no history file at the moment.");
}
menu(name);
return 0;
}
int saveHistory(char *name, float average){
FILE *f;
char c;
f=fopen("test.txt","a");
fprintf(f, "%s_%.2f\n", name, average);
fclose(f);
return 0;
}
int showTop5(){
//printf("\n");
FILE *f;
char c;
char line[256];
int count = 0;
f=fopen("test.txt","rt");
if (f) {
while (fgets(line, sizeof(line), f)) {
if (strlen(line) > 0) {
count++;
}
}
char delimiter[] = "_";
char *ptr;
fclose(f);
f=fopen("test.txt","rt");
struct gamer gamers[count];
int i = 0;
printf("\n");
while (fgets(line, sizeof(line), f)) {
ptr = strtok(line, delimiter);
char n[40];
strcpy(n, ptr);
//printf("n: %s, ", n);
ptr = strtok(NULL, delimiter);
float avg = *(float *) ptr;
//printf("points: %f\n", avg);
gamers[i].average = avg;
gamers[i].username = n;
i++;
}
qsort(gamers, count, sizeof(struct gamer), compare);
int j;
for(j = 0; j < count; j++){
if (j==5){
break;
}
printf("Rank %d: %s with %.2f %% as average.\n", j+1, gamers[j].username, gamers[j]. average);
}
fclose(f);
} else {
printf("There is no history file at the moment.");
}
menu(name);
return 0;
}
答案 0 :(得分:1)
这不会起作用
ptr = strtok(NULL, delimiter);
float avg = *(float *) ptr;
您正在将文件中的分数视为浮点数的二进制表示形式。它不是,它是文本。你需要做
ptr = strtok(NULL, delimiter);
float avg = atof(ptr);
答案 1 :(得分:0)
所以,当我修复导致程序崩溃的代码时(正如答案中提到的那样,谢谢!)似乎排序的问题来自这行代码而不是正确处理值,因为我想返回它作为百分比,它必须在小数点后停止读取,这使得每个用户得分为0或1,如果这是有道理的。当返回值乘以100时,它现在可以工作。
开始使用此代码:
int compare(const void *s1, const void *s2)
{
struct gamer *g1 = (struct gamer *)s1;
struct gamer *g2 = (struct gamer *)s2;/* same gender so sort by id */
return g1->average - g2->average;
}
这段代码似乎是问题解决者。
int compare(const void *s1, const void *s2)
{
struct Gamer *g1 = (struct Gamer *)s1;
struct Gamer *g2 = (struct Gamer *)s2;/* same gender so sort by id */
return (int)((g2->average - g1->average)*100);
答案 2 :(得分:0)
在考虑之前的评论之后,通过使用struct gamer
优化函数正确管理qsort()
数组之前,仍存在其他问题。
问题1 - 没有分配空间将username
存储到struct gamer
。
struct gamer
正在使用char *username;
; "test.txt"
文件读取数据时,用户名仅存储指向缓冲区的指针而不是内容gamers[i]->username = n;
; strcpy(n, ptr);
,该存储位于堆中并在下次读取操作时被覆盖; float avg = *(float *) ptr;
在showTop5()
函数中使用:
while (fgets(line, sizeof(line), f)) {
ptr = strtok(line, delimiter);
char n[40];
strcpy(n, ptr);
ptr = strtok(NULL, delimiter);
float avg = atof(ptr); // @pm100 proposed solution
gamers[i].average = avg;
gamers[i].username = (char *)malloc(sizeof(char)*(strlen(n)+1));
strcpy(gamers[i].username,n); // allocate and copy the username
i++;
}
而不是
while (fgets(line, sizeof(line), f)) {
ptr = strtok(line, delimiter);
char n[40];
strcpy(n, ptr);
//printf("n: %s, ", n);
ptr = strtok(NULL, delimiter);
float avg = *(float *) ptr;
//printf("points: %f\n", avg);
gamers[i].average = avg;
gamers[i].username = n; // error no size allocated
i++;
}
问题2 - 使用qsort()
时,数据结构的长度应固定。使用指针struct gamer *
数组进行排序。
sizeof(struct gamer)
用于qsort()
,float average
和char *username
将被排序,但未进行优化; username
之后,如果从堆中分配数组struct gamer gamers[count];
,则释放内存并不容易; 分配gamers[]
数组:
struct gamer **gamers;
int i;
gamers = malloc(count*sizeof(struct gamer *));
for (i = 0; i < count; i++) {
gamers[i] = malloc(sizeof(struct gamer));
}
管理对数组的读取操作:
请参阅问题1
中的源代码
更新quicksort的compare
功能:
int compare(const void *s1, const void *s2)
{
struct gamer *g1 = *((struct gamer **)s1); // instead of (struct gamer *)s1;
struct gamer *g2 = *((struct gamer **)s2); // instead of (struct gamer *)s2;
// as @BeginnerC suggests, reverse the comparison
return ((int)((g2->average - g1->average)*100));
}
执行快速排序:
qsort(gamers, count, sizeof(struct gamer *), compare);
打印前5名玩家:
for(j = 0; j < count; j++){
if (j==5){
break;
}
printf("Rank %d: %s with %.2f %% as average.\n", j+1,
gamers[j]->username, gamers[j]->average);
}
释放已分配的gamers[]
数组:
for (i = 0; i < count; i++) {
free (gamers[i]->username); // allocated to the username
free (gamers[i]);
}
free(gamers);
注意:为了简化问题1的解决方案并拥有固定大小的struct gamer
,字段char *username
可以替换为char username[40];
(因为char n[40];
之前strcpy(n, ptr);
。