C言語の質問です

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
きょろ

C言語の質問です

#1

投稿記事 by きょろ » 12年前

テキストファイルに英単語を登録したり、それを検索(読み出)したりするプログラムを作りました。
しかし、登録までは上手くいくのですが検索になるとうまく行きません。何処が悪いのでしょうか?
下のプログラムをご覧下さい。(登録は大丈夫でしたが、全文を載せておきます)


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#
#
#define FILENAME "tango.txt"
#//夫々の配列の要素数
#define SPELL 20
#define MEAN 100
#define PAGE 10
#define LINE 10
#define SNT 100
#
#define GOKEI SPELL + MEAN + PAGE + LINE + SNT

char Spelling[SPELL], //単語の綴り
Meaning[MEAN], //単語の意味
Page[PAGE], //単語の登録されているページ
Line[LINE], //単語の登録されている行数
Sonota[SNT]; //その他の情報

// 綴り 意味 ページ 行数 その他の情報
char *format = "%-20s%-100s%-5d%-5d%-100s";
char *format2 = "%-100s%-5d%-5d%-100s";

int menu(); //メニュー関数
int toroku(); //単語を登録する関数
int kensaku(); //単語を検索する関数
int shusei(); //単語を修正する関数  ※まだ此れは未制作です

int main()
{
int no;

no = menu();

while(1) {
switch(no) {
case 1: no = toroku();
continue;

case 2: no = kensaku();
continue;

case 3: no = shusei();
continue;

case 0: break;

case 4: no = menu();
continue;

}
break;
}
return 0;

}


//メニュー画面
int menu()
{
char no[4];
int so;

while(1) {
system("cls");
puts("-----------------------");
puts("1:単語の登録");
puts("2:単語の検索");
puts("3:単語の修正");
puts("0:終了");
puts("-----------------------");
printf("->>");
gets(no);
if(no[0] < '0' || no[0] > '3') {
printf("入力が不正です\n\n");
continue;
}else{
no[1] = '\0';
}

so = atoi(no);
break;
}
system("cls"); //画面を消去

return so;
}


//データの登録
int toroku()
{
FILE *fp;
int Pa, Li, no;
char yesno[5];

fp = fopen(FILENAME,"a");
if(fp == NULL) {
perror("ファイルをオープンできません\n");
return -1;
}

while(1) {
printf("単語を入力して下さい\n");
printf("-> ");
gets(Spelling);

printf("単語の意味を入力して下さい\n");
printf("-> ");
gets(Meaning);

printf("単語の登録されているページを入力して下さい\n");
printf("-> ");
gets(Page);
Page[PAGE - 1] = '\0';
Pa = atoi(Page);

printf("単語の登録されているページの行数を入力して下さい\n");
printf("-> ");
gets(Line);
Line[LINE - 1] = '\0';
Li = atoi(Line);

printf("その他の情報がありましたら入力して下さい\n");
printf("-> ");
gets(Sonota);

printf("\nこれらを登録しますか?\n");
printf("はい -- Y\n");
printf("もう一度打ち直す-- N\n");
printf("タイトルに戻る -- E\n");
printf("-> ");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y') {
fprintf(fp, format, Spelling, Meaning, Pa, Li, Sonota); //入力したデータを書き込む

printf("入力を続けますか?(Y/N):");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y')
continue;
else
break;

}else if(yesno[0] == 'N' || yesno[0] == 'n') {
continue;
}else if(yesno[0] == 'E' || yesno[0] == 'e'){
break;
}else{
break;
}
}

system("cls"); //画面を消去

fclose(fp);

return 4;
}


int kensaku()
{
saisho_K:
FILE *fp;
char search[SPELL], str2[SPELL], yesno[5] = {0};
int Pa1 = 0, Li1, i = -1, find = 0;

fp = fopen(FILENAME, "r");
if(fp == NULL){
perror("ファイルをオープンできません\n");
return -1;
}

printf("検索したい単語名を入力して下さい\n");
printf("-> ");
gets(search);

while(1) {
i++;
fseek(fp, GOKEI*i, SEEK_SET);
if(fscanf(fp, "%s", str2) == EOF) {
printf("見つかりませんでした\n");
Pa1 = 100;
break;
}
if(strcmp(str2, search) == 0) {
fscanf(fp, format2, Meaning, Page, Line, Sonota);
find++;
printf("見つかりました\n");
printf("-------------------");
printf("単語名:%-20s\n", str2);
printf("意味 :%-100s\n", Meaning);
printf("ページ:%dページ\n", Page);
printf("行  :%d行\n",Line);
printf("その他の情報\n"
"%s",Sonota);
printf("-------------------");

printf("更に検索を続けますか?(Y/N):");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y')
continue;
else
break;
}

}
printf("%d件が検索されました",find);
if(Pa1 == 100) {
printf("もう一度検索しますか?(Y/N):");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y')
goto saisho_K;
}

fclose(fp);
return 4;
}

ご解答宜しくお願いします
また、ご不明な点がございましたら仰って下さい。

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

Re: C言語の質問です

#2

投稿記事 by みけCAT » 12年前

とりあえず、コードを提示するときはBBcodeを有効にした状態でcodeタグで囲み、
かつ適切なインデントをしていただけると、見やすくて助かります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: C言語の質問です

#3

投稿記事 by みけCAT » 12年前

kensaku関数の「fseek(fp, GOKEI*i, SEEK_SET);」という部分で、GOKEIマクロがどのように展開されるかを考えてみましょう。

参照:1=2 - アンサイクロペディア
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: C言語の質問です

#4

投稿記事 by みけCAT » 12年前

さらに、PAGEおよびLINEマクロの情報とformatに代入されている情報が矛盾しており、
上記の問題を修正しても不都合が生じる原因になります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

きょろ

Re: C言語の質問です

#5

投稿記事 by きょろ » 12年前

format
が矛盾してるってどういう事ですか?

きょろ

Re: C言語の質問です

#6

投稿記事 by きょろ » 12年前

ああ
GOKEI * i
だと
SPELL + MEAN + PAGE + LINE + SNT * i
つまり
SPELL + MEAN + PAGE + LINE +( SNT * i)
こうなるという事ですか!?

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

Re: C言語の質問です

#7

投稿記事 by みけCAT » 12年前

きょろ さんが書きました:format
が矛盾してるってどういう事ですか?
書き込まれる1単語あたりのデータサイズと、読み込むときの1単語あたりのシーク距離が違うので、
読み込むが上手くいかない可能性が高いと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: C言語の質問です

#8

投稿記事 by みけCAT » 12年前

きょろ さんが書きました:ああ
GOKEI * i
だと
SPELL + MEAN + PAGE + LINE + SNT * i
つまり
SPELL + MEAN + PAGE + LINE +( SNT * i)
こうなるという事ですか!?
そうですね。

コード:

#define GOKEI (SPELL + MEAN + PAGE + LINE + SNT)
とするのがいいと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

きょろ

Re: C言語の質問です

#9

投稿記事 by きょろ » 12年前

済みません、BBcodeとかのやり方が解らないのでそのまま載せます。
ひとまず所々直し、検索して無事に見つかったのですが、やはりご指摘通りformatが悪いのかページが「3666733ページ」
とか表示されます。しかし、どうすれば直るか解りません。
アドバイスだけでもいいので頂けませんか?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#
#
#define FILENAME "tango.txt"
#define SPELL 20
#define MEAN 100
#define PAGE 10
#define LINE 10
#define SNT 100
#define GOKEI (SPELL + MEAN + PAGE + LINE + SNT)
char Spelling[SPELL], //単語の綴り
Meaning[MEAN], //単語の意味
Page[PAGE], //単語の登録されているページ
Line[LINE], //単語の登録されている行数
Sonota[SNT]; //その他の情報

// 綴り 意味 ページ 行数 その他の情報
char *format = "%-20s%-100s%-5d%-5d%-100s";
char *format2 = "%-100s%-5d%-5d%-100s";

int menu(); //メニュー関数
int toroku(); //単語を登録する関数
int kensaku(); //単語を検索する関数
int shusei(); //単語を修正する関数


int main()
{
int no;

no = menu();

while(1) {
switch(no) {
case 1: no = toroku();
continue;

case 2: no = kensaku();
continue;

case 3: no = shusei();
continue;

case 0: break;

case 4: no = menu();
continue;

}
break;
}
return 0;

}


//メニュー画面
int menu()
{
char no[4];
int so;

while(1) {
system("cls");
puts("-----------------------");
puts("1:単語の登録");
puts("2:単語の検索");
puts("3:単語の修正");
puts("0:終了");
puts("-----------------------");
printf("->>");
gets(no);
if(no[0] < '0' || no[0] > '3') {
printf("入力が不正です\n\n");
continue;
}else{
no[1] = '\0';
}

so = atoi(no);
break;
}
system("cls"); //画面を消去

return so;
}


//データの登録
int toroku()
{
FILE *fp;
int Pa, Li;
char yesno[5];

fp = fopen(FILENAME,"a");
if(fp == NULL) {
perror("ファイルをオープンできません\n");
return -1;
}

while(1) {
printf("単語を入力して下さい\n");
printf("-> ");
gets(Spelling);

printf("単語の意味を入力して下さい\n");
printf("-> ");
gets(Meaning);

printf("単語の登録されているページを入力して下さい\n");
printf("-> ");
gets(Page);
Page[PAGE - 1] = '\0';
Pa = atoi(Page);

printf("単語の登録されているページの行数を入力して下さい\n");
printf("-> ");
gets(Line);
Line[LINE - 1] = '\0';
Li = atoi(Line);

printf("その他の情報がありましたら入力して下さい\n");
printf("-> ");
gets(Sonota);

printf("\nこれらを登録しますか?\n");
printf("はい -- Y\n");
printf("もう一度打ち直す-- N\n");
printf("タイトルに戻る -- E\n");
printf("-> ");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y') {
fprintf(fp, format, Spelling, Meaning, Pa, Li, Sonota); //入力したデータを書き込む

printf("入力を続けますか?(Y/N):");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y')
continue;
else
break;

}else if(yesno[0] == 'N' || yesno[0] == 'n') {
continue;
}else if(yesno[0] == 'E' || yesno[0] == 'e'){
break;
}else{
break;
}
}

system("cls"); //画面を消去

fclose(fp);

return 4;
}


int kensaku()
{
FILE *fp;
char search[SPELL], str2[SPELL], yesno[5] = {0};
int Pa1 = 0, Li1, i = -1, find = 0;

fp = fopen(FILENAME, "r");
if(fp == NULL){
perror("ファイルをオープンできません\n");
return -1;
}

printf("検索したい単語名を入力して下さい\n");
printf("-> ");
gets(search);

while(1) {
i++;
fseek(fp, GOKEI*i, SEEK_SET);
if(fscanf(fp, "%s", str2) == EOF) {
printf("見つかりませんでした\n");
Pa1 = 100;
break;
}
if(strcmp(str2, search) == 0) {
fscanf(fp, format2, Meaning, Page, Line, Sonota);
find++;
printf("見つかりました\n");
printf("-------------------\n");
printf("単語名:%-20s\n", str2);
printf("意味 :%-100s\n", Meaning);
printf("ページ:%dページ\n", Page);
printf("行  :%d行\n",Line);
printf("その他の情報\n"
"%s",Sonota);
printf("-------------------\n");

printf("更に検索を続けますか?(Y/N):");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y')
continue;
else
break;
}

}
printf("%d件が検索されました",find);
if(Pa1 == 100) {
printf("もう一度検索しますか?(Y/N):");
gets(yesno);
if(yesno[0] == 'Y' || yesno[0] == 'y')
return 2;
}
gets(yesno);

while(1)
if(yesno[0] == '\0')
break;


fclose(fp);
return 4;
}

int shusei()
{
return 4;
}

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

Re: C言語の質問です

#10

投稿記事 by みけCAT » 12年前

とりあえず、formatとformat2を

コード:

char *format = "%-20s%-100s%-10d%-10d%-100s";
char *format2 = "%-100s%-10d%-10d%-100s";
としてみてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: C言語の質問です

#11

投稿記事 by みけCAT » 12年前

表示部分を

コード:

printf("ページ:%dページ\n", *((int*)Page));
printf("行  :%d行\n",*((int*)Line));
としてください。

【追記】テストしましたが、これでは上手くいかないようです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: C言語の質問です

#12

投稿記事 by みけCAT » 12年前

formatの宣言と代入を

コード:

char *format = "%-20s%-100s%-10d%-10d%-100s";
char *format2 = "%100s%10d%10d%100s";
とし、表示部分を

コード:

			printf("見つかりました\n");
			printf("-------------------\n");
			printf("単語名:%-20s\n", str2);
			printf("意味 :%-100s\n", Meaning);
			printf("ページ:%dページ\n", *((int*)Page));
			printf("行  :%d行\n",*((int*)Line));
			printf("その他の情報\n"
			"%s",Sonota);
			printf("-------------------\n");
とすることで、うまく表示することができました。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

きょろ

Re: C言語の質問です

#13

投稿記事 by きょろ » 12年前

済みません
*((int*)Page)
と変えても表示結果が正しく表示されません。

あと、何故*((int*)Page)とすれば良かったのでしょうか?

きょろ

Re: C言語の質問です

#14

投稿記事 by きょろ » 12年前

自分勝手で申し訳ありませんが、先に休ませて戴きます。
明日の午後4時~ほどから再開致しますので、お暇でしたら宜しくお願いします。

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

Re: C言語の質問です

#15

投稿記事 by みけCAT » 12年前

きょろ さんが書きました:済みません
*((int*)Page)
と変えても表示結果が正しく表示されません。
単語や単語の意味に空白やタブを含めると正しく表示されないはずですが、
こちらでのテストではそれらのコーナーケースを与えなければ正しく表示されているように思えます。
ソースコードとデータファイル、実行形式バイナリを添付します。
きょろ さんが書きました:あと、何故*((int*)Page)とすれば良かったのでしょうか?
kensaku関数の

コード:

fscanf(fp, format2, Meaning, Page, Line, Sonota);
という行において、Page配列の先頭に%10dという指定で読み込まれたint型のデータが格納されます。
この時、Page配列は10バイト確保されているので、int型のサイズが4バイト/8バイトの環境ではバッファオーバーランは起きません。
しかし、Page配列はchar[10]型なので、int型のデータをそのまま取り出すことはできません。
そこで、(int*)Pageという記述により、コンパイラにPage配列の先頭をint型へのポインタとして解釈させ、
さらに*((int*)Page)という記述によりそのint型へのポインタが差す場所(Page配列の先頭)に格納されているint型のデータを読み取り、
printf関数に渡すので、きちんと読み取ったページが表示されるはずです。
添付ファイル
tango_kensaku.zip
テストに使用したファイル一式
(6.66 KiB) ダウンロード数: 124 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

閉鎖

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