gets_s,fscanf,strncmpが思ったように動きません.

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
熊ー
記事: 5
登録日時: 1ヶ月前

gets_s,fscanf,strncmpが思ったように動きません.

#1

投稿記事 by 熊ー » 1ヶ月前

最近、あるc言語入門サイトを一通り終わったばかりのレベルの者です。
考えながら電話帳のプログラムを書いてみたのですが、gets_s,strncmp,fscanfが思った通りの動作をしません。
文法的なエラーも警告もないので何が間違っているのかわかりません。
touroku関数は最初の氏名電話番号は登録できるのですが、2回目の登録の際、fscanfで配列plarryにファイルを読み込んで配列内容が0だった場合、その配列要素に入力内容を代入するというイメージです。しかしデバッグモードのローカル変数を見ると氏名は読み込めますが電話番号は読み込めてないです。なぜかわかりません。
そして、strncmpで読み込んだ内容(0の場合)と入力内容が違う場合その配列要素に入力内容を代入するという書き方も、代入処理にならずスルーになってしまいます。
あと名前の入力のところで1回目のgets_s関数がスルーされてしまいます。swichのエンターが働いているのだと思いますが、どう対処すればよいでしょうか?

code

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>

typedef struct {
char name[60];
unsigned int number;
}phoneLIst;

void touroku(void);
void search(void);
void dele(void);

int main(void) {

int a = 0;

printf("\n-----電話帳-----\n");

printf("新規登録 -- 1\n");
printf("検索 -- 2\n");
printf("削除 -- 3\n");
printf("終了 -- 他\n");

scanf_s("%d", &a);

switch (a) {

case 1:touroku();
break;

case 2:search();
break;

case 3:dele();
break;

default:
break;
}

printf("\n終了");

return 0;

}

void touroku(void) {

printf("10件まで登録可\n");

phoneLIst pl = { 0,0 };
phoneLIst plarry[10] = { 0,0 };
int i = 0;

printf("名前の入力 -- ");
gets_s( pl.name,60);
gets_s( pl.name, 60);

printf("\n番号入力(-)なし -- ");
scanf_s("%d",&pl.number);

FILE* fp = NULL;
fopen_s(&fp, "PhoneList.txt", "r+");

if (fp == NULL) { //ファイルがなければ書き込みモード
fopen_s(&fp, "PhoneList.txt", "w");
plarry[0] = pl;
if (fp != NULL) {
fprintf(fp, "氏名:%s,電話番号:%d\n", plarry.name, plarry.number);
printf("保存完了");
return;
}
else {
printf("ERROR");
return;
}
}

else if (fp != NULL) { //ファイルがあれば読み込み

while(fscanf(fp, "氏名:%s,電話番号:%d\n", plarry.name, &plarry.number)!=EOF){

//if(strncmp(plarry.name,pl.name,60) != 0) //読み込んだ内容と違う場合
if (plarry.name == 0) //読み込んだ内容が0の場合
{
plarry = pl;
break;
}

}

fseek(fp, 0, SEEK_SET);

for (int i = 0; i < 10; i++) {
fprintf(fp, "氏名: % s, 電話番号 : % d\n", plarry.name, plarry.number);
}

printf("登録完了");
fclose(fp);
}

else{
printf("登録不可");
}

return;
}

void search(void) {

phoneLIst pl = { 0 };
phoneLIst plarry[10] = { 0 };
int i = 0, j = 0;

printf("検索 名前の入力\n");
gets_s(pl.name, 60);
gets_s(pl.name, 60);

FILE* fp = NULL;
fopen_s(&fp, "PhoneList.txt", "r");

if (fp != NULL) {
while (fscanf(fp, "氏名:%s,電話番号:%d\n", plarry.name, &plarry[i].number) != EOF) {

if (strncmp(pl.name,plarry[i].name,60)==0) { //同じ名前があれば表示
printf("氏名:%s,電話番号:%d", plarry[i].name, plarry[i].number);
j++;
break;
}
}
}
else {
printf("電話帳なし\n");
return;
}

if (j>0) {
printf("該当なし");
}

return;
}

void dele(void) {

phoneLIst pl = { 0 };
phoneLIst plarry[10] = { 0 };
int a;

printf("電話帳リスト\n");

FILE* fp = NULL;
fopen_s(&fp, "PhoneList.txt", "r+");
int i = 0;

if (fp != NULL) {

while (fscanf(fp, "%s,%d", plarry[i].name, &plarry[i].number) != EOF) {
printf("番号[%d]氏名:%s,電話番号:%d", i, plarry[i].name, plarry[i].number);
}


printf("削除する番号の選択---");
scanf_s("%d", &a);
plarry[a] = pl;

fseek(fp, 0, SEEK_SET);

for (int i = 0; i < 10; i++) {
fprintf(fp, "氏名:%s,電話番号:%d", plarry[i].name, plarry[i].number);
if (plarry[i].name == 0) {
plarry[i] = pl;
break;
}
}
printf("登録完了");
fclose(fp);

}
return;
}

/code

アバター
あたっしゅ
記事: 458
登録日時: 10年前
住所: 東京23区
連絡を取る:

Re: gets_s,fscanf,strncmpが思ったように動きません.

#2

投稿記事 by あたっしゅ » 1ヶ月前

東上☆海美☆「
Windows 10 Pro 64bit
VS2019
にぶっこんだら、74 行を初めとして、エラー出まくりでみみ。

いったいどのような環境で、「文法的なエラーも警告もない」のでしょうかみみ ?
ファイルのアップミスでしょうかみみ ?
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.rosx.net/vtuber/index.html
レスがついていないものを優先して、レスすみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたっしゅ、[MrAtassyu]
http://ameblo.jp/mratassyu/
Pixiv: 666303
手提鞄屋魚有店(てさげかばんやうおありてん)

熊ー
記事: 5
登録日時: 1ヶ月前

Re: gets_s,fscanf,strncmpが思ったように動きません.

#3

投稿記事 by 熊ー » 1ヶ月前

ん?エラー出まくりですか?そうなんですね!ん?
環境は
Windows 10 Home64bit
Microsoft Visual Studio Community 2019
Version 16.8.6
です。
ファイルはソースをそのまんまコピペしましたのでアップミスはないはずです。
見直してみましたがそのままだと思います。
これでソリューションのビルドをしても問題は見つかりませんでしたってなります。
========== ビルド: 0 正常終了、0 失敗、1 更新不要、0 スキップ ==========
失敗1はやはり失敗なんですよね?といいますか、そもそもvisualstudioをインストールしなおした方がいい気がしてきました。

アバター
みけCAT
記事: 6436
登録日時: 10年前
住所: 千葉県
連絡を取る:

Re: gets_s,fscanf,strncmpが思ったように動きません.

#4

投稿記事 by みけCAT » 1ヶ月前

熊ー さんが書きました:
1ヶ月前
ファイルはソースをそのまんまコピペしましたのでアップミスはないはずです。
codeタグに必要な[]が抜けているというミスがあります。
あたっしゅ さんが書きました:
1ヶ月前
74 行を初めとして、エラー出まくりでみみ。
この掲示板は[​/i]が無くても[​i]が斜体タグとして解釈されてしまう不親切設計なので、
codeタグで囲まれていないコードは引用画面からコピーするべきです。

※ここの[​i]はU+200Bを挟むことで、タグとしての解釈を回避しています
熊ー さんが書きました:
1ヶ月前
========== ビルド: 0 正常終了、0 失敗、1 更新不要、0 スキップ ==========
失敗1はやはり失敗なんですよね?
失敗1ではなく、「0 失敗、1 更新不要」ですね。
熊ーさんの解釈が失敗です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

熊ー
記事: 5
登録日時: 1ヶ月前

Re: gets_s,fscanf,strncmpが思ったように動きません.

#5

投稿記事 by 熊ー » 1ヶ月前

こういうことですね!

コード:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>

typedef struct {
	char name[60];
	unsigned int number;
}phoneLIst;

void touroku(void);
void search(void);
void dele(void);

int main(void) {

	int a = 0;

	printf("\n-----電話帳-----\n");

	printf("新規登録 -- 1\n");
	printf("検索 -- 2\n");
	printf("削除 -- 3\n");
	printf("終了 -- 他\n");

	scanf_s("%d", &a);

	switch (a)	{

	case 1:touroku();
		break;

	case 2:search();
		break;

	case 3:dele();
		break;

	default:
		break;
	}

	printf("\n終了");

	return 0;

}

void touroku(void) {

	printf("10件まで登録可\n");

	phoneLIst pl = { 0,0 };
	phoneLIst plarry[10] = { 0,0 };
	int i = 0;

	printf("名前の入力 -- ");
	gets_s( pl.name,60);
	gets_s( pl.name, 60);

	printf("\n番号入力(-)なし -- ");
	scanf_s("%d",&pl.number);

	FILE* fp = NULL;
	fopen_s(&fp, "PhoneList.txt", "r+");

	if (fp == NULL) {							//ファイルがなければ書き込みモード
		fopen_s(&fp, "PhoneList.txt", "w");
		plarry[0] = pl;
		if (fp != NULL) {
			fprintf(fp, "氏名:%s,電話番号:%d\n", plarry[i].name, plarry[i].number);
			printf("保存完了");
			return;
		}
		else {
			printf("ERROR");
			return;
		}
	}
		
	else if (fp != NULL)	{					//ファイルがあれば読み込み

		while(fscanf(fp, "氏名:%s,電話番号:%d\n", plarry[i].name, &plarry[i].number)!=EOF){

				//if(strncmp(plarry[i].name,pl.name,60) != 0)	//読み込んだ内容と違う場合
				if (plarry[i].name == 0)						//読み込んだ内容が0の場合
				{	
				plarry[i] = pl;
				break;
			}

		}

		fseek(fp, 0, SEEK_SET);

		for (int i = 0; i < 10; i++)		{
			fprintf(fp, "氏名: % s, 電話番号 : % d\n", plarry[i].name, plarry[i].number);
		}

		printf("登録完了");
		fclose(fp);
	}	

	else{
		printf("登録不可");
	}

	return;
}

void search(void) {
	 
	phoneLIst pl = { 0 };
	phoneLIst plarry[10] = { 0 };
	int i = 0, j = 0;

	printf("検索 名前の入力\n");
	gets_s(pl.name, 60);
	gets_s(pl.name, 60);

	FILE* fp = NULL;
	fopen_s(&fp, "PhoneList.txt", "r");
	
	if (fp != NULL)	{
		while (fscanf(fp, "氏名:%s,電話番号:%d\n", plarry[i].name, &plarry[i].number) != EOF) {

			if (strncmp(pl.name,plarry[i].name,60)==0)			{						//同じ名前があれば表示
				printf("氏名:%s,電話番号:%d", plarry[i].name, plarry[i].number);
				j++;
				break;
			}
		}
	}
	else	{
		printf("電話帳なし\n");
		return;
	}

	if (j>0)	{
		printf("該当なし");
	}

	return;
}

void dele(void) {

	phoneLIst pl = { 0 };
	phoneLIst plarry[10] = { 0 };
	int a;

	printf("電話帳リスト\n");

	FILE* fp = NULL;
	fopen_s(&fp, "PhoneList.txt", "r+");
	int i = 0;

	if (fp != NULL) {

		while (fscanf(fp, "%s,%d", plarry[i].name, &plarry[i].number) != EOF) {
			printf("番号[%d]氏名:%s,電話番号:%d", i, plarry[i].name, plarry[i].number);
		}


		printf("削除する番号の選択---");
		scanf_s("%d", &a);
		plarry[a] = pl;

		fseek(fp, 0, SEEK_SET);

		for (int i = 0; i < 10; i++) {
			fprintf(fp, "氏名:%s,電話番号:%d", plarry[i].name, plarry[i].number);
			if (plarry[i].name == 0) {
				plarry[i] = pl;
				break;
			}
		}
		printf("登録完了");
		fclose(fp);

	}
	return;
}

熊ー
記事: 5
登録日時: 1ヶ月前

Re: gets_s,fscanf,strncmpが思ったように動きません.

#6

投稿記事 by 熊ー » 1ヶ月前

strncmpは解決しました。
         //if(strncmp(plarry.name,pl.name,60) != 0) //読み込んだ内容と違う場合
if (plarry.name == 0) //読み込んだ内容が0の場合
{
plarry = pl;
break;
}
このif文をif(strncmp(plarry.name,"",60) == 0)にしました。
これで空の配列要素に代入できました。

touroku関数のfscanfは、なぜplarry.nameに氏名と電話番号が読み込まれるのでしょうか?

熊ー
記事: 5
登録日時: 1ヶ月前

Re: gets_s,fscanf,strncmpが思ったように動きません.

#7

投稿記事 by 熊ー » 1ヶ月前

とりあえず思った通りの動作はするようになりました。

コード:

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

typedef struct {
	char name[60];
	char number[12];
}phoneLIst;

void touroku(int);
void search(void);
void dele(int);

int main(void) {

	int a = 0;
	FILE* fp = NULL;
	int ret = 0;
	phoneLIst plist[10] = { 0 };
	fopen_s(&fp, "PhoneList.txt", "r");

	if (fp != NULL)	{		//登録の空きがあるかチェック
		while (fscanf(fp,"氏名:%s 電話番号:%s\n", plist[ret].name,plist[ret].number) != EOF)		{
			if (strncmp(plist[ret].number, "", 12) == 0)		{
				break;
			}
			ret++;
		}
		fclose(fp);
	}		

	printf("\n-----電話帳-----\n");

	printf("新規登録 -- 1\n");
	printf("検索 -- 2\n");
	printf("削除 -- 3\n");
	printf("終了 -- 他\n");

	scanf_s("%d", &a);

	switch (a) {

	case 1:	if (ret == 10) {
			printf("これ以上登録できません");
			break;
			}			
			touroku(ret);
		break;

	case 2:search();
		break;

	case 3:dele(ret);
		break;

	default:
		break;
	}

	printf("\n終了\n");

	return 0;

}

void touroku(int ret) {

	printf("10件まで登録可\n");

	phoneLIst pl = { 0,0 };
	phoneLIst plarry[10] = { 0,0 };
	int i = 0;

	printf("名前の入力 -- ");
	gets_s(pl.name, 60);
	gets_s(pl.name, 60);

	printf("\n番号入力(-)なし -- ");
	gets_s(pl.number, 12);

	FILE* fp = NULL;
	fopen_s(&fp, "PhoneList.txt", "r+");

	if (fp == NULL) {							//ファイルがなければ書き込みモード
		fopen_s(&fp, "PhoneList.txt", "w");
		plarry[0] = pl;
		if (fp != NULL) {
			for (int i = 0; i < 10; i++) {
				fprintf(fp, "氏名:%s 電話番号:%s\n", plarry[i].name, plarry[i].number);
			}
			printf("保存完了");
			return;
		}
		else {
			printf("ERROR");
			return;
		}
	}

	else if (fp != NULL) {					//ファイルがあれば読み込み

		while (fscanf(fp, "氏名:%s 電話番号:%s\n", plarry[i].name, &plarry[i].number) != EOF) {

			if (ret == i) {					//読み込んだ内容が0の場合入力内容の代入
				plarry[i] = pl;
			}
			i++;
		}

		fseek(fp, 0, SEEK_SET);

		for (int i = 0; i < 10; i++) {
			fprintf(fp, "氏名:%s 電話番号:%s\n", plarry[i].name, plarry[i].number);
		}

		printf("登録完了");
		fclose(fp);
	}

	else {
		printf("登録不可");
	}

	return;
}

void search(void) {

	phoneLIst pl = { 0 };
	phoneLIst plsearch = { 0 };
	int i = 0, j = 0;

	printf("検索 名前の入力\n");
	gets_s(pl.name, 60);
	gets_s(pl.name, 60);

	FILE* fp = NULL;
	fopen_s(&fp, "PhoneList.txt", "r");

	if (fp != NULL) {
		while (fscanf(fp, "氏名:%s 電話番号:%s\n", plsearch.name, &plsearch.number) != EOF) {

			if (strncmp(pl.name, plsearch.name, 60) == 0) {						//同じ名前があれば表示
				printf("\n氏名:%s 電話番号:%s\n", plsearch.name, plsearch.number);
				j++;
				//break;
			}
		}
	}
	else {
		printf("電話帳なし\n");
		return;
	}

	if (j == 0) {
		printf("該当なし");
	}

	return;
}

void dele(int ret) {

	phoneLIst pl = { 0 };
	phoneLIst plarry[10] = { 0 };
	int a;

	printf("電話帳リスト\n");

	FILE* fp = NULL;
	fopen_s(&fp, "PhoneList.txt", "r+");
	int i = 0;

	if (fp != NULL) {

		while (fscanf(fp, "氏名:%s 電話番号:%s\n", plarry[i].name, &plarry[i].number) != EOF) {
			printf("番号[%d]氏名:%s 電話番号:%s\n", i, plarry[i].name, plarry[i].number);
			i++;
		}


		printf("削除する番号の選択--- 0-9,キャンセルは10\n");
		while (1)		{
			scanf_s("%d", &a);
			if (a<10&&a>=0)			{
				break;
			}			

			else if (a==10)		{
				printf("キャンセルします\n");
				return;
			}
			printf("不正な番号です。再入力待ち\n");
		}
		plarry[a] = pl;

		fclose(fp);
	}

	fopen_s(&fp, "PhoneList.txt", "w");
	if (fp != NULL)	{

		for (int i = 0; i < 10; i++) {
			fprintf(fp, "氏名:%s 電話番号:%s\n", plarry[i].name, plarry[i].number);
			printf("番号[%d]氏名:%s 電話番号:%s\n", i, plarry[i].name, plarry[i].number);
			
		}
		printf("削除完了");
		fclose(fp);

	}
	return;
}
 


やはりgets_s関数を二重にしないと動かないのと、初めのfscanfでファイルに名前が保存されてないときplist[ret].nameに電話番号:が代入されるのもなぜなんでしょうか?半角の空白で区切ってるのですが。

box
記事: 1787
登録日時: 10年前

Re: gets_s,fscanf,strncmpが思ったように動きません.

#8

投稿記事 by box » 4週間前

gets_s関数ってたぶん処理系独自のものだから
何とも言えないところですが、末尾の'\n'を取り除く処理が
いるのではないでしょうか。
標準のgets関数では、例えば

コード:

    str[strlen(str)-1] = '\0';
か何かで強制的に改行を取っ払うことが必要だったように思います。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
みけCAT
記事: 6436
登録日時: 10年前
住所: 千葉県
連絡を取る:

Re: gets_s,fscanf,strncmpが思ったように動きません.

#9

投稿記事 by みけCAT » 4週間前

box さんが書きました:
4週間前
gets_s関数ってたぶん処理系独自のものだから
何とも言えないところですが、末尾の'\n'を取り除く処理が
いるのではないでしょうか。
'\n'の除去はgets_s関数側でやってくれるようなので、明示的に書く必要は無いでしょう。

gets_s, _getws_s | Microsoft Docs
gets_s then replaces the newline character with a null character ('\0') before returning the line.
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6436
登録日時: 10年前
住所: 千葉県
連絡を取る:

Re: gets_s,fscanf,strncmpが思ったように動きません.

#10

投稿記事 by みけCAT » 4週間前

熊ー さんが書きました:
1ヶ月前
やはりgets_s関数を二重にしないと動かないのと、

コード:

 
	scanf_s("%d", &a);
は改行文字をストリームから除去しないので、次のgets_s関数はこの改行文字を読み込んで止まってしまいます。
ここもgets_s関数で読み込んで、sscanf_s関数で数値に変換するようにするといいでしょう。

コード:

char lineBuffer[1024];
gets_s(lineBuffer, sizeof(lineBuffer));
sscanf_s(lineBuffer, "%d", &a);
熊ー さんが書きました:
1ヶ月前
初めのfscanfでファイルに名前が保存されてないときplist[ret].nameに電話番号:が代入されるのもなぜなんでしょうか?半角の空白で区切ってるのですが。
行が

コード:

氏名: 電話番号:
のようになっているとすると、%sは空白文字を読み飛ばすので、
「氏名:」の次に来る空白文字でない文字で始まる文字列、すなわち「電話番号:」が
%sによってplist[ret].nameに読み込まれることになります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
あたっしゅ
記事: 458
登録日時: 10年前
住所: 東京23区
連絡を取る:

Re: gets_s,fscanf,strncmpが思ったように動きません.

#11

投稿記事 by あたっしゅ » 3週間前

東上☆海美☆「
とりあえず、

コード:

int
main(void) 
{
	//int a = 0;
	FILE* fp = NULL;
	int ret = 0;
	phoneLIst plist[10] = { 0 };
	fopen_s(&fp, "PhoneList.txt", "r");

	if (fp != NULL) {		//登録の空きがあるかチェック
		while (fscanf(fp, "氏名:%s 電話番号:%s\n", plist[ret].name, plist[ret].number) != EOF) {
			if (strncmp(plist[ret].number, "", 12) == 0) {
				break;
			}
			ret++;
		}
		fclose(fp);
	}

	try {
		while (1) {
			printf("\n-----電話帳-----\n");

			printf("新規登録 -- 1\n");
			printf("検索 -- 2\n");
			printf("削除 -- 3\n");
			printf("終了 -- 他\n");

			//scanf_s("%d", &a);
			{
				const int BUFMAX = 60;
				char buf[BUFMAX];

				gets_s(buf, BUFMAX);

				switch (buf[0]) {

				case '1':
					if (ret == 10) {
						printf("これ以上登録できません");
						break;
					}
					touroku(ret);
					break;

				case '2':
					search();
					break;

				case '3':
					dele(ret);
					break;

				default:
					throw "LoopBreak";
					break;
				}
			}
		}
	} catch(...) {
		printf("\n終了\n");
	}
	return 0;
}
main 関数を書き換えてみたみみ
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.rosx.net/vtuber/index.html
レスがついていないものを優先して、レスすみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたっしゅ、[MrAtassyu]
http://ameblo.jp/mratassyu/
Pixiv: 666303
手提鞄屋魚有店(てさげかばんやうおありてん)

返信

“C言語何でも質問掲示板” へ戻る