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

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

新規登録して質問してみよう
ただいま回答率
85.47%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Q&A

解決済

3回答

5287閲覧

構造体・文字列をファイルに入出力したい

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

0グッド

0クリップ

投稿2020/06/29 05:47

編集2020/06/29 07:49

前提・実現したいこと

C言語を始めて3か月の初心者です。
住所録プログラムを作成しています。
以下のソースコードで、Register関数を用いてファイルmemo.txtに入力した文字列配列をSearch関数で検索(出力)すると正しく表示されません。次のように出力するためにはどう修正したら良いのでしょうか。

宜しくお願い致します。

memo.txtの内容:

A0000000placeA0000000000 B1111111placeB1111111111 C2222222placeC2222222222

以下のように表示したい:

検索したいIDは?(1~100) 1 IDの認証に成功しました。 名前:A 郵便番号:0000000 住所:placeA 電話番号:00000000000 検索したいIDは?(1~100) 2 IDの認証に成功しました。 名前:B 郵便番号:1111111 住所:placeB 電話番号:1111111111 検索したいIDは?(1~100) 3 IDの認証に成功しました。 名前:C 郵便番号:2222222 住所:placeC 電話番号:2222222222

宜しくお願い致します。

発生している問題

次のように表示されてしまう。

検索したいIDは?(1~100) 1 IDの認証に成功しました。 ID:1 名前:A0000000placeA0000000000 郵便番号:B1111111placeB1111111111 住所:C2222222placeC2222222222 電話番号:

該当のソースコード

C

1#include<stdio.h> 2#include<stdlib.h> 3#include<string.h> 4#include<stdbool.h> 5#define SIZE 100 6#define LENGTH 512 7 8//名前・郵便番号・住所・電話番号を構造体にまとめる 9typedef struct{ 10 char name[LENGTH]; 11 char zipcode[LENGTH]; 12 char address[LENGTH]; 13 char tel[LENGTH]; 14}Info; 15 16typedef Info InfoList[SIZE + 1]; 17 18//IDの入力を関数化する 19int InputID(){ 20 int ID; 21 printf("IDは?(1~%d)\n",SIZE); 22 while(1){ 23 if(scanf("%d", &ID) != 1) exit(1); 24 if(1 <= ID && ID <= SIZE){ 25 printf("IDの認証に成功しました。\n"); 26 return ID; 27 } 28 printf("IDが正しくありません。再入力して下さい。\n"); 29 } 30} 31 32//名前・郵便番号・住所・電話番号の入力 33void InputInfo(Info *info){ 34 printf("名前を入力して下さい。\n"); 35 if(scanf("%s", info->name) != 1) exit(1); 36 printf("郵便番号を入力して下さい。(ハイフンなし)\n"); 37 if(scanf("%s", info->zipcode) != 1) exit(1); 38 printf("住所を入力してください。\n"); 39 if(scanf("%s", info->address) != 1) exit(1); 40 printf("電話番号を入力してください。(ハイフンなし)\n"); 41 if(scanf("%s", info->tel) != 1) exit(1); 42} 43 44//ID・名前・郵便番号・住所・電話番号の出力 45void OutputInfo(InfoList list, int ID, FILE *fp){ 46 printf("ID:%d\n", ID); 47 fscanf(fp, "%s", list[ID].name); 48 fscanf(fp, "%s", list[ID].zipcode); 49 fscanf(fp, "%s", list[ID].address); 50 fscanf(fp, "%s", list[ID].tel); 51 printf("名前:%s\n",list[ID].name); 52 printf("郵便番号:%s\n",list[ID].zipcode); 53 printf("住所:%s\n",list[ID].address); 54 printf("電話番号:%s\n",list[ID].tel); 55} 56 57//名前・郵便番号・住所・電話番号の登録 58void Register(InfoList list, FILE *fp){ 59 fp = fopen("memo.txt", "a"); 60 printf("登録したい"); 61 int ID = InputID(); 62 InputInfo(&list[ID]); 63 fprintf(fp, "%s", list[ID].name); 64 fprintf(fp, "%s", list[ID].zipcode); 65 fprintf(fp, "%s", list[ID].address); 66 fprintf(fp, "%s\n", list[ID].tel); 67 printf("登録に成功しました。\n"); 68} 69 70//個人情報の検索 71void Search(InfoList list, FILE *fp){ 72 fp = fopen("memo.txt", "r"); 73 int select, ID; 74 char *element[] = {"", "ID", "名前", "郵便番号", "住所", "電話番号"}; 75 while(true){ 76 printf("何で検索しますか?\n1.ID 2.名前 3.郵便番号 4.住所 5.電話番号\n"); 77 if(scanf("%d", &select) != 1) exit(1); 78 if(1 <= select && select <= 5) break; 79 printf("不正な番号です。再入力して下さい。\n"); 80 } 81 if(select == 1){ 82 printf("検索したい"); 83 ID = InputID(); 84 OutputInfo(list, ID, fp); 85 return; 86 } 87 printf("検索する%sを入力して下さい。\n", element[select]); 88 char target[512]; 89 if(scanf("%s", target) != 1) exit(1); 90 bool found = false; 91 for(ID=1; ID<=SIZE; ID++){ 92 char *field; 93 switch(select){ 94 case 2: field = list[ID].name; break; 95 case 3: field = list[ID].zipcode; break; 96 case 4: field = list[ID].address; break; 97 case 5: field = list[ID].tel; break; 98 default: break; 99 } 100 if(strcmp(field, target) == 0){ 101 OutputInfo(list, ID, fp); 102 found = true; 103 } 104 } 105 if(!found) printf("そのような%sは存在しません。\n", element[select]); 106} 107 108 109//メインメニュー 110int main(){ 111 FILE *fp; 112 InfoList list; 113 int MenuID = 0; 114 while(MenuID != 5){ 115 printf("---------------住所録プログラム--------------\n"); 116 printf("1:登録\n"); 117 printf("2:検索\n"); 118 printf("3:終了\n"); 119 printf("行う操作の番号を入力して下さい。\n"); 120 if(scanf("%d", &MenuID) != 1) exit(1); 121 system("cls"); 122 switch(MenuID){ 123 case 1: Register(list, fp); break; 124 case 2: Search(list, fp); break; 125 case 3: return 0; 126 default: printf("不正な番号です。再入力して下さい。\n"); break; 127 } 128 } 129 fclose(fp); 130} 131

###回答していただきたいこと
・ターミナルに上記のように正しく表示する方法
・何が間違っているから文字列が正しく表示されないのか

補足情報

VSCodeで作成。gccでコンパイラ。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

DreamTheater

2020/06/29 06:28

実質丸投げなんですが、それ以前に出力ファイルにどのように構造体メンバーを格納するのか全く書かれていません。 アウトプットが提示されなければ回答しようがありませんよ。
ohys

2020/06/29 06:31

数値をfscanf,fprintfできたなら、文字列も同じ要領でできると思いますが、「文字列の扱いが難しく」とはどういうことでしょうか 「こうやってみたけど正しく構造体に格納できない」などあると助かるのですが
hope_mucci

2020/06/29 06:40

>以下のソースコードを修正して、(略)プログラムを作成して頂きたいです。 作業依頼、丸投げに類する質問はTeratailで最も嫌われる質問です。 以下のヘルプをよく読み、試してみても分からなかった点を明確にして質問しなおしましょう。 https://teratail.com/help/avoid-asking https://teratail.com/help/question-tips#questionTips1-1 文字列のファイル入出力がわからないなら、まずはその部分に絞った小さなコードを書くところから練習しましょう。
退会済みユーザー

退会済みユーザー

2020/06/29 06:41

質問の方法が悪かった旨、申し訳ありません。 今から修正致します。
dodox86

2020/06/29 07:30

> 文字列のファイル入出力がわからないなら、まずはその部分に絞った小さなコードを書くところから練習しましょう。 hope_mucciさんのこの部分に激しく同意。例示で回答しようかと思っていましたが止めました。
退会済みユーザー

退会済みユーザー

2020/06/29 07:52

皆さんのご指摘を頂き、質問内容を修正致しました。 ご迷惑をおかけして申し訳ありませんでした。 優しいご指摘を有り難うございました。 また何かあれば仰ってください。
dodox86

2020/06/29 08:29

修正いただいた質問内容を見て、叱責ではありません(<当たり前ですが)が、あえて指摘させていただきます。当初の質問でいただいた回答が既にあるので、質問内容を大きく変えると質問と回答がちぐはぐになります。そうなると後で閲覧した方々が????となるので、そういった場合はなるべく当初の質問を残したうえで、追記修正である旨を添えて編集すると良いです。
dodox86

2020/06/29 08:33

尚、この質問から直して!、と言っている訳ではありません。(少なくとも私個人の意見としては)上記にご納得いただけるのであれば、次からそうしてもらえればよろしいかと思います。
退会済みユーザー

退会済みユーザー

2020/06/29 08:34

確かにそうですね、ご指摘有り難うございます。 ネット上での質問はあまり経験がないので、優しく対応して頂いて幸いです。
退会済みユーザー

退会済みユーザー

2020/06/29 08:35

次回からは気を付けたいと思います。 有り難うございます。
guest

回答3

0

ベストアンサー

結論

現状のmemo.txtの構成およびプログラムでは、どんなに頑張っても正しく出力することはできません。
名前、郵便番号、住所、電話番号の区切りがわからないからです。

データ構造に関する基本的なこと

あるデータを保存する際の形式として、主に「固定長」「可変長」の2種類があります。

  • 固定長データ

データの種類ごとに固定のサイズを設定します。例えば名前を20バイト、郵便番号を7バイト、住所を100バイトとすると、1バイト目から20バイト目が名前、21バイト目から27バイト目までが郵便番号、28バイト目から最後までが住所、というように目的のデータが何バイト目かで探せます。名前や住所は20バイト、100バイトも必要ないかもしれませんが、その場合は空白だったりヌル文字で規定サイズになるまで埋めます。これをパディングと呼びます。
MyName 1506190東京都渋谷区渋谷2-24-12 (以下略)

  • 可変長データ

データの種類ごとに特定の区切り文字をつけて区別します。csvファイルをイメージしてもらうと良いです。例えば,を区切りにすれば名前,郵便番号,住所でデータを表せます。パディングが不要なのでデータサイズを削減できますが区切り文字からデータを分割する処理が必要で、またデータ内に区切り文字が出現する場合の考慮が必要となります。

Register時の問題点

データを保存する構造体Infoは固定長データです。

  • name 512バイト
  • zipcode 512バイト
  • address 512バイト
  • tel 512バイト

registar関数でファイルにデータを出力する際、fprintf関数で"%s"指定で出力しているのでchar配列の先頭から\0の手前までしか出力されていません。この時点でデータサイズの情報が失われています。
また、複数の種類のデータをそのまま続けて出力しているので、データとデータの区切りもわからなくなっています。
つまり、出力先のmemo.txtに記録されたデータは、あらかじめ設定されたデータサイズでの出力もされず、データ間の区切り文字も出力されていないのでデータの区切りがわからなくなっています。

改善策

まず、記録方法を固定長データにするか可変長データにするか決める必要があります。

  • 固定長データにするなら

構造体のデータをそのままfwriteで書き込み、freadで読み込みできます。ttyp03さんの回答の方法でほぼほぼ可能です。
ただし、非常に無駄のあるデータサイズになります。7桁の郵便番号に512バイトも必要ないし。必要なデータサイズを見積もりしないと後々大変なことになります。

  • 可変長データにするなら

registar関数で出力する際に、データとデータの間に何らかの区切り文字を出力すればよいです。
例えば、半角空白を区切り文字にするなら。

c

1void Register(InfoList list, FILE *fp){ 2 fp = fopen("memo.txt", "a"); 3(略) 4 fprintf(fp, "%s %s %s %s\n", 5 list[ID].name, 6 list[ID].zipcode, 7 list[ID].address, 8 list[ID].tel 9 ); 10 printf("登録に成功しました。\n"); 11}

読み込み時には、

c

1void OutputInfo(InfoList list, int ID, FILE *fp){ 2 printf("ID:%d\n", ID); 3 fscanf(fp, "%s %s %s %s\n", 4 list[ID].name, 5 list[ID].zipcode, 6 list[ID].address, 7 list[ID].tel 8 ); 9}

のようにすることで空白区切りのデータを扱えます。(データ内に半角空白が存在しないことが条件です)

まずは構造体でなくても良いので文字列をファイルに出力し、そのまま読み込んで期待通りのデータが表示できるかどうか別プログラムで練習してみることをお勧めします。小さなプログラムでうまくできるようになったら本件のプログラムに適用してみましょう。なるべく小さなコードから練習し徐々に大きなコードを書いていくのが結局は上達の近道なのです。

投稿2020/06/29 10:24

hope_mucci

総合スコア4447

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/29 10:57

こんなに詳しく記述して頂き有り難うございます...! 質問内容が分かりにくくご迷惑をお掛けしました。 ご丁寧に解説して下さり有り難うございました。 今後は小さなコードで質問しようと思います。
hope_mucci

2020/06/29 11:04

問題を小さなコードで再現しようとすると、その過程でなにが誤っているのか分かることがよくあります。 https://teratail.com/help/question-tips#questionTips3-5 ミニマムコードはバグ解決に大いに有用です。これからも頑張ってください。
退会済みユーザー

退会済みユーザー

2020/06/29 11:18

本当に感謝してもしきれません。 今回の経験を糧に頑張ります。
guest

0

出力フォーマットが不明ですが、構造体の要素数も固定で決まっていますので、構造体丸ごと読み書きでいいんじゃないでしょうか?
バイナリファイルになるので、出力したファイルをエディタ等で編集はしづらくなりますが。
自分で回答しておいて今の時代でバイナリデータはないな、とは思いますが。。。
勉強で作っているのでしょうから、他の回答の方法など色々やってみるといいですね。

c

1// 書き込み 2FILE *fp; 3fp = fopen("hoge.dat", "wb"); 4fwrite(InfoList, sizeof(InforList), 1, fp); 5fclose(fp); 6 7// 読み込み 8FILE *fp; 9fp = fopen("hoge.dat", "rb"); 10fread(InfoList, sizeof(InforList), 1, fp); 11fclose(fp); 12

投稿2020/06/29 07:30

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/29 07:56

ご回答有り難うございます。 バイナリファイルの場合も試してみたいと思います。
guest

0

ひんと: 構造体いっこだけ save/load するサンプル:

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <stdbool.h> 5 6#define SIZE 100 7#define LENGTH 512 8 9//名前・郵便番号・住所・電話番号を構造体にまとめる 10typedef struct{ 11 char name[LENGTH]; 12 char zipcode[LENGTH]; 13 char address[LENGTH]; 14 char tel[LENGTH]; 15} Info; 16 17// かく 18bool save(const char* filename, const Info* item) { 19 FILE* fp = fopen(filename, "w"); 20 if ( fp == NULL ) return false; 21 fputs(item->name, fp); fputs("\n", fp); 22 fputs(item->zipcode, fp); fputs("\n", fp); 23 fputs(item->address, fp); fputs("\n", fp); 24 fputs(item->tel, fp); fputs("\n", fp); 25 fclose(fp); 26 return true; 27} 28 29/* 末尾の改行を潰す */ 30void trim_nl(char* str) { 31 char* p = strchr(str,'\n'); 32 if ( p != NULL ) *p = '\0'; 33} 34 35// よむ 36bool load(const char* filename, Info* item) { 37 FILE* fp = fopen(filename, "r"); 38 if ( fp == NULL ) return false; 39 fgets(item->name, LENGTH, fp); trim_nl(item->name); 40 fgets(item->zipcode, LENGTH, fp); trim_nl(item->zipcode); 41 fgets(item->address, LENGTH, fp); trim_nl(item->address); 42 fgets(item->tel, LENGTH, fp); trim_nl(item->tel); 43 fclose(fp); 44 return true; 45} 46 47int main() { 48 Info from; 49 Info to; 50 51 strcpy(from.name, "安倍晋三"); 52 strcpy(from.zipcode, "100-0014"); 53 strcpy(from.address, "東京都千代田区永田町2丁目3-1"); 54 strcpy(from.tel, "03-3581-0101"); 55 56 save("book.txt", &from); 57 load("book.txt", &to); 58 59 printf("%s / %s / %s / %s\n", to.name, to.zipcode, to.address, to.tel); 60 return 0; 61}

投稿2020/06/29 06:39

episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/06/29 06:45

ご回答有り難うございます。 自分でもう一度考えてみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問