宮商定時制計算部

計算部日記180813 成績処理プログラム

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INCREMENT_SIZE 512

#define GRADE_NUMBER 4
#define GRADE_ARRAY "1", "2", "3", "4"

//リスト用構造体定義。
struct ListNode {
    struct ListNode *next;
    char **member;
};

//評定平均リスト用構造体定義。
struct Hyotei {
    struct Hyotei *next;
    struct ListNode *to_st_node;    //生徒名簿へのポインタ。
    double hyotei;                  //評定平均。
};

//ソートのためのリスト用構造体定義。
struct SortList {
    struct SortList *next;
    struct Hyotei *nodelink;
};

//生徒成績計算結果リスト用構造体定義。
struct StudentCalc {
    struct StudentCalc *next;
    struct ListNode *to_st_node;
    int total[GRADE_NUMBER];
    int sj_num[GRADE_NUMBER];
    double ave[GRADE_NUMBER];
    int rank[GRADE_NUMBER];
};

//科目成績計算結果リスト用構造体定義。
struct SubjectCalc {
    struct SubjectCalc *next;
    struct ListNode *to_sj_node;
    int total;
    int cnt;    //これは必要ないが、アライメント警告防止のために入れておく。
    double ave;
};

//=============================================================================
/*プロトタイプ宣言。*/
FILE *FileOpen(const char * const file_name, const char * const mode);
int ColumnsCount(FILE *fp, const char * const file_name);
struct ListNode *ListCreate(FILE *fp, const int columns_num);

char *BufferExtension(char *buf, const size_t buf_size);
void ListFree(struct ListNode *p);
void ListDisplay(struct ListNode *p);

struct ListNode *smList(const char * const file_name);

struct ListNode **Loop_smList(const char ** const file_name, 
                              const int file_num);
double RoundingSecond(double d);
struct Hyotei *GradeAverage(struct ListNode **headps);
struct Hyotei **GASort(struct Hyotei *hyotei_head_p);
void GADisplay(struct Hyotei **sa, struct Hyotei *hyotei_head_p, 
               const int selected);
void GAFree(struct Hyotei **sa, struct Hyotei *hyotei_head_p);

int GradeTable(struct ListNode **headps, const int selected);

struct StudentCalc *CalcGradeList(struct ListNode **headps);
void CGLValue(struct ListNode **headps, struct StudentCalc *scp_head_p);
int RankStudentCalc(struct StudentCalc *scp_head_p);

struct SubjectCalc *SubjectAverageList(struct ListNode **headps);
void SALValue(struct ListNode **headps, struct SubjectCalc *sjcp_head_p);

void SubjTotalAve(const int year, const int gokei, const int kosu, 
                  struct SubjectCalc *sjcp_head_p);
void GTFree(struct StudentCalc *scp_head_p, struct SubjectCalc *sjcp_head_p);

int OperationSelect(void);

//=============================================================================
/*ファイルを開く関数。*/
FILE *FileOpen(const char * const file_name, const char * const mode) {
    FILE *fp;
      
    fp = fopen(file_name, mode);
    if (fp == NULL) {printf("ファイル %s が開けませんでした。\n", file_name);}
    return fp;
}

//=============================================================================
/*csvファイルの列数(項目数)を求める関数。*/
int ColumnsCount(FILE *fp, const char * const file_name) {
    int c, first_comma_num = 0, comma_num = 0, rows_num = 0, columns_num = 0;
    size_t ch_num = 0, total_ch_num = 0;
     
    while (1) {
        c = fgetc(fp);
        if (c == EOF) {
            if (ch_num == 0) {break;}
            else {c = '\n';}
        }
        ch_num++;
        if (c == ',') {comma_num++;}
        if (c == '\n') {
            rows_num++;
            if (rows_num == 1) {
                first_comma_num = comma_num;
                columns_num = first_comma_num + 1;
            } else {
                if ((comma_num != first_comma_num) ||
                    ((first_comma_num == 0) && (ch_num == 1) && (c == '\n'))) {
                    printf("%s の %d 行目が空白か、または列数が合いません。\n"
                        "該当行のカンマの数や、\n"
                        "空白行になっていないかどうかを確認してください。\n"
                        "処理を中止します。\n", file_name, rows_num);
                    columns_num = -1;
                    return columns_num;
                }
            }
            comma_num = 0;
            ch_num = 0;
        } else {
            total_ch_num++;
        }
    }
    if ((rows_num == 0) || (total_ch_num == 0)) {
        printf("%s は空ファイルです。処理を中止します。\n", file_name);
        columns_num = -1;
    }
    return columns_num;
}

//=============================================================================
/*csvファイルを読み込んで構造体リストを作る関数。*/
struct ListNode *ListCreate(FILE *fp, const int columns_num) {
    char *buf = NULL, *s;
    size_t buf_num = 0, buf_size = 0;
    int *ch_idx, ch_idx_num = 1, c, i;
    struct ListNode *p, *pre_p = NULL, *list_head_p = NULL;
      
    ch_idx = malloc(sizeof(int) * (size_t)columns_num);
    if (ch_idx == NULL) {
        puts("インデックスのメモリが確保できませんでした。");
        return list_head_p;
    }
    ch_idx[0] = 0;
      
    rewind(fp);
    while (1) {
        c = fgetc(fp);
        if (c == EOF) {
            if (buf_num == 0) {break;}
            else {c = '\n';}
        }
        if (buf_size == buf_num) {
            buf = BufferExtension(buf, buf_size);
            if (buf == NULL) {
                list_head_p = NULL;
                break;
            }
            buf_size += INCREMENT_SIZE;
        }
        if ((c != ',') && (c != '\n')) {
            buf[buf_num++] = (char)c;
            continue;
        }
        if (c == ',') {
            buf[buf_num++] = '\0';
            ch_idx[ch_idx_num++] = (int)buf_num;
            continue;
        }
        if (c == '\n') {
            buf[buf_num++] = '\0';
            s = malloc(sizeof(char) * buf_num);
            if (s == NULL) {
                puts("文字列格納領域が確保できませんでした。");
                list_head_p = NULL;
                break;
            }
            memcpy(s, buf, sizeof(char) * buf_num);
            p = malloc(sizeof(struct ListNode));
            if (p == NULL) {
                puts("構造体が作れませんでした。");
                ListFree(list_head_p);
                list_head_p = NULL;
                break;
            }
            p->member = malloc(sizeof(char *) * (size_t)(columns_num + 1));
            if (p->member == NULL) {
                puts("構造体のメンバ領域が確保できませんでした。");
                free(p);
                ListFree(list_head_p);
                list_head_p = NULL;
                break;
            }
            p->member[columns_num] = NULL;
            if (list_head_p == NULL) {
                list_head_p = p;
            } else {
                pre_p->next = p;
            }
            p->next = NULL;
            for (i = 0; i < columns_num; i++) {
                p->member[i] = (char *)(s + ch_idx[i]);    //念のためキャスト。
            }
            pre_p = p;
            ch_idx_num = 1; buf_num = 0;
        }
    }
    free(buf);
    free(ch_idx);
    return list_head_p;
}

//=============================================================================
/*文字列格納領域を拡張する関数。*/
char *BufferExtension(char *buf, const size_t buf_size) {
    char *new_buf;
      
    new_buf = realloc(buf, sizeof(char) * (buf_size + INCREMENT_SIZE));
    if(new_buf == NULL){
        puts("文字列バッファが確保できませんでした。");
        free(buf);
    }
    return new_buf;
}

//=============================================================================
/*構造体リストを解放する関数。*/
void ListFree(struct ListNode *p) {
    struct ListNode *next_p;
     
    while (p != NULL) {
        next_p = p->next;
        free(p->member[0]);
        free(p->member);
        free(p);
        p = next_p;
    }
}

//=============================================================================
/*構造体リストの中身を表示する関数。*/
void ListDisplay(struct ListNode *p) {
    int i = 0;
     
    while (p != NULL) {
        while (p->member[i] != NULL) {
            if (i == 0) {printf("%s", p->member[i]);}
            else {printf(",%s", p->member[i]);}
            i++;
        }
        printf("\n");
        i = 0;
        p = p->next;
    }
}

//=============================================================================
/*ファイルを開いてから構造体リストを作るまでの関数をまとめた関数。*/
//csvファイルから構造体リストを作るときは、この関数を呼ぶだけでよい。
struct ListNode *smList(const char * const file_name) {
    FILE *fp;
    struct ListNode *list_head_p = NULL;
    int columns_num;
    
    fp = FileOpen(file_name, "r");
    if (fp == NULL) {return list_head_p;}
    
    columns_num = ColumnsCount(fp, file_name);
    if (columns_num == -1) {
        fclose(fp);
        return list_head_p;
    }
    
    list_head_p = ListCreate(fp, columns_num);
    fclose(fp);
    
    return list_head_p;
}

//=============================================================================
/*複数のリストを作ってその先頭ポインタを配列に格納する関数。*/
//csvファイルからリストを作るsmList関数をループする。
//失敗なら先頭ポインタ格納配列へのポインタにNULLを代入して返す。
struct ListNode **Loop_smList(const char ** const file_name, 
                              const int file_num) {
    struct ListNode **headps;
    int i, j;
    
    headps = malloc(sizeof(struct ListNode *) * (size_t)file_num);
    if (headps == NULL) {
        puts("リストの先頭ポインタ格納領域が確保できませんでした。");
        return headps;
    }
    
    for (i = 0; i < file_num; i++) {
        headps[i] = smList(file_name[i]);
        if (headps[i] == NULL) {        //ここから下は失敗時の解放処理。
            for (j = 0; j < i; j++) {
                ListFree(headps[j]);
            }
            free(headps);
            headps = NULL;
            break;
        }
    }
    return headps;
}

//=============================================================================
/*小数第二位で四捨五入する関数。*/
double RoundingSecond(double d) {
    int i;
    
    if (d == 0.0) {return 0.0;}    //万一0を受け取った場合は0を返す。
    d = d * 10.0 + 0.5;    //小数第二位で四捨五入して小数第一位まで出すが、
    i = (int)d;            //math.hの関数は使わないことにする。
    d = i / 10.0;          //10倍して0.5を足し、小数を切り捨てて1/10にする。
    
    return d;
}
 
//=============================================================================
/*評定平均リストを作ってその先頭ポインタを返す関数。*/
struct Hyotei *GradeAverage(struct ListNode **headps) {
    struct ListNode *seitomeibo_head_p, *kamokuhyo_head_p, 
                    *seisekihyo_head_p, *p, *q, *r;
    int gokei = 0, kosu = 0;
    double hyotei;
    struct Hyotei *hyotei_p, *hyotei_head_p = NULL, 
                  *hyotei_pre_p = NULL, *hyotei_next_p;
    
    seitomeibo_head_p = headps[0];  //配列のままでは分かりにくいので、
    kamokuhyo_head_p = headps[1];   //先頭ノードを名前付きの変数にコピーする。
    seisekihyo_head_p = headps[2];
    
    p = seitomeibo_head_p;
    while (p != NULL) {        //生徒名簿リストをループ。
        q = kamokuhyo_head_p;
        while (q != NULL) {      //その中で科目表リストをループ。
            r = seisekihyo_head_p;
            while (r != NULL) {    //その中で成績表リストをループ。
                if ((strcmp(p->member[0], r->member[1]) == 0)
                    && (strcmp(q->member[0], r->member[0]) == 0)) {
                    gokei += (int)strtol(r->member[2], NULL, 10);
                    kosu++;
                }    //↑生徒コードが一致したら評定を足していき、個数も求める。
                r = r->next;
            }
            q = q->next;
        }
        if (gokei * kosu != 0) {     //四捨五入関数を呼ぶ(ゼロ除算回避付き)。
            hyotei = RoundingSecond((double)gokei / kosu);
        } else {
            hyotei = 0.0;
        }
        hyotei_p = malloc(sizeof(struct Hyotei));    //以下、評定リストを作る。
        if (hyotei_p == NULL) {                      //ノードが作れなければ……
            puts("評定平均格納構造体が作れませんでした。");
            hyotei_p = hyotei_head_p;                  //作成済みリストを解放。
            while (hyotei_p != NULL) {
                hyotei_next_p = hyotei_p->next;
                free(hyotei_p);
                hyotei_p = hyotei_next_p;
            }
            hyotei_head_p = NULL;                      //先頭ノードをNULLとして
            break;                                     //ループから抜ける。
        }
        hyotei_p->next = NULL;        //各メンバに値を入れる。nextにはNULL。
        hyotei_p->to_st_node = p;     //to_st_nodeには名簿ノードへのポインタ。
        hyotei_p->hyotei = hyotei;    //hyoteiには評定平均値。
        if (hyotei_head_p == NULL) {  //以下はリスト構築処理。
            hyotei_head_p = hyotei_p;
        } else {
            hyotei_pre_p->next = hyotei_p;
        }
        hyotei_pre_p = hyotei_p;
        gokei = 0; kosu = 0;
        p = p->next;
    }
    return hyotei_head_p;
}

//=============================================================================
/*ソート用配列を作って評定平均リストを降順ソートする関数。*/
//ソート用配列の先頭ポインタを返す。
struct Hyotei **GASort(struct Hyotei *hyotei_head_p) {
    struct Hyotei *hp, **sa, *tmp;
    int node_num = 0, i = 0, j, m;
    
    hp = hyotei_head_p;        //評定平均値ノードの数を調べる。
    while (hp != NULL) {
        node_num++;
        hp = hp->next;
    }
        //↓ソート用配列を作る。その際、一つ多く作って番兵を置く。
    sa = malloc(sizeof(struct ListNode *) * (size_t)(node_num + 1));
    if (sa == NULL) {
        puts("ソート用構造体配列が作れませんでした。");
        return sa;
    }
    sa[node_num] = NULL;       //番兵はNULL。
    
    hp = hyotei_head_p;        //配列に評定平均値ノードへのポインタを代入。
    while (hp != NULL) {
        sa[i++] = hp;
        hp = hp->next;
    }
    
    for (i = 0; i < node_num - 1; i++) {     //ソート処理。
        m = i;
        for (j = i + 1; j < node_num; j++) {
            if (
                ((int)(sa[m]->hyotei * 10) < 
                 (int)(sa[j]->hyotei * 10)) 
                ||
                (
                    ((int)(sa[m]->hyotei * 10) == 
                     (int)(sa[j]->hyotei * 10))
                    &&
                    (strcmp(sa[m]->to_st_node->member[0], 
                            sa[j]->to_st_node->member[0]) > 0)
                )
               ) {
                m = j;           //最大値の更新は添字を入れ替えるだけで済む。
            }
        }
        if (i != m) {
            tmp = sa[i];
            sa[i] = sa[m];
            sa[m] = tmp;
        }
    }
    return sa;
}

//=============================================================================
/*評定平均リストを番号順または成績順に表示する関数。*/
void GADisplay(struct Hyotei **sa, struct Hyotei *hyotei_head_p, 
               const int selected) {
    int i = 0;
    struct Hyotei *hyotei_p;
      //switch文で書く場合は、selected == 3をfall throughすることになる。
      //意図したものなのにGCCで-Wextraを付けるとfall through警告が出てしまう。
      //煩わしいので、switch文はやめてdo~while文とif文の組み合わせとした。
    do {
        if ((selected == 1) || (selected == 3)) {
            puts("評定平均値番号順");
            hyotei_p = hyotei_head_p;
            while (hyotei_p != NULL) {
                printf("%s,%s,%.1f\n", 
                    hyotei_p->to_st_node->member[0], 
                    hyotei_p->to_st_node->member[1], 
                    hyotei_p->hyotei);
                hyotei_p = hyotei_p->next;
            }
            if (selected == 1) {break;}
            else {printf("\n");}
        }
        if ((selected == 2) || (selected == 3)) {
            puts("評定平均値成績降順");
            while (sa[i] != NULL) {
                printf("%s,%s,%.1f\n", 
                    sa[i]->to_st_node->member[0], 
                    sa[i]->to_st_node->member[1], 
                    sa[i]->hyotei);
                i++;
            }
        }
    } while (0);
}

//=============================================================================
/*評定平均ソート用配列と評定平均リストを解放する関数。*/
void GAFree(struct Hyotei **sa, struct Hyotei *hyotei_head_p) {
    struct Hyotei *hyotei_p, *hyotei_next_p;
    
    free(sa);
    
    hyotei_p = hyotei_head_p;
    while (hyotei_p != NULL) {
        hyotei_next_p = hyotei_p->next;
        free(hyotei_p);
        hyotei_p = hyotei_next_p;
    }
}

//=============================================================================
/*成績一覧表を作る関数(学年選択機能付き)。*/
int GradeTable(struct ListNode **headps, const int selected) {
    struct ListNode *seitomeibo_head_p, *kamokuhyo_head_p, 
                    *seisekihyo_head_p, *p, *q, *r;
    struct StudentCalc *scp_head_p, *scp;
    struct SubjectCalc *sjcp_head_p;
    const char *gakunen[GRADE_NUMBER] = {GRADE_ARRAY};
    int i, bg, ed, flg = 0, gokei = 0, kosu = 0;
    
    switch (selected) {      //bgとedはループの開始番号と終了番号。
    case 8:                  //全学年の一覧表。ループは学年数。
        bg = 0;
        ed = GRADE_NUMBER;
        break;
    default:                 //単独学年一覧表。ループは1回。
        bg = selected - 4;   //選択肢番号から4を引くと当該学年になる。
        ed = bg + 1;
    }
    
    seitomeibo_head_p = headps[0];
    kamokuhyo_head_p = headps[1];
    seisekihyo_head_p = headps[2];
    
    scp_head_p = CalcGradeList(headps);
    if (scp_head_p == NULL) {return 1;}
    
    sjcp_head_p = SubjectAverageList(headps);
    if (sjcp_head_p == NULL) {return 1;}
    
    for (i = bg; i < ed; i++) {
        printf("\n");
        printf("第%s学年成績一覧表\n", gakunen[i]);
        printf("番号,氏名");
        p = kamokuhyo_head_p;
        while (p != NULL) {
            if (strcmp(p->member[2], gakunen[i]) == 0) {
                printf(",%s", p->member[1]);
            }
            p = p->next;
        }
        printf(",合計,科目数,平均,平均順位");
        printf("\n");
        
        p = seitomeibo_head_p;
        while (p != NULL) {
            printf("%s,%s", p->member[0], p->member[1]);
            q = kamokuhyo_head_p;
            while (q != NULL) {
                r = seisekihyo_head_p;
                while (r != NULL) {
                    if((strcmp(p->member[0], r->member[1]) == 0) &&
                        (strcmp(q->member[0], r->member[0]) == 0) &&
                        (strcmp(q->member[2], gakunen[i]) == 0)) {
                        printf(",%s", r->member[2]);
                        flg = 1;   //↓評価総計。
                        gokei += (int)strtol(r->member[2], NULL, 10);
                        kosu++;    //←全評価平均のための評価総数カウント。
                        break;
                    }
                    r = r->next;
                }
                if ((flg == 0) && (strcmp(q->member[2], gakunen[i]) == 0)) {
                    printf(",");
                }
                flg = 0;
                q = q->next;
            }
            scp = scp_head_p;    //右端に評定合計・科目数・平均・順位を入れる。
            while (scp != NULL) {
                if (strcmp(p->member[0], scp->to_st_node->member[0]) == 0) {
                    printf(",%d,%d,%.1f,%d", 
                    scp->total[i], scp->sj_num[i], scp->ave[i], scp->rank[i]);
                }
                scp = scp->next;
            }
            p = p->next;
            printf("\n");
        }   //↓科目合計と科目平均を記入する関数を呼ぶ。
        SubjTotalAve(i, gokei, kosu, sjcp_head_p);
        gokei = 0; kosu = 0;
    }  //↓生徒成績計算結果リストと科目成績計算結果リストを解放する関数を呼ぶ。
    GTFree(scp_head_p, sjcp_head_p);
    return 0;
}

//=============================================================================
/*生徒成績計算結果リストを作る関数。*/
struct StudentCalc *CalcGradeList(struct ListNode **headps) {
    struct StudentCalc *scp_head_p = NULL, *scp, *pre_scp = NULL, *next_scp;
    struct ListNode *seitomeibo_head_p, *p;
    int is_rank;
    
    seitomeibo_head_p = headps[0];
    
    p = seitomeibo_head_p;
    while (p != NULL) {
        scp = malloc(sizeof(struct StudentCalc));
        if (scp == NULL) {
            puts("生徒別成績集計リストが作れませんでした。");
            scp = scp_head_p;
            while (scp != NULL) {
                next_scp = scp->next;
                free(scp);
                scp = next_scp;
            }
            scp_head_p = NULL;
            return scp_head_p;
        }
        scp->next = NULL;
        scp->to_st_node = p;
        if (scp_head_p == NULL) {
            scp_head_p = scp;
        } else {
            pre_scp->next = scp;
        }
        pre_scp = scp;
        p = p->next;
    }
        //ランキング以外を、当リストのノードに格納する関数を呼ぶ。
    CGLValue(headps, scp_head_p);
        //ランキングを求め、当リストのノードに格納する関数を呼ぶ。
    is_rank = RankStudentCalc(scp_head_p);
    if (is_rank != 0) {
        scp_head_p = NULL;
    }
    return scp_head_p;
}

//=============================================================================
//生徒成績計算結果リストの各ノードに値を格納する関数。
void CGLValue(struct ListNode **headps, struct StudentCalc *scp_head_p) {
    struct StudentCalc *scp;
    struct ListNode *kamokuhyo_head_p, *seisekihyo_head_p, *p, *q;
    const char *gakunen[GRADE_NUMBER] = {GRADE_ARRAY};
    int i, gokei = 0, kosu = 0;
    double heikin;
    
    kamokuhyo_head_p = headps[1];
    seisekihyo_head_p = headps[2];
    
    for (i = 0; i < GRADE_NUMBER; i++) {
        scp = scp_head_p;
        while (scp != NULL) {
            p = kamokuhyo_head_p;
            while (p != NULL) {
                q = seisekihyo_head_p;
                while (q != NULL) {
                    if ((strcmp(scp->to_st_node->member[0], 
                                q->member[1]) == 0) &&
                        (strcmp(p->member[0], q->member[0]) == 0) &&
                        (strcmp(p->member[2], gakunen[i]) == 0)) {
                        gokei += (int)strtol(q->member[2], NULL, 10);
                        kosu++;
                        break;
                    }
                    q = q->next;
                }
                p = p->next;
            }
            if (gokei * kosu != 0) {
                heikin = RoundingSecond((double)gokei / kosu);
            } else {
                heikin = 0.0;
            }
            scp->total[i] = gokei;
            scp->sj_num[i] = kosu;
            scp->ave[i] = heikin;
            scp->rank[i] = 0;    //ランキングは別関数で埋める。ここでは仮の値。
            gokei = 0; kosu = 0;
             
            scp = scp->next;
        }
    }
}

//=============================================================================
/*各生徒の学年ごとのランキングを求めて生徒成績計算結果構造体に書き込む関数。*/
//ソート用配列でソートしてからランキングを出す。
int RankStudentCalc(struct StudentCalc *scp_head_p) {
    int node_num = 0, i = 0, j, m, g;
    struct StudentCalc *scp, **sca, *tmp;
    
    scp = scp_head_p;
    while (scp != NULL) {
        node_num++;
        scp = scp->next;
    }
    
    sca = malloc(sizeof(struct StudentCalc *) * (size_t)(node_num + 1));
    if (sca == NULL) {
        puts("ソート用配列が作れませんでした。");
        return 1;
    }
    sca[node_num] = NULL;
    
    scp = scp_head_p;
    while (scp != NULL) {
        sca[i++] = scp;
        scp = scp->next;
    }
    
    for (g = 0; g < GRADE_NUMBER; g++) {
        for (i = 0; i < node_num - 1; i++) {
            m = i;
            for (j = i + 1; j < node_num; j++) {
                if ((int)(sca[m]->ave[g] * 10) < (int)(sca[j]->ave[g] * 10)) {
                    m = j;
                }
            }
            if (i != m) {
                tmp = sca[i];
                sca[i] = sca[m];
                sca[m] = tmp;
            }
        }
        sca[0]->rank[g] = 1;
        i = 1;
        while (sca[i] != NULL) {
            if ((int)(sca[i - 1]->ave[g] * 10) == (int)(sca[i]->ave[g] * 10)) {
                sca[i]->rank[g] = sca[i - 1]->rank[g];
            } else {
                sca[i]->rank[g] = i + 1;
            }
            i++;
        }
    }
    free(sca);    //ランキングが入ればソート用配列は不要になるので解放する。
    return 0;
}

//=============================================================================
/*科目成績計算結果リストを作る関数。*/
struct SubjectCalc *SubjectAverageList(struct ListNode **headps) {
    struct SubjectCalc *sjcp_head_p = NULL, *sjcp, 
                       *pre_sjcp = NULL, *next_sjcp;
    struct ListNode *kamokuhyo_head_p, *p;
    
    kamokuhyo_head_p = headps[1];
    
    p = kamokuhyo_head_p;
    while (p != NULL) {
        sjcp = malloc(sizeof(struct SubjectCalc));
        if (sjcp == NULL) {
            puts("科目平均値リストが作れませんでした。");
            sjcp = sjcp_head_p;
            while (sjcp != NULL) {
                next_sjcp = sjcp->next;
                free(sjcp);
                sjcp = next_sjcp;
            }
            sjcp_head_p = NULL;
            return sjcp_head_p;
        }
        sjcp->next = NULL;
        sjcp->to_sj_node = p;
        if (sjcp_head_p == NULL) {
            sjcp_head_p = sjcp;
        } else {
            pre_sjcp->next = sjcp;
        }
        pre_sjcp = sjcp;
        p = p->next;
    }
        //各科目の評価合計と平均を、当リストのノードに格納する関数を呼ぶ。
    SALValue(headps, sjcp_head_p);
    
    return sjcp_head_p;
}

//=============================================================================
/*科目成績計算結果リストの各ノードに値を格納する関数。*/
void SALValue(struct ListNode **headps, struct SubjectCalc *sjcp_head_p) {
    struct SubjectCalc *sjcp;
    struct ListNode *seitomeibo_head_p, *seisekihyo_head_p, *p, *q;
    int gokei = 0, kosu = 0;
    double heikin;
    
    seitomeibo_head_p = headps[0];
    seisekihyo_head_p = headps[2];
    
    sjcp = sjcp_head_p;
    while (sjcp != NULL) {
        p = seitomeibo_head_p;
        while (p != NULL) {
            q = seisekihyo_head_p;
            while (q != NULL) {
                if ((strcmp(sjcp->to_sj_node->member[0], q->member[0]) == 0)
                    && (strcmp(p->member[0], q->member[1]) == 0)) {
                    gokei += (int)strtol(q->member[2], NULL, 10);
                    kosu++;
                }
                q = q->next;
            }
            p = p->next;
        }
        if (gokei * kosu != 0) {
            heikin = RoundingSecond((double)gokei / kosu);
        } else {
            heikin = 0.0;
        }
        sjcp->total = gokei;
        sjcp->cnt = kosu;
        sjcp->ave = heikin;
        gokei = 0; kosu = 0;
        
        sjcp = sjcp->next;
    }
}

//=============================================================================
/*成績一覧表に科目合計と科目平均を記入する関数。*/
void SubjTotalAve(const int year, const int gokei, const int kosu, 
                  struct SubjectCalc *sjcp_head_p) {
    int i;
    struct SubjectCalc *sjcp;
    const char *gakunen[GRADE_NUMBER] = {GRADE_ARRAY};
    double heikin;
    
    for (i = 0; i < 2; i++) {
        if (i == 0) {
            printf(",科目合計");
        } else {
            printf(",科目平均");
        }
        sjcp = sjcp_head_p;
        while (sjcp != NULL) {
            if (strcmp(sjcp->to_sj_node->member[2], gakunen[year]) == 0) {
                if (i == 0) {
                    printf(",%d", sjcp->total);
                } else {
                    printf(",%.1f", sjcp->ave);
                }
            }
            sjcp = sjcp->next;
        }
        if (i == 0) {    //評価総計と全評価平均を求めて表示する。
            printf(",%d\n", gokei);
        } else {
            if (gokei * kosu != 0) {
                heikin = RoundingSecond((double)gokei / kosu);
            } else {
                heikin = 0.0;
            }
            printf(",%.1f\n", heikin);
        }
    }
}

//=============================================================================
/*生徒成績計算結果リストと科目成績計算結果リストを解放する関数。*/
void GTFree(struct StudentCalc *scp_head_p, struct SubjectCalc *sjcp_head_p) {
    struct StudentCalc *scp, *scp_next;
    struct SubjectCalc *sjcp, *sjcp_next;
    
    scp = scp_head_p;
    while (scp != NULL) {
        scp_next = scp->next;
        free(scp);
        scp = scp_next;
    }
    sjcp = sjcp_head_p;
    while (sjcp != NULL) {
        sjcp_next = sjcp->next;
        free(sjcp);
        sjcp = sjcp_next;
    }
}

//=============================================================================
/*処理をユーザーに選択させる関数。*/
int OperationSelect(void) {
    const char *str_select[] = {"0","1","2","3","4","5","6","7","8","9","a"};
    char str[3], select_lf_add[3];
    int i, select_num, selected = 0, flg = 0;
    
    select_num = sizeof(str_select) / sizeof(char *);
    
    puts("成績処理\n"
         "0:全csvファイル確認表示\n"
         "1:評定平均表表示(番号順)\n"
         "2:評定平均表表示(成績降順)\n"
         "3:評定平均表表示(番号順+成績降順)\n"
         "4:成績一覧表表示(第1学年)\n"
         "5:成績一覧表表示(第2学年)\n"
         "6:成績一覧表表示(第3学年)\n"
         "7:成績一覧表表示(第4学年)\n"
         "8:成績一覧表表示(全学年)\n"
         "9:終了\n"
         "a:csvファイルを読み込み直す");
    
    do {
        printf("番号または記号を入力してからEnterを押してください。\n"
               "入力:");
        fgets(str, sizeof(str), stdin);
        for (i = 0; i < select_num; i++) {
            sprintf(select_lf_add, "%s\n", str_select[i]);
            if (strcmp(str, select_lf_add) == 0) {
                selected = i;
                flg = 1;
                break;
            }
        }
        if (flg == 1) {
            break;
        } else {
            puts("入力値が不正です。");
            if (strchr(str, '\n') == NULL) {  //str中に\nがなければ……
                while(getchar() != '\n') {};    //3文字以上入力され、バッファに
            }                                   //余計な文字が残った状態なので
        }                                       //それらをgetcharでクリアする。
    } while (1);
     
    return selected;
}

//=============================================================================
int main(void) {
    const char *file_name[] = {
        "seitomeibo.csv", "kamokuhyo.csv", "seisekihyo.csv"
    };
    int file_num, i, is_gt, selected;
    struct ListNode **headps;
    struct Hyotei *hyotei_head_p, **sa = NULL;
    
    setvbuf(stdout, NULL, _IONBF, 0);    //バッファをオフにする。
    
    file_num = sizeof(file_name) / sizeof(char *);    //ファイル数を求める。
        //csvファイルから複数の構造体リストを作る関数を呼ぶ。
    headps = Loop_smList(file_name, file_num);
    if (headps == NULL) {exit(1);}
    
    do {
        selected = OperationSelect();
        switch (selected) {
        case 0:    //csvファイルのリストを順に表示する関数を呼ぶ。
            for (i = 0; i < file_num; i++) {
                ListDisplay(headps[i]);
            }
            printf("\n");
            continue;
        case 1:
        case 2:    //評定平均表とその降順表を作る関数を呼び、
        case 3:    //さらに評定平均リストを表示・解放する関数を呼ぶ。
            hyotei_head_p = GradeAverage(headps);
            if (hyotei_head_p != NULL) {
                sa = GASort(hyotei_head_p);
            }
            if ((sa != NULL) && (hyotei_head_p != NULL)) {
                printf("\n");
                GADisplay(sa, hyotei_head_p, selected);
                printf("\n");
                GAFree(sa, hyotei_head_p);
            }
            continue;
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:    //一覧表を作る関数を呼ぶ。
            is_gt = GradeTable(headps, selected);
            if (is_gt != 0) {
                puts("一覧表が作れませんでした。");
                break;
            }
            printf("\n");
            continue;
        case 9:
            puts("終了します。");
            break;
        case 10:   //csvファイルを読み込み直して新しいリストを作る。
            for (i = 0; i < file_num; i++) {  //既存のcsvリストの解放。
                ListFree(headps[i]);
            }
            headps = Loop_smList(file_name, file_num);
            if (headps == NULL) {exit(1);}
            puts("\nファイルを読み込み直しました。\n");
        }
    } while (selected != 9);
        //以下は終了処理。csvリストとリスト先頭ポインタ配列を解放。
    for (i = 0; i < file_num; i++) {
        ListFree(headps[i]);
    }
    free(headps);
    return 0;
}