コマンドラインを使ったファイル入出力について

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

コマンドラインを使ったファイル入出力について

#1

投稿記事 by KOMU » 12年前

以下のプログラムは、3人の2科目分のテストの点数をキーボードから入力し、優秀者と優秀者の平均点を表示させるプログラムです。
これの出力結果を、テキストファイルに出力し、保存する際にファイル名をコマンドラインから任意で指定できるようにしたいです。
参考にさせていただきたいのでどなたか例を示していただけますでしょうか?

コード:

#include <stdio.h>
#include <stdlib.h>

#define NUM 3
#define NCH 30
#define KAM 2

int read_score() {
	int	i;
	char chs[NCH];	
	int is_number;
	int score;
	do{
		gets(chs);
		is_number = 1;
		for (i = 0; i < strlen(chs); i++) {
			if (!isdigit(chs[i])) {
				printf("0から100までの整数を入力してください\n");
				is_number = 0;
				break;

			}
		}
		if (is_number == 1) {
			score = atoi(chs);
		}
		else {
			score = -1;
		}
	} while (score < 0 || score > 100);
	return score;
}

int main(void)
{
	int i;
	int j;
	double subject[KAM];	
	double mean[NUM];
	int score;
	int person_no;
	double best = -1.0;

	printf("テストの点数を入力せよ\n");
	printf("3科目分入力せよ\n");

	for (i = 0; i < NUM; i++) {
		printf("%d人目\n", i + 1);
		mean[i] = 0.0;
		for (j = 0; j < KAM; j++) {
			score = read_score();
			mean[i] += (double)score;
		}
		mean[i] /= 3.0;
		if (mean[i] >= best) {
			person_no = i;
			best = mean[i];
		}
	}
	printf("優秀者は%d人目\n", person_no + 1);
	printf("優秀者の平均点は %.2fでした\n", best);

	return 0;
}


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

Re: コマンドラインを使ったファイル入出力について

#2

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

コード:

#include <stdio.h>

int main(int argc,char* argv[]) {
    int i;
    for(i=0;i<argc;i++)printf("argv[%d] : %s\n",i,argv[i]);
    return 0;
}
このコードを実行するとわかるように、
argv配列のi番目にコマンドライン引数のi番目が入る(0番目は「普通」実行中のファイル名)ので、
そのパラメータをfopenに渡し、そのファイルに対して操作を行えばいいです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#3

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

ファイルを開いて読み込む例です。

コード:

#include <stdio.h>

/* fgetsをしたあと、最後の改行を外す */
char* fgets2(char* buf,size_t size,FILE* fp) {
	char* ret;
	if(buf==NULL)return NULL;
	ret=fgets(buf,size,fp);
	if(ret==NULL)return NULL;
	for(;*ret!='\0';ret++) {
		if(*ret=='\n') {
			*ret='\0';
			break;
		}
	}
	return buf;
}

int main(void) {
	FILE* fp;
	char buf[1024]={};
	fp=fopen("test.txt","r");
	if(fp==NULL)return 1;
	fgets2(buf,sizeof(buf),fp);
	fclose(buf);
	puts(buf);
	return 0;
}
【追記】今回ファイル操作が必要なのは保存なので、この例は必要なかったです。ごめんなさい。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#4

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

ファイルに書き込む例です。

コード:

#include <stdio.h>

int main(void) {
	FILE* fp;
	fp=fopen("test.txt","w");
	if(fp==NULL)return 1;
	fprintf(fp,"%d + %d = %d\n",1,1,1+1);
	fclose(fp);
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

KOMU

Re: コマンドラインを使ったファイル入出力について

#5

投稿記事 by KOMU » 12年前

回答有難う御座います。いろいろいじくってみたんですが以下のような構文で間違い無いでしょうか?
もし修正したほうがいいところとかあったらアドバイスください。

コード:


#include <stdio.h>
#include <stdlib.h>

#define NUM 3
#define NCH 30
#define KAM 2

int read_score(FILE *fp)
{
	int i;
	char chs[NCH];
	int is_number;
	int score;

	do{
		gets(chs);
		is_number = 1;
		for (i = 0; i < strlen(chs); i++) {
			if (!isdigit(chs[i])) {
				fprintf(fp, "%c\n", chs[i]);
				fprintf(fp, "0から100までの整数を入力してください\n");
				is_number = 0;
				break;

			}
		}
		if (is_number == 1) {
			score = atoi(chs);
		}
		else {
			score = -1;
		}
	} while (score < 0 || score > 100);
	return score;
}

int main(int argc, char *argv[])
{
	int i;
	int j;
	double subject[KAM];
	double mean[NUM];
	int score;
	int person_no;
	double best = -1.0;
	char *filename;
	FILE *fp;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s filename\n", argv[0]);
		exit(1);

	}
	filename = argv[1];

	fp = fopen(filename, "w");
	if (fp == NULL) {
		fprintf(stderr, "Can't open a file: %s\n", filename);
		exit(1);
	}

	fprintf(fp, "テストの点数を入力せよ\n");
	fprintf(fp, "3科目分入力せよ\n");

	for (i = 0; i < NUM; i++) {
		fprintf(fp, "%d人目\n", i + 1);
		mean[i] = 0.0;
		for (j = 0; j < KAM; j++) {
			score = read_score(fp);
			mean[i] += (double)score;
			fprintf(fp, "%d\n", score);

		}
		mean[i] /= 3.0;
		if (mean[i] >= best) {
			person_no = i;
			best = mean[i];

		}

	}

	fprintf(fp, "成績優秀者は%d人目です\n", person_no + 1);
	fprintf(fp, "平均点は %.2fです\n", best);
	fclose(fp);
	return 0;
}


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

Re: コマンドラインを使ったファイル入出力について

#6

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

コンパイルエラーが出ました。

コード:

YUKI.N>g++ -Wall -Wextra raw.cpp -o raw.exe
raw.cpp: In function 'int read_score(FILE*)':
raw.cpp:18:29: error: 'strlen' was not declared in this scope
   for (i = 0; i < strlen(chs); i++) {
                             ^
raw.cpp:19:23: error: 'isdigit' was not declared in this scope
    if (!isdigit(chs[i])) {
                       ^
raw.cpp: In function 'int main(int, char**)':
raw.cpp:41:9: warning: unused variable 'subject' [-Wunused-variable]
  double subject[KAM];
         ^

YUKI.N>
標準のstrlenを使うにはstring.hを、isdigitを使うにはctype.hをそれぞれインクルードするべきです。
また、subject変数が使用されていないようです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#7

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

標準ライブラリのgets関数は、バッファオーバーランのリスクがあり危険とみなされるので、使用を避けるべきです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#8

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

入力するべき内容や入力の間違いを知らせるメッセージをファイルに出力し、画面には出力しないのは、
本来の目的から外れ、ユーザーに不親切になるのではないでしょうか?

【追記】
KOMU さんが書きました:これの出力結果を、テキストファイルに出力し、保存する際にファイル名をコマンドラインから任意で指定できるようにしたいです。
よく考えると、この問題に対しては「正しい」答案であり、「いい」実装ですね。
このソースコードをオンラインジャッジに提出するなら、(他に間違いがなければ)Acceptedが得られるでしょう。
ただし、実際に人間が利用すると仮定すると、仕様が不自然に感じます。

【さらに追記】
この一連のツイートと似た状況かもしれません。
https://twitter.com/Air_Hold/status/436275888594755584
(紛争回避のため注:2014年2月23日 18自13分(JST)現在、
公式webで自分が使用しているWindows Vista上のFirefox 27.0.1から閲覧すると、
「一連のツイート」が表示されました。)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#9

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

for文の継続条件判定で毎回strlen関数を呼び出すのは、
(今回の場合は)無駄な計算が生じる可能性があり、あまりよくないと感じます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#10

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

read_score関数で、オーバーフローするかのチェックを入れた方がいいと思います。
(今回は点数の範囲を100点までに限定しているので、int型が少なくとも0以上9999以下の整数を正しく格納できる環境なら
(int型が2バイト以上かつ1バイトが8ビットである環境は、これを満たす可能性が高いと考えられる)
単純に4桁以上ならオーバーフローとみなしてよい)
オフトピック
下手なことを書くと名前がアルファベット4文字の怖い人に攻撃されそうで怖いです。
そしてこのofftopicもその「下手なこと」に入りそうです。
((((;゚Д゚))))ガクガクブルブル
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#11

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

入力として空のファイルを読み込ませたとき、
The C89 Draftによると、
gets関数で1文字も読み込まれずにEOFがきた場合は(この場合に引数で渡している)変数chsの中身は書き換えられないため、
中身は未定義になり、read_score関数内で無限ループが発生し、巨大なファイルが出力される恐れがあります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: コマンドラインを使ったファイル入出力について

#12

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

そもそも、main関数内で2科目分のテストの点数なのに3で割っており、誤った値を返しそうに見えます。
その上の文字列「3科目分入力せよ」も不自然です。
mean /= 3.0;というようにマジックナンバーを使用するのではなく、
mean /= KAM;というように定数を利用する方がいいと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

KOMU

Re: コマンドラインを使ったファイル入出力について

#13

投稿記事 by KOMU » 12年前

たくさんのご指摘ありがとうございました。自分なりに咀嚼してみます。
現時点で、以下のコードを実行するとパラメータの数が違いますと出て先に進めない(点数の入力ができない)んですがなにが原因だと考えられますか?何度もごめんなさい

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define NUM 3
#define NCH 30
#define KAM 2

int read_score(FILE *fp)
{
	int i;
	char chs[NCH];
	int is_number;
	int score;

	do{
		gets(chs);
		is_number = 1;
		for (i = 0; i < strlen(chs); i++) {
			if (!isdigit(chs[i])) {
				fprintf(fp, "%c\n", chs[i]);
				fprintf(fp, "0から100までの整数を入力してください\n");
				is_number = 0;
				break;

			}
		}
		if (is_number == 1) {
			score = atoi(chs);
		}
		else {
			score = -1;
		}
	} while (score < 0 || score > 100);
	return score;
}

int main(int argc, char *argv[])
{
	int i;
	int j;
	double subject[KAM];
	double mean[NUM];
	int score;
	int person_no;
	double best = -1.0;
	char *filename;
	FILE *fp;

	if (argc != 2) {
		fprintf(stderr, "パラメータの数が違います\n", argv[0]);
		exit(1);
	}
	filename = argv[1];

	fp = fopen(filename, "w");
	if (fp == NULL) {
		fprintf(stderr, "ファイルをオープンできませんでした\n", filename);
		exit(1);
	}

	fprintf(fp, "テストの点数を入力せよ\n");
	fprintf(fp, "2科目分入力せよ\n");

	for (i = 0; i < NUM; i++) {
		fprintf(fp, "%d人目\n", i + 1);
		mean[i] = 0.0;
		for (j = 0; j < KAM; j++) {
			score = read_score(fp);
			mean[i] += (double)score;
			fprintf(fp, "%d\n", score);

		}
		mean[i] /= KAM;
		if (mean[i] >= best) {
			person_no = i;
			best = mean[i];
		}
	}

	fprintf(fp, "成績優秀者は%d人目です\n", person_no + 1);
	fprintf(fp, "平均点は %.2fです\n", best);
	fclose(fp);
	return 0;
}




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

Re: コマンドラインを使ったファイル入出力について

#14

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

パラメータの数が違うことが原因だと考えられます。
実行したコマンドを教えてください。

ちなみに、コンパイルは通りましたが、警告が出ました。

コード:

YUKI.N>g++ -Wall -Wextra -o raw2.exe raw2.cpp
raw2.cpp: In function 'int read_score(FILE*)':
raw2.cpp:20:29: warning: comparison between signed and unsigned integer expressi
ons [-Wsign-compare]
   for (i = 0; i < strlen(chs); i++) {
                             ^
raw2.cpp: In function 'int main(int, char**)':
raw2.cpp:52:56: warning: too many arguments for format [-Wformat-extra-args]
   fprintf(stderr, "パラメータの数が違います\n", argv[0]);
                                                        ^
raw2.cpp:59:67: warning: too many arguments for format [-Wformat-extra-args]
   fprintf(stderr, "ファイルをオープンできませんでした\n", filename);
                                                                   ^
raw2.cpp:43:9: warning: unused variable 'subject' [-Wunused-variable]
  double subject[KAM];
         ^

YUKI.N>
fprintf関数の書式または引数が不自然なようですね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

KOMU

Re: コマンドラインを使ったファイル入出力について

#15

投稿記事 by KOMU » 12年前

大変申し訳ありません。自分がコマンドプロンプトの使い方間違ってただけでした。
くだらないこと何回もしつこく聞いてごめんなさい。一応「解決」とさせていただきます。
みけCATさんありがとうございました&ごめんなさい

閉鎖

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