我需要有关函数的帮助来显示C

时间:2017-01-06 22:28:07

标签: c sorting return-value

我目前正在为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;
}

3 个答案:

答案 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

  1. struct gamer正在使用char *username;;
  2. "test.txt"文件读取数据时,用户名仅存储指向缓冲区的指针而不是内容gamers[i]->username = n;;
  3. 即使读取的用户名为strcpy(n, ptr);,该存储位于堆中并在下次读取操作时被覆盖;
  4. 作为@ pm100建议,平均值被解码为二进制数据而不是文本值float avg = *(float *) ptr;
  5. 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 *数组进行排序。

    1. 如果sizeof(struct gamer)用于qsort()float averagechar *username将被排序,但未进行优化;
    2. 在分配大小到存储username之后,如果从堆中分配数组struct gamer gamers[count];,则释放内存并不容易;
    3. 游戏玩家的数量很大,存储在堆中可能是个问题;
    4. 分配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);