質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.80%

ファイルを読み込んで計算

受付中

回答 4

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 774

winglass

score 11

前提・実現したいこと

C言語に関する質問です。以下にコードを記載しました。
以下のプログラムを起動してファイルに書き込まれた各教科の点数から最大、最小、平均、標準偏差を計算したいです。
また、在籍番号や成績の順番で照準に並べ替えることもしたいと思っています。
ファイルに書き込まれたデータを扱う手段があまりわかりません。
調べたところ、事前に人数や点数が定まっている場合のやり方は見つけられたのですが。。。
アドバイスいただきたいです。

該当のソースコード> 

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

#define NAME_LEN 64
#define NUMBER 10

typedef struct{
    char num[10];           /*在籍番号*/
    char name[NAME_LEN];    /*名前*/
    double math;            /*数学*/
    double phy;             /*物理*/
    double eng;             /*英語*/
} Student;

/*データを入力してファイルに書き込み*/
void data_input(Student a[]){
    FILE *fp;
    int i;
    if ((fp=fopen("score.dat","w"))==NULL)    /*オープン*/
        printf("\aファイルをオープンできません。\n");
    else{
        for(i=0;;i++){
            int flag;
            printf("%d人目のデータを入力しますか(YES-1/NO-0):",i+1);
            scanf("%d",&flag);
            if(flag==0)
                break;
            printf("Num:");scanf("%s",a[i].num);
            printf("Name:");scanf("%s",a[i].name);
            printf("Math:");scanf("%lf",&a[i].math);
            printf("Phy:");scanf("%lf",&a[i].phy);
            printf("Eng:");scanf("%lf",&a[i].eng);
            fprintf(fp,"%-10s  %-10s %5.1f %5.1f %5.1f \n",a[i].num,a[i].name,a[i].math,a[i].phy,a[i].eng);
        }
        fclose(fp);
    }
}

float gpa(int average){       
    if(average<=60)
        return 0;
    if(average>=61 && average<=70)
        return 1.0;
    if(average>=71 && average<=80) 
        return 2.0;
    if(average>=81 && average<=90) 
        return 3.0;
    if(average>=91 && average<=100)
        return 4.0;
    return 0;
}

// 数学の合計の計算
double sum_math(Student a[], int person){
    int i;                
    double total = 0.0;
    for(i=0; i<person; i++){
        total += a[i].math; 
    }
    return total;       
}
// 物理の合計の計算
double sum_phy(Student a[], int person){
    int i;               
    double total = 0.0;
    for(i=0; i<person; i++){
        total += a[i].phy;
    }
    return total;     
}
// 英語の合計の計算
double sum_eng(Student a[], int person){
    int i;             
    double total = 0.0;
    for(i=0; i<person; i++){
        total += a[i].eng; 
    }
    return total;           
}

// 数学の平均の計算
double ave_math(Student a[], int person){
    double total = sum_math(a, person); 
    return total/person;             
}
// 物理の平均の計算
double ave_phy(Student a[], int person){
    double total = sum_phy(a, person);
    return total/person;  
}
// 英語の平均の計算
double ave_eng(Student a[], int person){
    double total = sum_eng(a, person);
    return total/person;         
}

// 数学の分散の計算
double var_math(Student a[], int person) {
    int i;
    double A = ave_math(a, person);   
    double v = 0.0;     
    for (i=0; i<person; i++)
        v += (a[i].math - A) * (a[i].math - A);
    return v/person;                  
}

// 物理の分散の計算
double var_phy(Student a[], int person) {
    int i;
    double A = ave_phy(a, person);  
    double v = 0.0;               
    for (i=0; i<person; i++)
        v += (a[i].phy - A) * (a[i].phy - A);
    return v/person;                  
}
// 英語の分散の計算
double var_eng(Student a[], int person) {
    int i;
    double A = ave_eng(a, person);  
    double v = 0.0;              
    // 分散を計算
    for (i=0; i<person; i++)
        v += (a[i].eng - A) * (a[i].eng - A);
    return v/person;                  
}

// 数学の標準偏差の計算
double std_math(Student a[], int person) {
    return sqrt(var_math(a, person));           
}

// 標準偏差の計算
double std_phy(Student a[], int person) {
    return sqrt(var_phy(a, person));          
}
// 標準偏差の計算
double std_eng(Student a[], int person) {
    return sqrt(var_eng(a, person));            
}

/*ファイルを読み込んでデータを出力*/
void data_output(Student a[]){
    FILE *fp;
    int i;
    int person = 0;
    int average = 0;
    int total = 0;
    int math_max,math_min,phy_max,phy_min,eng_max,eng_min;

    if ((fp=fopen("score.dat","r"))==NULL)    /*オープン*/
        printf("\aファイルをオープンできません。\n");
    else{
        i=0;
        while(fscanf(fp,"%s%s%lf%lf%lf",a[i].num,a[i].name,&a[i].math,&a[i].phy,&a[i].eng)==5){
            printf("%-10s %-10s %5.1f %5.1f %5.1f \n",a[i].num,a[i].name,a[i].math,a[i].phy,a[i].eng);
            i++;
            person++;
        }
        math_max = math_min = a[0].math;
        phy_max = phy_min = a[0].phy;
        eng_max = eng_min = a[0].eng;
        for(i=1;i<person;++i){
            if (math_max < a[i].math)
                math_max = a[i].math;
            if (math_min > a[i].math)
                math_min = a[i].math;
            if (phy_max < a[i].phy)
                phy_max = a[i].phy;
            if (phy_min > a[i].phy)
                phy_min = a[i].phy;
            if (eng_max < a[i].eng)
                eng_max = a[i].eng;
            if (eng_min > a[i].eng)
                eng_min = a[i].eng;
        }
        /* 最高点と最低点の表示 */
        printf("最高点 数学:%d 物理:%d 英語:%d\n", math_max,phy_max,eng_max);
        printf("最低点 数学:%d 物理:%d 英語:%d\n", math_min,phy_min,eng_min);
        for(i=0;i<person;i++){
            total=(a[i].math+a[i].phy+a[i].eng);/*平均を計算*/
            average=(a[i].math+a[i].phy+a[i].eng)/3;  /*平均を計算*/
            printf("%-10s %-10s 合計:%3.1d 平均:%5.1f → GPA:%5.1f\n",a[i].num,a[i].name,total,average,gpa(average));
        }

        printf("math 合計:%5.1f 平均:%5.1f 分散:%5.1f 標準偏差:%f\n",sum_math(a,person),ave_math(a,person),var_math(a,person),std_math(a,person));
        printf("phy 合計:%5.1f 平均:%5.1f 分散:%5.1f 標準偏差:%f\n",sum_phy(a,person),ave_phy(a,person),var_phy(a,person),std_phy(a,person));
        printf("eng 合計:%5.1f 平均:%5.1f 分散:%5.1f 標準偏差:%f\n",sum_eng(a,person),ave_eng(a,person),var_eng(a,person),std_eng(a,person));
        fclose(fp);
    }
}

void swap_Student(Student *x,Student *y){
    Student temp=*x;
    *x=*y;
    *y=temp;
}

/*昇順*/
void sort_by_num(Student a[], int n){
    int i,j;
    for(i=0;i<n-1;i++){
        for(j=n-1;j>1;j--){
            if (a[j-1].num>a[j].num)
                swap_Student(&a[j-1],&a[j]);
        }
    }
}
/*降順*/
void sort_by_num_2(Student a[], int n){
    int i,j;
    for(i=0;i<n-1;i++){
        for(j=n-1;j>1;j--){
            if (strcmp(a[j-1].num,a[j].num)<0)
                swap_Student(&a[j-1],&a[j]);
        }
    }
}

int main(void){
    Student std[NUMBER];
    int flag;
    do{
        printf("ファイル書き込み-1 / ファイル読み込み-2 / 終了-0 :");
        scanf("%d",&flag);
        if(flag==1)
            data_input(std);
        else if(flag==2)
            data_output(std);
    }while(flag != 0);
    return 0;
}

やってみたこと

色々と書き換えてここまできました。
各教科の平均は関数を用いて記述できましたが、各個人の平均を関数で表す方法が思い浮かばなかったため、無理やり?計算しました。また、学籍番号を用いたソートのように、各個人の平均やGPAを用いたソートを行いたいのですが、構造体を使っていないため?かstrcmp(a[j-1].num,a[j].num)<0の部分の書き方がわかりません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • pepperleaf

    2019/06/30 10:29

    それなりに動いていると思いますが、何が問題なのでしょうか?
    読込んだデータは、std[]に入っているので、それを参照すれば、計算もできると思います。
    また、コードにインデントが無いので、読みずらいです。

    キャンセル

  • winglass

    2019/06/30 13:55

    インデントを加えました。
    iが変動するため、それを参照する書き方がわかりません。また、関数を扱うとエラーが生じてしまいます。

    キャンセル

  • episteme

    2019/06/30 15:25 編集

    > ファイルに書き込まれたデータを扱う手段があまりわかりません。

    あまりわからん とは如何なる意味か? 何がわからん? キーボードから入力するのと大差ないが。

    キャンセル

回答 4

+2

最大、最小、平均、標準偏差の計算するのに、全データを配列に保持する必要ないですよ?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/30 16:45

    …まあそうなんですけど、理由まで書かないと我々文系プログラマにはすぐには伝わらないと思いますよ? 具体的に言えば、標準偏差なら各教科の、点数の平方の平均から平均点の平方を減じた後、正の平方根をとることで標準偏差を算出できる、というのは、確かに現行の指導要領では高校数学で教えますが、習ってない世代にはすぐには伝わらないと思いますよ?

    キャンセル

  • 2019/06/30 17:40

    んー...そーか わからんか...
    # 統計に関わる数学は文系には必須ちゃうん? (むしろ理系以上に)

    キャンセル

  • 2019/06/30 17:51

    わからんとすると、ここでの問題は「データ数が未知のとき、可変長の集合を実装できるか」なのよね。
    ファイルからの入力とかあんま関係ない。ってこと、質問者は理解してるかしら?
    # 実際この問題は(ビギナには)かなり手強い。メモリ管理を避けられないから。

    キャンセル

  • 2019/07/01 01:14

    回答ありがとうございます。
    平均や標準偏差の計算は行えました。
    学籍番号の構造体の.numを扱い、ソートが行うことができました。しかし、平均やGPAではソートを行う手段がわかりません。知識が乏しく申し訳ありません。

    キャンセル

+1

関数の返り値として、正常に読み込めたデータ数、もしくは書き込めたデータ数を返し、それを全体の人数として処理してはいかがですか?

追記1:元々意図していたことが伝わらなかったようなのでコード例を載せます。

int data_input(Student a[]){
    ...
    if ((fp=fopen("score.dat","w"))==NULL){
        printf("\aファイルをオープンできません。\n");
        return 0;
    }
    else{
        ...
        fclose(fp);
        return i;
    }
}

void data_output(Student a[], int person){
    FILE *fp;
    int i;
    int average = 0;
    ...
}

int main(void){
    ...
    int nStudents = 0;
    do{
        ...
        if(flag==1)
            nStudents = data_input(std);
        else if(flag==2)
            data_output(std, nStudents);
    }while(flag != 0);
    return 0;
}

追記2:strcmp関数の仕様を調べ、それと類似するインターフェースを持つ関数を定義して差し替えれば解決します。

// 使用側
if (avecmp(&a[j-1], &a[j])<0)
    swap_Student(&a[j-1],&a[j]);

// 関数定義
int avecmp(const void* p, const void* q){
    const Student *a = p, *b = q;

    // 平均の大小は、科目数が同じ場合合計の大小と一致する
    double diff = (a->math + a->phy + a->eng) - (b->math + b->phy + b->eng);
    if(diff < 0) return -1;
    if(diff > 0) return 1;
    return 0;
}


要するに、関数の内部でgpaなり平均を計算して、それを比較すればいいのです。

事後課題:qsortという関数を調べてみてください。謎仕様のインターフェースの意図が分かると思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/01 01:23 編集

    ソースコードを書き換えてみました。このようなことでよろしいのでしょうか。。

    追記
    そのように記述すればいいのですね!返り値を勘違いしておりました。

    int data_input(Student a[])の return で
    エラー文、Void function 'data_input' should not return a value がでで動いてくれません。

    キャンセル

  • 2019/07/02 20:28

    data_inputの前のvoidはintに書き換えましたか?

    キャンセル

+1

学籍番号を用いたソートのように、各個人の平均やGPAを用いたソートを行いたいのですが、構造体を使っていないため?かstrcmp(a[j-1].num,a[j].num)<0の部分の書き方がわかりません。

a[j-1]の平均 と a[j]の平均 とを比べればいいんだから、

int mean(Student s) {
  return sのmath,phy,engを加えて3で割った値
}

を用意して、if ( mean(a[j-1]) < mean(a[j]) ) ... ってやればよくね?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

次のプログラムで分からないところは自分でよく調べて、
それでも分からないところがあるときは、質問してください。

#include <stdio.h>   // printf, scanf, puts
#include <stdlib.h>  // malloc, free
#include <string.h>  // strcmp
#include <math.h>    // sqrt

#define NAME_LEN 64
#define NUMBER 10

typedef struct {
    int num;                /* 在籍番号 */
    char name[NAME_LEN];    /* 名前 */
    double math;            /* 数学 */
    double phy;             /* 物理 */
    double eng;             /* 英語 */
} Student;

void data_input(Student a[])
{
    FILE *fp = fopen("score.dat","w");
    if (fp == NULL) { printf("\aファイルをオープンできません。\n"); return ; }
    for (int i = 0; i < NUMBER; i++) {
        int flag;
        printf("%d人目のデータを入力しますか(YES-1/NO-0):", i + 1);
        scanf("%d", &flag);
        if (flag == 0) break;
        printf("Num:");  scanf("%d", &a[i].num);
        printf("Name:"); scanf("%s", a[i].name);
        printf("Math:"); scanf("%lf", &a[i].math);
        printf("Phy:");  scanf("%lf", &a[i].phy);
        printf("Eng:");  scanf("%lf", &a[i].eng);
        fprintf(fp, "%-10d  %-10s %5.1f %5.1f %5.1f\n",
                a[i].num, a[i].name, a[i].math, a[i].phy, a[i].eng);
    }
    fclose(fp);
}

double gpa(double average)
{
    return (average > 60 && average <= 100) ? (int)(average - 51) / 10 : 0;
}

void print_index(Student a[], int person, Student *index[])
{
    for (int i = 0; i < person; i++) {
        Student *p = index[i];
        printf("%-10d %-10s %5.1f %5.1f %5.1f \n",
                p->num, p->name, p->math, p->phy, p->eng);
    }
}

void sort_by_num(Student *index[], int person)
{
    for (int i = 0; i < person - 1; i++) {
        Student *p = index[i];
        for (int j = 1; j < person; j++) {
            Student *q = index[j];
            if (p->num > q->num) {
                index[i] = q;
                index[j] = p;
            }
        }
    }
}

void sort_by_name(Student *index[], int person)
{
    for (int i = 0; i < person - 1; i++) {
        Student *p = index[i];
        for (int j = 1; j < person; j++) {
            Student *q = index[j];
            if (strcmp(p->name, q->name) > 0) {
                index[i] = q;
                index[j] = p;
            }
        }
    }
}

void data_output(Student a[])
{
    FILE *fp = fopen("score.dat","r");
    if (fp == NULL) { printf("\aファイルをオープンできません。\n"); return; }
    int i;
    for (i = 0; fscanf(fp, "%d%s%lf%lf%lf", &a[i].num, a[i].name,
                &a[i].math, &a[i].phy, &a[i].eng) == 5; i++) {
        printf("%-10d %-10s %5.1f %5.1f %5.1f \n", a[i].num, a[i].name,
                a[i].math, a[i].phy, a[i].eng);
    }
    fclose(fp);

    int person = i;
    double math_max, math_min, phy_max, phy_min, eng_max, eng_min;
    math_max = math_min = a[0].math;
    phy_max = phy_min = a[0].phy;
    eng_max = eng_min = a[0].eng;
    for (i = 1; i < person; i++) {
        if (math_max < a[i].math) math_max = a[i].math;
        if (math_min > a[i].math) math_min = a[i].math;
        if (phy_max < a[i].phy) phy_max = a[i].phy;
        if (phy_min > a[i].phy) phy_min = a[i].phy;
        if (eng_max < a[i].eng) eng_max = a[i].eng;
        if (eng_min > a[i].eng) eng_min = a[i].eng;
    }
    printf("最高点 数学:%5.1f 物理:%5.1f 英語:%5.1f\n",
            math_max, phy_max, eng_max);
    printf("最低点 数学:%5.1f 物理:%5.1f 英語:%5.1f\n",
            math_min, phy_min, eng_min);

    double math_sum = 0, phy_sum = 0, eng_sum = 0;
    double math_sum2 = 0, phy_sum2 = 0, eng_sum2 = 0;
    for (i = 0; i < person; i++) {
        double total = a[i].math + a[i].phy + a[i].eng;  /* 合計を計算*/
        double average = total / 3;                      /* 平均を計算*/
        printf("%-10d %-10s 合計:%3.1f 平均:%5.1f → GPA:%5.1f\n",
                a[i].num, a[i].name, total, average, gpa(average));
        math_sum += a[i].math; math_sum2 += a[i].math * a[i].math;
        phy_sum += a[i].phy;   phy_sum2 += a[i].phy * a[i].phy;
        eng_sum += a[i].eng;   eng_sum2 += a[i].eng * a[i].eng;
    }
    double math_ave = math_sum / person;
    double phy_ave = phy_sum / person;
    double eng_ave = eng_sum / person;

    double math_var = (math_sum2 - math_sum * math_ave * 2) / person
        + math_ave * math_ave;
    double phy_var = (phy_sum2 - phy_sum * phy_ave * 2) / person
        + phy_ave * phy_ave;
    double eng_var = (eng_sum2 - eng_sum * eng_ave * 2) / person
        + eng_ave * eng_ave;

    printf("math 合計:%5.1f 平均:%5.1f 分散:%5.1f 標準偏差:%f\n",
            math_sum, math_ave, math_var, sqrt(math_var));
    printf("phy  合計:%5.1f 平均:%5.1f 分散:%5.1f 標準偏差:%f\n",
            phy_sum, phy_ave, phy_var, sqrt(phy_var));
    printf("eng  合計:%5.1f 平均:%5.1f 分散:%5.1f 標準偏差:%f\n",
            eng_sum, eng_ave, eng_var, sqrt(eng_var));

    Student **index = (Student **)malloc(sizeof(Student *) * person);
    if (index == NULL) return;
    for (int i = 0; i < person; i++) index[i] = &a[i];
    sort_by_num(index, person);
    puts("--- sort by num ---");
    print_index(a, person, index);
    sort_by_name(index, person);
    puts("--- sort by name ---");
    print_index(a, person, index);
    free(index);
}

int main(void)
{
    Student std[NUMBER];
    int flag = 0;
    do {
        printf("ファイル書き込み-1 / ファイル読み込み-2 / 終了-0 :");
        scanf("%d", &flag);
        if (flag == 1)
            data_input(std);
        else if (flag == 2)
            data_output(std);
    } while (flag != 0);
    return 0;
}


まだ直したいところがいっぱいあるけど。

追記

各個人の平均やGPAを用いたソートを行いたいのですが、

それなら、Student構造体の中に、平均や GPA を記録するメンバを追加しないとだめでしょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る