テキストファイルを読み込み、入力したデータとの重複がないかどうかを調べたいのですが、
わからない点があるため、質問させていただきます。
--------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE *fp;
char datafile[];= "sample.txt";
char buff[512]; //読み込んだ1行分のデータを格納
char *data[1000]; //読み込んだデータを格納
int data_c = 0; //データの数
char str[256]; //入力された文字列を格納
int i;
int check; //重複チェック
(中略)
//ファイルを1行ずつ読み込み、その長さのメモリを確保し、値をコピー
while(fgets(buff, sizeof buff, fp) != NULL) {
data[data_c] = (char*)malloc(strlen(buff) + 1);
strcpy(data[data_c++], buff);
}
(中略)
//文字列を入力
fgets(str, 256, stdin);
check = 0;
//すでにあるデータと入力したデータの重複を調べる
for(i=0; i<data_c; i++) {
if(strcmp(data[i], str) == 0) {
check = 1;
break;
}
}
(中略)
--------------------------------------------------------
例えば読み込むファイルに5行書かれていた場合、
data[0]からdata[4]に確保したメモリの先頭アドレスが格納されますよね?
ということはdata_cの値は4となるのですが、
その後のファイルデータと入力したデータの重複を調べるところで、
for(i=0; i<data_c; i++) となっており、data[0]からdata[3]までの4行分しか調べられないことになります。
なぜ、i<=data_cではなく、i<data_cとなっているのか、わかりましたら教えていただけますでしょうか。
A 回答 (7件)
- 最新から表示
- 回答順に表示
No.7
- 回答日時:
>fgets(str, sizeof str, stdin); としたほうがバランスがいいのでしょうか。
>ちなみに、変数宣言のところでchar str[256];としているため、
>改めてfgets(str, 256, stdin); と読み込み文字数を256にする必要はないのでしょうか?
char str[256];
では足りなかったから
char str[384];
に拡張しよう!
というのが、この先「絶対に」発生しないならいいんじゃないですか?
# 私はdefine定義で対応することの方が多いですけど。(wcharとか使うコトほとんどないし)
256から384に拡張したからgrepで256探して書き換えればオッケー♪
とか思っていた場合は、今回の254が漏れて不可解な挙動を…ということに。
まぁ、そんなワケで……
いわゆる「マジックナンバー」の使用は控えた方がいい。
ということで。
# プログラミング続けていればマジックナンバーで余計な手間を経験することもあるでしょう。
# そういう、(ある意味)痛い目を見ないと理解できないかも知れませんね。
何度も回答いただきありがとうございます。
fgets(str, 256, stdin); の256をマジックナンバーということを初めて知りました。意味のある定数を直接数字で記述することはよくないのですね。
define定義も初めて知りましたが、define定義をしておけば、strの大きさが変更になった際もdefineの数値を変更すればいいため、わかりやすいですね。
まだ勉強を始めたところで知らないことばかりでしたので、大変勉強になりました。ありがとうございました。
No.6
- 回答日時:
いや, while の条件では
fgets(buff, sizeof buff, fp)
としてるのに標準入力から読み込むときに
fgets(str, 256, stdin);
としてるのがバランス悪いなと思ったんだけど.... 「入力する文字数を254文字以内と制限しているため」だとしても, sizeof を使わない理由にはならないんじゃない?
そして 2つの fgets で指定した長さが違うので, 「長い行」があると「同じもの」が見付けられないという不都合が生じてしまう.
この回答への補足
何度も回答いただきありがとうございます。
テキストファイルの1行は区分と内容という形になっており、
(例:hito 日本人)
区分は数字で指定し、内容をコマンドから入力するようにしています。
そのため、データ1行分を格納するbuff[512]と入力した内容を格納するstr[256]は長さが異なっています。
実際のソースコードでは、入力した区分と内容をセットにして別の変数に格納したものとbuffを比較して重複がないか調べていますが、strのままのほうがわかりやすいと思い、このように記述しました。
sizeof を使わない理由についてですが、本に載っているソースコードをほぼそのまま載せているため、前回の回答の通り「入力する文字数を254文字以内と制限しているため」とお答えするしかないのですが、
fgets(str, sizeof str, stdin); としたほうがバランスがいいのでしょうか。
ちなみに、変数宣言のところでchar str[256];としているため、
改めてfgets(str, 256, stdin); と読み込み文字数を256にする必要はないのでしょうか?
No.5
- 回答日時:
>data_c++ はdata_cに1ずつ足していく、
>++data_cはdata_cを足していく、ということでよろしいでしょうか。
違います。
data_c++ はdata_c を渡してから+1
+data_c は+1してから data_c を渡します。
data_cが2のとき、
data[data_c++] はdata[2]を、
data[++data_c] はdata[3]を渡します。渡したあと+1するのは同じです。
失礼いたしました。
回答NO.2の補足コメントを書き込んだ後で調べたところ、
間違っていることに気がつき、御礼コメントの欄で訂正させていただきました。
インクリメント演算子に前置きと後置きがあることを教えていただき、
大変勉強になりました。
何度も回答いただきましてありがとうございました。
No.4
- 回答日時:
あ, バグがいた.
char datafile[];= "sample.txt";
はおかしい. まあタイポだろうけど.
あとねんのため確認ですが, fgets が必ずしも
「1行読み込む」とは限らない
というのは大丈夫でしょうか?
ちなみにですが
fgets(str, 256, stdin);
のところ, sizeof を使わないのはなぜ?
この回答への補足
回答いただきありがとうございます。
char datafile[];= "sample.txt"; は、
char datafile[]= "sample.txt"; でした。失礼いたしました。
fgetsは、この場合入力された文字が255文字までをstr変数に格納するということですよね?(改行文字が現れたらその時点で読み込み終了)
fgets(str, 256, stdin);
のところで"sizeof"を使わないのは、上では記述していないんですが、入力する文字数を254文字以内と制限しているためだと思います。
No.3
- 回答日時:
動きがわかりにくい場合は、
strcpy(data[data_c++], buff);
を
strcpy(data[data_c], buff);
data_c++;
のように分解するのはとても良いです。
無暗に式の評価順に依存する書き方をしないようにしましょう。
データ数 N の配列について、値が入っているのは [0] ~ [N-1] で、for ループを回すときは (i = 0; i < N; i++) と書く、のは極めて一般的なイディオムなので覚えましょう。
> 配列0は使わずに、1から5にデータを入れて個数は5にしておく
このような一般的でない書き方は、余程の理由がない限り絶対にやってはいけません。バグの温床になります。
回答いただきありがとうございます。
strcpy(data[data_c++], buff);のところで、
data_c++は2回目にループしたところでdata_cに1を加えると思ったため、
わけがわからず質問させていただいたところ、
strcpy(data[data_c], buff);
data_c++;
と分解できることを知り、大変勉強になりました。
配列の添え字にインクリメント演算子を使うのは分かりずらいですね。
配列につきましては、回答NO.2様の御礼コメントにかかせていただきましたが、配列は配列[0]からデータを入れていかなければならないと思っておりましたが、やはりそうしたほうがいいのですね。
この場合、読み込んだデータの配列の添え字にデータの個数を使用していたためわかりにくくなっていたのですが、上記のように2文に分解すると理解することができました。配列に関するご指摘も重ねてありがとうございました。
No.2
- 回答日時:
>strcpy(data[4], buff); となり、
data_c++; の行で 4+1=5 となるということでよろしいでしょうか。
そうです。
data_c++ と ++data_c が違うことも頭に置きましょう。
データが0から4に入り、個数が5となっている。これはパッと見、わかりにくいです。
ので、配列0は使わずに、1から5にデータを入れて個数は5にしておくのが良いかと思います。
この回答への補足
何度も回答いただきありがとうございます。
>data_c++ と ++data_c が違うことも頭に置きましょう。
data_c++ はdata_cに1ずつ足していく、
++data_cはdata_cを足していく、ということでよろしいでしょうか。
すみません、上の補足コメントは間違っておりました。
data_c++は最後にインクリメント演算子の処理を行い、
++data_cは最初にインクリメント演算子の処理を行うということだったのですね。
++data_cは初めて見た形でしたので、教えていただき、勉強になりました。
配列に関しては、配列[0]からデータを入れていかなければならないと思っていましたが、この場合は配列[1]から入れていくほうがわかりやすいですね。
何度も解答いただきまして、ありがとうございました。
No.1
- 回答日時:
ややこしいソースですね。
data[data_c] = (char*)malloc(strlen(buff) + 1); data[0]にアドレスを格納
strcpy(data[data_c++], buff); data[0]にデータを格納したあと+1
ので、5行の場合は0から4にデータが入っててdata_cは5になっています。
この回答への補足
strcpy(data[data_c++], buff);の行は
strcpy(data[data_c], buff);
data_c++;
と書き換えられ、5行目を読み込んだ時
strcpy(data[4], buff); となり、
data_c++; の行で 4+1=5 となるということでよろしいでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# pythonのファイルの並びでの読み込みとリストについて 4 2022/04/13 03:52
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# C#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04:47
- その他(プログラミング・Web制作) Python - Excel で Webからデータを連続取得したいのですが エラーが出ます 1 2023/07/06 20:08
- Java 動かなくなったのでJavaソースを手直しお願いします。 2 2022/04/30 05:35
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ポインター引数の関数でコンパ...
-
pythonでDBのカラム名で取得し...
-
構造体のソートの方法について...
-
POSTで配列のデータを渡す方法は?
-
stable diffusionのエラー
-
C言語 構造体の名前欄?を小文...
-
C言語の構造体にてバブルソート...
-
テキストファイルの結合について
-
関数マクロの書き方
-
printfの%eで指数部分の桁数を...
-
linuxのシェルでファイル名に先...
-
matlabでのRRI検出
-
'dataType' 引数を Null にする...
-
ループ変数の変更
-
c言語での wavファイルの編集(...
-
平滑化フィルタ
-
UTF-8で5~6バイトになる文字コ...
-
10Mバイトて文字数に すると何...
-
COBOLのCOMP形式について
-
Excel VBA メール作成について ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ポインター引数の関数でコンパ...
-
stable diffusionのエラー
-
printfの%eで指数部分の桁数を...
-
エクセルVBA:日付データの変換...
-
int型(2バイト)データの分割
-
c言語の多次元配列で1から100ま...
-
C#でのswitch文
-
【Excel VBA】10進数を2進数に...
-
途中経過も表示するプログラム
-
CreateProcessでの環境変数の設...
-
pythonでDBのカラム名で取得し...
-
C言語についてです! 同じ年の...
-
linuxのシェルでファイル名に先...
-
c言語 配列から数字だけをint型...
-
c言語での wavファイルの編集(...
-
matlabのソースコードをpython...
-
ビットデータのチェック方法
-
10個の実数に対する降順ソート...
-
python 気象データの取得
-
C言語の構造体にてバブルソート...
おすすめ情報