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

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

ただいまの
回答率

87.35%

C言語の構造体配列の末尾に新規の配列を追加する方法

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 4,153

score 45

現在、課題でsample.txtを構造体に入れたのちに新規でデータを入れることを実現したいとしているのですがうまく行きません。
今試している方法としては

  1. define MAXCNT 100で配列の最大数を100に指定します。
  2. そして、既存のデータ55件(sample.txt)をまずは構造体に読み込みます。
  3. さらに、追加で既存の配列+1(ここでは56番目などに)新規にデータを入れ込む方法です。
    これがうまくいかないのでうまくいく方法があれば教えてほしいです。

また、他に

  • 読み込んだ名前データの表示が文字化けする(深�)みたいなバグもありますのでよければこちらもついでに教えてほしいです。

C言語できる方どなたかご教授いただきたいです。
よろしくお願いします。

深津 旭弘    29    183    78
深津 英臣    27    180    70
藤井 直伸    25    183    78
関田 誠大    23    177    70
米山 裕太    32    185    85
鈴木 悠二    35    190    80
清水 邦広    30    193    94
高松 卓矢    29    186    82
栗山 雅史    28    190    85
柳田 将洋    24    186    79
山田 脩造    24    193    78
高野 直哉    24    190    77
小川 猛    22    193    90
久原 翼    22    189    79
石川 祐希    21    192    84
鈴木 祐貴    20    201    85
新井 雄大    19    188    86
富松 崇彰    32    192    85
李  博    26    194    83
傳田 亮太    25    191    84
出耒田 敬    25    200    92
山内 晶大    23    204    72
高橋健太郎    22    202    100
大竹 壱青    21    201    100
小野寺太志    21    201    98
古賀太一郎    27    170    70
浅野 博亮    26    178    69
鶴田 大樹    25    177    76
井手 智    25    174    74
冨永こよみ    28    176    67
佐藤 美弥    27    174    61
田中 美咲    25    173    61
宮下 遥    22    177    61
石井 里沙    27    179    65
新鍋 理沙    26    173    64
長岡 望悠    25    179    64
石井 優希    26    180    68
野本 梨佳    25    180    73
内瀬戸真実    25    171    70
堀川 真理    25    183    70
高橋 沙織    24    177    67
鍋谷友理枝    23    176    57
井上愛里沙    22    178    66
田中 瑞稀    21    170    73
古賀紗理那    21    180    66
黒後 愛    19    180    69
荒木絵里香    32    186    78
松本亜弥華    28    188    71
岩坂 名奈    26    187    76
奥村 麻依    26    177    66
島村 春世    25    182    77
大野 果奈    25    180    70
井上 琴絵    27    162    53
佐藤あり紗    27    166    53
小幡 真子    24    164    55
/*
- case2で年齢(age)27を表示するときだけ7になってしまうバグがあります。
- 日本語を読み込んだ際に文字化けするバグがあります。
char型-string型によるバグだと思う
*/

#include <stdio.h>
#define MAXCNT 100// sample.txtの行の最大許容数
// 構造体定義の宣言
// 参考:【B4】おまけ課題 4~10行目
// 関数のプロトタイプ宣言
int is_loaded();
// 参考:【B4】おまけ課題 12~14行目
typedef struct Body
{   char name;
    // char name[10]; もしかしたらこっちの方がいいかも
    int age;
    int height;
    int weight;
}Body;

int main(void)
{
// 変数の宣言
// 参考:【B4】おまけ課題 18~20行目
    int i,j=0,max_num,flag=0;//初期値など
    int tmp_age=0,tmp_height=0,tmp_weight=0;//平均の準備用
    float avg_age,avg_height,avg_weight;//平均
    char new_name;
    int new_age,new_height,new_weight;
    char fname[] = "sample.txt";//読み込むためのファイル名
    Body Data[MAXCNT];
    FILE* fp;

int menu;
while(1){
printf("\n=== 操作メニュー ===\n");
printf("1:ファイル読み込み\n");
printf("2:全データ表示\n");
printf("3:新データ入力\n");
printf("4:ファイル書き出し\n");
printf("1~4以外:終了\n\n");
printf("操作No の入力:");
scanf("%d",&menu); printf("\n");
switch(menu){
case 1:
// ファイルを開き 構造体配列に読み込む
// 参考:【B4】おまけ課題 22~33行目
fp=fopen(fname,"r");
if(fp == NULL){
    printf("ファイルを開けませんでした\n");
    return -1;//異常終了
} else
{
    printf("%s ファイルを開きました\n",fname);
    // fread(fname,sizeof(char),10,fp);
    printf("%s ファイルを読み込みました\n",fname);
    for(i=0;i<MAXCNT;i++) {
        if(fscanf(fp,"%s%d%d%d\n",
                    &Data[i].name,
                    &Data[i].age,
                    &Data[i].height,
                    &Data[i].weight) == EOF) break;
    }

    Data[56].name = '*';
    Data[56].age = 27;
    Data[56].height = 130;
    Data[56].weight = 61;
    // Data[57].name = '*';
    // Data[57].age = 23;
    // Data[57].height = 170;
    // Data[57].weight = 40;
    // printf("%d",Data[56].age);


    fclose(fp);
    printf("ファイルポインタを閉じました\n");
    flag=1;//case 2に映るためのフラグを持たせる
    printf("サイズの確認:%d",i);
}
break;// case1 終了

case 2:
// 構造体配列の内容を1件1行として画面に表示
// データ件数と、それぞれの項目についての全件の平均値を表示
// 参考:【B4】おまけ課題 35~37行目
// if(!is_loaded(flag))//テキスト読み込まれてるか
// {break;}
if (flag!=1){
    printf("\nファイル読み込みを先に実行してください\n");
    break;
}
else
 {
printf("データを表示する\n行数\t名前\t年齢\t身長\t体重\n");
while(1) {
    printf("%d:\t%s\t%d\t%d\t%d\n",
                j+1,
                &Data[j].name,
                Data[j].age,
                Data[j].height,
                Data[j].weight);
    // 年齢、身長、体重の合計
    tmp_age += Data[j].age;
    tmp_height += Data[j].height;
    tmp_weight += Data[j].weight;
    if(j==i) break;//iはデータの読み込み件数(デフォルト55件)
    j++;
}
printf("\n全データを出力しました\n");
// 年齢、体重、身長の平均
avg_age = tmp_age/MAXCNT;
avg_height = tmp_height/MAXCNT;
avg_weight = tmp_weight/MAXCNT;
printf("\n合計件数:%d\n",MAXCNT);
printf("平均を表示\n");
printf("平均年齢:%f\t平均身長:%f\t平均体重:%f\n",avg_age,avg_height,avg_weight);
break;
}
case 3:
// 構造体配列に新データを追加する
// 参考:【B4】おまけ課題 39~48行目
if (flag!=1){
    printf("\nファイル読み込みを先に実行してください\n");
break;
}
else{
    int new_row = i+1;
    printf("新しく追加するデータを入力してください\n");
    printf("それぞれ追加するデータを|名前,年齢,身長,体重|の形式で入力してください\n");
    // scanf("%c,%d,%d,%d",&new_name,&new_age,&new_height,&new_weight);
    scanf("%c,%d,%d,%d",&Data[56].name,&Data[56].age,&Data[56].height,&Data[56].weight);
    // scanf("\n年齢:%d",&Data[56].age);
    // scanf("身長:%d",&Data[56].height);
    // scanf("体重:%d",&Data[56].weight);
    // Body Data[new_row] = {new_name,new_age,new_height,new_weight};//直近の行数+1配列目にデータを入れる
}
case 4:
// 構造体配列の内容をファイルに書き出す
// 参考:【B4】おまけ課題 50~59行目
break;
default:
// プログラムを終了
return 0;
}
}
return 0;
}
// 関 数 の 定義
// FIXME
int is_loaded(int flag)
{
    if (flag!=1){
    printf("\nファイル読み込みを先に実行してください\n");
    return -1;
}else{
    // ファイル読み込みチェック完了
    return 0;
}
}
// 参考:【B4】おまけ課題 64行目以降
//【B1】ステップ1 27~44行目 、【B2】ステップ2 52~63行目
コード
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • meg_

    2019/08/04 13:54

    「3.3さらに、追加で既存の配列+1(ここでは56番目などに)新規にデータを入れ込む方法です。
    これがうまくいかないのでうまくいく方法があれば教えてほしいです。」とはどういうことでしょうか?

    どんなコードを試してどんな結果(エラーなど)になったのでしょうか?

    キャンセル

  • humuhimi

    2019/08/04 16:48

    Body構造体のDataを定義してから
    ```c
    printf("%s ファイルを開きました\n",fname);
    // fread(fname,sizeof(char),10,fp);
    printf("%s ファイルを読み込みました\n",fname);
    for(i=0;i<MAXCNT;i++) {
    if(fscanf(fp,"%s%d%d%d\n",
    &Data[i].name,
    &Data[i].age,
    &Data[i].height,
    &Data[i].weight) == EOF) break;
    ```
    でsample.txtを読み込んだのですが、下記のcase3 にて新しいデータを追加したいです。

    scanf("%c,%d,%d,%d",&Data[56].name,&Data[56].age,&Data[56].height,&Data[56].weight);

    キャンセル

  • humuhimi

    2019/08/04 16:48

    読みにくっ!すみません。。。

    キャンセル

回答 5

checkベストアンサー

+2

全部、見た訳ではありませんが、

typedef struct Body
{   char name;

となっていて、

if(fscanf(fp,"%s%d%d%d\n",
        &Data[i].name,

はダメです。name は一文字分しか、無いのに複数の文字が読み込まれています。

 // char name[10]; もしかしたらこっちの方がいいかも
ではなく、そうする必要があります。 ただ、10はサイズが小さいので、名前に十分なサイズの確保を。(お試しだっら、100くらい指定するが、それと末尾の '\0' 分を忘れないように)

質問文には、56番目とありますが、

 Data[56].name = '*';

C言語は、番号は0から始まるので、 55じゃないでしょうか?
上記にも書きましたが、 nameは、1文字分なので、文字列にはなりません。

まず、この辺の確認から、お願いします。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/04 16:56

    まさに配列番号は55でした。不注意でした
    ちゃんと55番にしたら構造体内に入ったので出口が見えてきました!ありがとうございます 助かりました!

    キャンセル

  • 2019/08/04 17:16

    全ておっしゃる通りでした。
    char nameの配列数を増やしてインデックスを55に直したら全部うまく行きました。
    本当に助かりました。ベストアンサーありがとうございます

    キャンセル

  • 2019/08/06 12:46

    (横から失礼します...)
    あ、そっちか!
    なるほど。

    キャンセル

+2

mainの中でなんもかんもやらせてる時点で、僕なら窓から投げ捨てる。
少なくともメニュー項目ごとに関数に分割し適切に呼び分けろ。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/04 16:50

    そうですね。期限ギリギリなんでスパゲティーコードでいいやという甘い考えでやってました。
    リファクタリングします!
    ありがとうございます

    キャンセル

0

C言語ではあらかじめ確保した配列を拡張させるような事はできません。
配列で宣言せずに、mallocで領域確保するようにすれば、reallocってので確保領域の再割り当てができますね

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/04 16:56

    malloc,reallocは初めて聞いたので調べてみます!
    ありがとうございます!

    キャンセル

0

たぶん、オプショナルな問題は表示するときの問題だと思います。
WindowsならWindowsのOS側で...っていう方法もあるようですが、

C言語側では日本語のような文字を使う場合は char ではなくて wchar_t を使います。

二バイトで一つ分と見なすようです。

もし

char str[] = "あいうえお";

とあったら、

wchar_t str[] = L"あいうえお";

となる。

wchar_t に関しては調べたら普通に出てきます。
("追記1" を参照)

あとはいわゆる『ダメ文字』って呼ばれる文字を使っているからかも。

[質問に関して]

※ 私自身が思考停止してしまっています...

まず、何がわからないのでしょうか。

今試している方法としては
1define MAXCNT 100で配列の最大数を100に指定します。
2そして、既存のデータ55件(sample.txt)をまずは構造体に読み込みます。
3さらに、追加で既存の配列+1(ここでは56番目などに)新規にデータを入れ込む方法です。
これがうまくいかないのでうまくいく方法があれば教えてほしいです。

とありますが、これ、全部じゃないですよね?

3がわからないのでしょうか?

もし3がわからないにしても、何がわからないのかが不明瞭です。

追加する方法自体がわからないのか、『ストックされている範囲がわからない』のか。

後者は、56番目まで使われているのなら、次の場所である 57番目にセットしたい...
みたいな感じだけど、その56番目まで使われている っていうことをどうやって調べるのか。

ってことでしょうか?

それなら、

方法1:
単純にforで『空の場所を探す』。

例:
056番目まで使われているなら、056までは何かしらのデータが入っている.
しかし57番目に差し掛かると、『何も入っていない状態』であるため、
現在位置である57番目を-1した (= 前回の場所 ) 要素番号までが使われているとみなす.
方法2:
変数を一つ用意して、その変数に『何番目まで使われているか』をストックしておく.

例:
何番目まで使われているかを示す変数を posとしておく.
posの初期値は-1 ( 何も入っていないため ).
0番目にセットしたら pos をインクリメント ( pos++ ) して、0にしておく.
(0番目までは使っている)
1番目に追加したら posをまたインクリメントして pos = 1 としておく.
...
(別にposを『位置』ではなく、『個数』としてもいい。その場合は初期値は0としておく。
使う場合は pos-1 のようにするが)

とかが思いつきますね。


[追記1]

どこの記事だったか、忘れましたが、wchar_tはもしかすると他のOSかコンパイラによっては使えない可能性があります。(うろ覚え)

一応の方法として上記のやつは残しておきます。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/04 12:31

    丁寧な回答ありがとうございます。
    とりあえず今は構造体配列に直接新規のデータを追加したいです。
    55件名まではデータを読み込んでいる。
    scanfで新たに名前、年齢、身長、体重を追加してそれをファイルに書き込むのではなく、main.cの構造体配列に追加するということをしたいです!

    今外出中なのであとでしっかり見ておきます!
    ありがとうございます

    キャンセル

  • 2019/08/04 17:15

    wchar_t型は初めて聞きました!調べたところうまく行きそうです。
    今回は結局は僕が指定している配列の番号が間違っていたのが原因でした。
    他のやり方として
    string.hのstrcpy(Data[55].name,"名前");とする方法でもできました。

    下の方法1と方法2についてですが、僕は方法2を使ってます。
    別の変数を用意するというより、データを読み込む際行数とループ用の変数iが対応しており、EOFでループを抜けるようにしました!

    長文の回答ありがとうございます。
    知識が広がり、助かりました

    キャンセル

  • 2019/08/06 12:43 編集

    >> 今回は結局は僕が指定している配列の番号が間違っていたのが原因でした。
    あ、そっちかぁ...

    キャンセル

0

scanf("%c,%d,%d,%d",&Data[56].name,&Data[56].age,&Data[56].height,&Data[56].weight);

の「"%c,%d,%d,%d」を「"%c %d %d %d」(,でなく半角空白1文字)に変えてみてください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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