ページ 1 / 1
コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 15:31
by KOMU
以下のプログラムは、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;
}
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 15:46
by みけCAT
コード:
#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に渡し、そのファイルに対して操作を行えばいいです。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 15:49
by みけCAT
ファイルを開いて読み込む例です。
コード:
#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;
}
【追記】今回ファイル操作が必要なのは保存なので、この例は必要なかったです。ごめんなさい。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 15:52
by みけCAT
ファイルに書き込む例です。
コード:
#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;
}
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 17:44
by KOMU
回答有難う御座います。いろいろいじくってみたんですが以下のような構文で間違い無いでしょうか?
もし修正したほうがいいところとかあったらアドバイスください。
コード:
#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;
}
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:03
by みけCAT
コンパイルエラーが出ました。
コード:
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変数が使用されていないようです。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:05
by みけCAT
標準ライブラリのgets関数は、バッファオーバーランのリスクがあり危険とみなされるので、使用を避けるべきです。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:06
by みけCAT
入力するべき内容や入力の間違いを知らせるメッセージをファイルに出力し、画面には出力しないのは、
本来の目的から外れ、ユーザーに不親切になるのではないでしょうか?
【追記】
KOMU さんが書きました:これの出力結果を、テキストファイルに出力し、保存する際にファイル名をコマンドラインから任意で指定できるようにしたいです。
よく考えると、この問題に対しては「正しい」答案であり、「いい」実装ですね。
このソースコードをオンラインジャッジに提出するなら、(他に間違いがなければ)Acceptedが得られるでしょう。
ただし、実際に人間が利用すると仮定すると、仕様が不自然に感じます。
【さらに追記】
この一連のツイートと似た状況かもしれません。
https://twitter.com/Air_Hold/status/436275888594755584
(紛争回避のため注:2014年2月23日 18自13分(JST)現在、
公式webで自分が使用しているWindows Vista上のFirefox 27.0.1から閲覧すると、
「一連のツイート」が表示されました。)
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:16
by みけCAT
for文の継続条件判定で毎回strlen関数を呼び出すのは、
(今回の場合は)無駄な計算が生じる可能性があり、あまりよくないと感じます。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:21
by みけCAT
read_score関数で、オーバーフローするかのチェックを入れた方がいいと思います。
(今回は点数の範囲を100点までに限定しているので、int型が少なくとも0以上9999以下の整数を正しく格納できる環境なら
(int型が2バイト以上かつ1バイトが8ビットである環境は、これを満たす可能性が高いと考えられる)
単純に4桁以上ならオーバーフローとみなしてよい)
オフトピック
下手なことを書くと名前がアルファベット4文字の怖い人に攻撃されそうで怖いです。
そしてこのofftopicもその「下手なこと」に入りそうです。
((((;゚Д゚))))ガクガクブルブル
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:34
by みけCAT
入力として空のファイルを読み込ませたとき、
The C89 Draftによると、
gets関数で1文字も読み込まれずにEOFがきた場合は(この場合に引数で渡している)変数chsの中身は書き換えられないため、
中身は未定義になり、read_score関数内で無限ループが発生し、巨大なファイルが出力される恐れがあります。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 18:59
by みけCAT
そもそも、main関数内で2科目分のテストの点数なのに3で割っており、誤った値を返しそうに見えます。
その上の文字列「3科目分入力せよ」も不自然です。
mean /= 3.0;というようにマジックナンバーを使用するのではなく、
mean /= KAM;というように定数を利用する方がいいと思います。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 19:41
by KOMU
たくさんのご指摘ありがとうございました。自分なりに咀嚼してみます。
現時点で、以下のコードを実行するとパラメータの数が違いますと出て先に進めない(点数の入力ができない)んですがなにが原因だと考えられますか?何度もごめんなさい
コード:
#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;
}
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 19:44
by みけCAT
パラメータの数が違うことが原因だと考えられます。
実行したコマンドを教えてください。
ちなみに、コンパイルは通りましたが、警告が出ました。
コード:
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関数の書式または引数が不自然なようですね。
Re: コマンドラインを使ったファイル入出力について
Posted: 2014年2月23日(日) 20:00
by KOMU
大変申し訳ありません。自分がコマンドプロンプトの使い方間違ってただけでした。
くだらないこと何回もしつこく聞いてごめんなさい。一応「解決」とさせていただきます。
みけCATさんありがとうございました&ごめんなさい