C语言简易选择题系统

部分效果图:

C语言简易选择题系统_第1张图片

C语言简易选择题系统_第2张图片

C语言简易选择题系统_第3张图片

配置文件如下:

加入新题库时,需要新建一个空文件,并把题目添加到新文件中,并把新文件名填入配置文件中。

实现代码:

/*******************************************************************?
 *
 *  File Name: do-test
 *  Sketch out: A simple multiple-choice system
 *
 *  Date created: 2019/12/18
 *  Author: YWR
 *
 ******************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_TOPIC  1024
#define MAX_OPTION 512
#define MAX_ITEM   50
#define CONFIG_FILE ".\\config.ini"

// Bring white
#define FINEWHITE \
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
// Bring red
#define AZARIN \
FOREGROUND_INTENSITY | FOREGROUND_RED


typedef struct _Config {
    char *test_file[MAX_PATH];  // test-file list
    char *wrong_file[MAX_PATH]; // wrong-file list
    int test_file_num;          // test-file total
    int wrong_file_num;         // wrong-file total
    int item_num;               // file subject total
} Config, *PConfig;

typedef struct _Item {
    int serial_num;             // serial_num
    char topic[MAX_TOPIC];      // topic
    char option[4][MAX_OPTION]; // options
    char answer[2];             // answer
} Item, *PItem;

static Item subject[MAX_ITEM];
static Config config;


void trim_config(char *s);
bool read_config();
void level_one_menu();
void level_two_menu();
void get_setting();
char *get_test_mark();
int read_test(const char *pfile);
int get_corrent(int min, int max);
bool file_is_null(FILE *tmp);
void clean_test();
int get_int();
bool add_subject(const char *pfile);
void free_file_name();
void set_cmd_color();

/** read_test: read file items
 * If return -1: can't found file
 * If return  0: read file is error
 * If return  1: read file is ok
 */
int read_test(const char *pfile)
{
    // Opened test-file
    FILE *in = NULL;
    in = fopen(pfile, "r+b");
    if (!in)
    {
        fprintf(stderr, "无法找到 %s.\n",
                pfile);
        return -1;
    }
    // If file is null
    if (file_is_null(in))
    {
        fprintf(stderr, "%s 是空的.\n",
                pfile);
        // Clean struct
        config.item_num = 0;
        clean_test();
        // Closed file
        fclose(in);
        return false;
    }
    int count = 0;
    // Read subject
    rewind(in);
    while (count < MAX_ITEM && (!feof(in)))
    {
        if (fread(&subject[count], 1, sizeof(Item), in)
            != sizeof(Item))
            break;
        count++;
    }
    // If Read is error
    if (count == 0)
    {
        clean_test();
        fprintf(stderr, "读取了 0 条项目\n");
        return false;
    }
    // If Read is ok
    else
    {
        config.item_num = count;
        printf("读取了 %d 条项目\n", count);
    }
    // Closed test-file
    if (fclose(in))
    {
        fprintf(stderr, "无法关闭 %s.\n",
                pfile);
        return false;
    }
    return true;
}

/** trim_config: delete string last '\n'
 * no return value
 */
void trim_config(char *s)
{
    int n;
    for (n = strlen(s)-1; n >= 0; n--)
    {
        if (s[n]!=' ' && s[n]!='\t' && s[n]!='\n')
            break;
        s[n]='\0';
    }
}
/** free_file_name: free heap space for names
 * no return value
 */
void free_file_name()
{
    int i;
    for (i = 0; i < config.test_file_num; i++)
        free(config.test_file[i]);
    for (i = 0; i < config.wrong_file_num; i++)
        free(config.wrong_file[i]);
}

/** read_config: read configuration files
 * If return 0: read error or can't found file
 * If return 1: found file and read ok
 */
bool read_config()
{
    // Opened config-file
    FILE *in = NULL;
    in = fopen(CONFIG_FILE, "r+b");
    if (!in)
    {
        fprintf(stderr, "无法找到 config-ini.\n");
        return false;
    }
    // order: Said file
    // param: config-file data
    // annontation: comments mark
    int order = 0;
    char param[256];
    int param_len = 0;
    int annontation = 0;
    int test_count, wrong_count;
    test_count = wrong_count = 0;
    /* Read configuration file and analyze */
    while (fgets(param, 256, in) != NULL)
    {
        trim_config(param);
        // Filter out comments
        if (param[0] != '#' && (param[0] != '/' || param[1] != '/'))
        {
            if (strstr(param, "/*") != NULL)
            {
                annontation = 1;
                continue;
            }
            else if (strstr(param, "*/") != NULL)
            {
                annontation = 0;
                continue;
            }
        }
        if (annontation == 1)
            continue;

        param_len = strlen(param);
        if (param_len <= 1 || param[0] == '#' || param[0] == '/' || param[0] == '=')
        {
            order++;
            continue;
        }
        // Param: *.txt'\n''\0'
        // first '\n' is in file
        param[param_len-1] = '\0';
        /* If order is 1, it's test-file
         * If order is 2, it's wrong-file */
        if (order == 1)
        {
            // *.txt'\0' len is 5+1
            config.test_file[test_count] = (char *)malloc((strlen(param)+1)*sizeof(char));
            strcpy(config.test_file[test_count], param);
            test_count++;

            config.test_file_num = test_count;
        }
        else if (order == 2)
        {
            config.wrong_file[wrong_count] = (char *)malloc((strlen(param)+1)*sizeof(char));
            strcpy(config.wrong_file[wrong_count], param);
            wrong_count++;
            config.wrong_file_num = wrong_count;
        }
        else // Read file is error, over two #
            return false;
    }
    // Closed config-file
    if (fclose(in))
    {
        fprintf(stderr, "无法关闭 config-ini\n");
        return false;
    }
    return true;
}

/** file_is_null: determine if the file is null
 * If return 1: file is null
 * If return 0: file isn't null
 */
bool file_is_null(FILE *tmp)
{
    char ch;
    ch = getc(tmp);
    if (ch == EOF)
        return true;
    return false;
}

void clean_test()
{
    int i;
    for (i = 0; i < MAX_ITEM; i++)
        memset(&subject[i], 0, sizeof(Item));
}

void set_cmd_color(int forecolor)
{
    // Get output handle
    handle_t std_out = GetStdHandle(STD_OUTPUT_HANDLE);
    // Set CMD color
    SetConsoleTextAttribute(std_out, forecolor);
}

/** begin_test: traverse the structure that holds the data
 * no return value
 */
void begin_test()
{
    system("cls");
    int index, ops;
    int count = 0;
    char answer[config.item_num][2];

    while (count < config.item_num)
    {
        printf("%d. %s\n", subject[count].serial_num,
               subject[count].topic);
        ops = 0;
        while (ops < 4)
            puts(subject[count].option[ops++]);
        answer[count][0] = toupper(getch());
        while (answer[count][0] < 'A' || answer[count][0] > 'D')
            answer[count][0] = toupper(getch());
        answer[count++][1] = '\0';
        system("cls");
    }
    int is_ok = 0;
    // Give a mark
    for (index = 0; index < config.item_num; index++)
    {
        if (strcmp(answer[index], subject[index].answer) == 0)
        {
            is_ok++;
            set_cmd_color(FINEWHITE);
        }
        else
            set_cmd_color(AZARIN);
        printf("%2d:%-2s", index+1, answer[index]);
        set_cmd_color(FINEWHITE);
        printf("-%-2s", subject[index].answer);
        if ((index+1)%5==0)
            putchar('\n');
    }
    printf("您的总分:%3d [%d/%d(%.2f%%)]\n", is_ok*3, is_ok, config.item_num,
           100.0*((float)is_ok/config.item_num));
    set_cmd_color(FINEWHITE);
    getchar();
}

/** get_int: gets a character from the buffer, and delete '\n'
 * return large letter
 */
int get_int()
{
    int ch;
    ch = getchar();
    // Clean memory
    while (getchar() != '\n')
        continue;
    return towupper(ch);
}

/** add_subject: add an item to the file
 * If return 0: can't found file or file is full
 * If return 1: add an item to the file success
 */
bool add_subject(const char *pfile)
{
    FILE *in = NULL;
    in = fopen(pfile, "r+b");
    if (!in)
    {
        fprintf(stderr, "无法找到 %s.\n",
                pfile);
        return false;
    }
    int index, ops;
    int count = config.item_num;
    // If file is full
    if (count == MAX_ITEM)
    {
        fprintf(stderr, "%s 是满的 <50>.\n",
                pfile);
        return false;
    }
    // Add new subject
    puts("输入添加的题目:");
    while (count < MAX_ITEM && gets(subject[count].topic) != NULL
                            && subject[count].topic[0] != '\0')
    {
        ops = 0;
        // Read option [1,4]
        while (ops < 4)
        {
            printf("输入添加的选项[%d]:", ops+1);
            gets(subject[count].option[ops]);
            ops++;
        }
        puts("输入题目的答案:");
        gets(subject[count].answer);
        // 'c' convert into 'C'
        subject[count].answer[0] = towupper(subject[count].answer[0]);
        subject[count].answer[1] = '\0';
        // serial_num [1,50]
        subject[count].serial_num = count+1;
        count++;
        // If over max-items
        if (count < MAX_ITEM)
            puts("请输入下一个题目:");
    }
    // If file is not null
    if (count > 0)
    {
        rewind(in);
        puts("Here is the list of your subjects:");
        for (index = 0; index < count; index++)
        {
            printf("%d. %s\n", subject[index].serial_num,
                   subject[index].answer);
            fwrite(&subject[index], 1, sizeof(Item), in);
        }
    }
    else
        puts("没有题目? 太槽糕了.\n");
     // Closed test-file
    if (fclose(in))
    {
        fprintf(stderr, "无法关闭 %s.\n",
                pfile);
        return false;
    }
    return true;
}

/** modity_subject: modity an item to the file
 * If return 0: can't found file or file is null
 * If return 1: modity an item to the file success
 */
bool modity_subject(const char *pfile)
{
    FILE *in = NULL;
    in = fopen(pfile, "r+b");
    if (!in)
    {
        fprintf(stderr, "无法找到 %s.\n",
                pfile);
        return false;
    }
    int index;
    int count = config.item_num;
    // If file is null
    if (count == 0)
    {
        fprintf(stderr, "%s 是满的 <50>.\n",
                pfile);
        return false;
    }
    // If ch not in [1,50]
    int tmp_num;
    printf("输入一个编号去查找(1-%d):",
           count);
    tmp_num = get_corrent(1, count);
    for (index = 0; index < count; index++)
    {
        if (subject[index].serial_num == tmp_num)
        {
            puts("输入一个新的答案:");
            gets(subject[index].answer);
            // small leter convert into large leter
            subject[index].answer[0] = toupper(subject[index].answer[0]);
            subject[index].answer[1] = '\0';
            fseek(in, index * sizeof(Item), SEEK_SET);
            fwrite(&subject[index], 1, sizeof(Item), in);
            break;
        }
    }
    // Closed test-file
    if (fclose(in))
    {
        fprintf(stderr, "无法关闭 %s.\n",
                pfile);
        return false;
    }
    return true;
}

/** show submenu */
void level_two_menu()
{
    puts("<--子菜单-->");
    puts("1. 添加题目");
    puts("2. 修改题目");
    puts("Q. 返回主菜单");
}

/** get_setting: gets a select change file
 * no return value
 */
void get_setting()
{
    int choices;
    char *pfile = NULL;
    level_two_menu();
    while ((choices = get_int()) != 'Q')
    {
        switch (choices)
        {
        case '1': // add subject
            pfile = get_test_mark();
            if (read_test(pfile) != -1)
                add_subject(pfile);
            break;
        case '2': // modify subject
            pfile = get_test_mark();
            if (read_test(pfile) != -1)
                modity_subject(pfile);
            break;
        case 'q':
            return;
        default:
            puts("您必须输入 1-2 或者退出\n");
        }
        level_two_menu();
    }
}

/** show_enable_file: show enable the file
 * no return value
 */
void show_enable_file(char n)
{
    int i;
    printf("关于 %s 的列表:\n",n=='1'?"Test":"Wrong");
    if (n == '1')
        for (i = 0; i < config.test_file_num; i++)
        {
            printf("%d:[%s] ", i+1,config.test_file[i]);
            if ((i+1) % 5 == 0)
                putchar('\n');
        }
    else if (n == '2')
        for (i = 0; i < config.wrong_file_num; i++)
        {
            printf("%d:[%s] ", i+1,config.wrong_file[i]);
            if ((i+1) % 5 == 0)
                putchar('\n');
        }
    printf("\n\n");
}

/** get_corrent: get a corrent range
 * return a number
 */
int get_corrent(int min, int max)
{
    int n;
    scanf("%d", &n);
    while (n < min || n > max)
    {
        printf("您必须输入 %d-%d 的范围:",
               min,max);
        scanf("%d", &n);
    }
    while (getchar() != '\n')
        continue;
    return n;
}

/** change_enable_file: gets the file to select
 * return a file pointer
 */
char *change_enable_file(char n)
{
    int c;
    char *tmp_file = NULL;
    if (n == '1')
    {
        c = get_corrent(1, config.test_file_num);
        tmp_file = config.test_file[c-1];
    }
    else if (n == '2')
    {
        c = get_corrent(1, config.wrong_file_num);
        tmp_file = config.wrong_file[c-1];
    }
    return tmp_file;
}

/** get_test_mark: And "change_enable_file" combination
 * return a file pointer
 */
char *get_test_mark()
{
    int ch;
    puts("您想要选择哪种类型的文件?");
    puts("1. 考试文件 2. 错题文件");
    ch = get_int();
    // Choices <1 or 2>
    show_enable_file(ch);
    // Load test-file
    return change_enable_file(ch);
}

/** show main menu */
void level_one_menu()
{
    puts("<--主菜单-->");
    puts("A. 开始考试");
    puts("B. 设置文件");
    puts("Q. 退出");
}

int main()
{
    int choices;
    char *pfile = NULL;

    if (!read_config())
    {
        fprintf(stderr, "读取配置信息错误.\n");
        return 1;
    }
    // show main-menu
    level_one_menu();
    while ((choices = get_int()) != 'Q')
    {
        switch (choices)
        {
        case 'A':
            pfile = get_test_mark();
            if (read_test(pfile) == true)
               begin_test();
            break;
        case 'B':
            get_setting();
            break;
        case 'Q':
            fprintf(stderr, "程序错误.\n");
            exit(1);
        }
        level_one_menu();
    }
    // free
    free_file_name();
    puts("再会.");
    return 0;
}

 

你可能感兴趣的:(C/C++,学习,学生管理系统,选择题管理系统,机票系统)