catコマンドの自作

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

catコマンドの自作

#1

投稿記事 by たかし » 12年前

大学の課題でcatコマンドの自作を出されました。

実装させる機能は
1.指定されたファイル内容を順番につなげて標準出力に出力する。
  ファイル名が1つも指定されない場合は、標準入力から入力する。

2.オプションは -n, -h, -t の3種類とし、オプションの指定の順序に関係なく自由に組み合わせることが可能とする。
  -nオプション: 各行の先頭に行番号を表示。
  -hオプション: 出力開始行の指定。たとえば -h3 で3行目から出力する。
  -tオプション: 出力終了行の指定。たとえば -t5 で5行目まで表示する。

※文字列の比較には以下に示すstrncmp関数を使うこと。

  int strncmp(const char *s1, const char *s2, size_t n)
  2つの文字列s1とs2で、最初のn文字だけを較べる。この関数は、s1がs2に較べて1)小さい、2)同じ、3)大きい場合に、
  0よりも1)小さい、2)同じ、3)大きい整数を返す。

また、文字列から整数値を得るために、以下の関数を利用してもよい。

  int atoi(const char *nptr)
  atoi関数は,notrによって支持される文字列の初めの部分をint型整数に変換する。

3.エラー処理をすること。少なくとも、オプションが正しくない場合に、オプションの指定を知らせるようにする。

というものです。
そして以下に書いたのが自分で書いたプログラムです。

コード:

#include <stdio.h>
#define BUFSIZE 1000


main(int argc, char *argv[])
{
	char buf[BUFSIZE];
	char *prog = argv[0];
	int line=1;
	
	FILE *fp = stdin;
	int i;
	
	for(i=1; i<=argc; i++){
		
		if(argc > 1 ){
			if((fp = fopen(argv[i],"r")) == NULL){
				fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
				exit(1);
			}
		}
		if(*fp == '-'){
				switch(*fp++){
					case 'n':
					while( fgets (buf, BUFSIZE, fp)!=NULL){
			
					printf("%d",line);
					fputs(buf,stdout);
					line++;
					}
					case 'h':
					*nptr==fp++;
					
					atoi(const char *nptr)
					while( fgets (buf, BUFSIZE, fp)!=NULL){
					fputs(buf,stdout);
					
					}

			}
			
		}
		if( fp != stdin){
			fclose( fp );
		}
	}
}
とりあえず機能1の指定されたファイルの内容を順番につなげて標準出力させることはできました。
機能2を実装させようと書いているうちの自分が何を書いているのかわけがわからなくなってしまいました。
自分ではまず*fpの指している文字が - のとき*fpが指している次の文字*fp++ をswich文で分類しようと思ったのですが、
これだとfor文で2週目にきたときに肝心のファイルを読み込んだときにfp*が - を指していなければ何も実行されずに終わってしまいます。
どうしたらよいのかまったく思いつかないので、
どうすれば -n -h -t を判別できるか、どうすれば 出力の開始(終了)位置を決められるか、などの考え方を教えていただけないでしょうか?

課題の提出期限が来週の火曜なのでこのGW中に終わらせないとまずいのであせっております。
何卒よろしくお願いします。

OS:windows7
コンパイルと実行に使っているソフト:Cygwin
最後に編集したユーザー たかし on 2013年5月04日(土) 16:33 [ 編集 1 回目 ]

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

Re: catコマンドの自作

#2

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

オプションはファイルの中ではなく、コマンドラインで渡されるのではないですか?
あと、ファイルポインタ変数を++するのは意味がないと思います。fgetsなどでファイルを読み込めばファイルポインタは勝手に進みます。
意図的にファイルポインタの位置を変更するには、fseek関数を使います。(必要ないと思いますが)

操作の手順としては、まず最初にコマンドライン引数を一通り見てオプションを探し、
その後コマンドライン引数をもう一度最初から見てファイルの処理をするべきだと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ただの屍のようだ

Re: catコマンドの自作

#3

投稿記事 by ただの屍のようだ » 12年前

コード読んだ感想をいうと『?』ですね。
何ステップに分けて考えましょう。
ステップ1 cmd引数で指定されたファイルを出力
ステップ2 オプションを一個ずつ実装してみる
ステップ3 cmd引数なしのとき、stdinで入力された文字列を配列に格納する。
ステップ4 配列を解析してみる
*気になったこと:
変数progって本当に必要ですか?
FILE*ってファイルにアクセスするためのポインタでしかないと認識してましたが?
main()はintの戻り値を返す必要があるはずですが?
全部mainに詰め込むとバグ探しでストレスたまると思いますが?

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: catコマンドの自作

#4

投稿記事 by ISLe » 12年前

cat -h2 foo.txt -h3 bar.txt
ってコマンドラインだと
foo.txtは2行目から、bar.txtは3行目から出力するのでは?
だとすると頭から順番に解析していくことになるけど、オプションの誤りを事前にチェックするとなると…。
なにげに難易度高いですよね。
そんな深く考えずに出された問題でしょうけど。

ただの屍のようだ

Re: catコマンドの自作

#5

投稿記事 by ただの屍のようだ » 12年前

みけねこさんの指摘しているやり方とまったくかみ合ってなくて質問者が混乱すると思いました。
とりあえず自分の目指すcatを説明します。
例:cat ファイル1 -n -h4 -t15 ファイル2 -n -t3 ファイル3 -t4 -h2
意味:ファイル1を行番号表示させながら、4-15行を出力して、ファイル2を行番号表示させながら3行目まで出力し、ファイル3を2-4行目まで出力します。
構文チェックをしっかりやれば案外簡単です。が
簡単そうにみえて割とハードル高いです。これができたら先生にほめられると思います。

最初のステップをうまく作れば、あとは配列から単語を切り出してポインタの配列に格納してargvと同様に渡すだけなので、最初が肝心です。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: catコマンドの自作

#6

投稿記事 by h2so5 » 12年前

おそらく h, t オプションは結合後の出力に対して適用されるのではないかと思います。
入力ごとに h, t を指定するのは流石にちょっと複雑ですから。
オフトピック
実際に使うとなると、head & tail で代用できる?

たかし
記事: 48
登録日時: 12年前

初歩的な質問なのですが・・・

#7

投稿記事 by たかし » 12年前

皆さんたくさん回答を下さってありがとうございます。
まだできていないのですが、ちょっと質問させてください。

./a.out -n sample.txt

とcmdで入力したとします。

その場合
argv[0] = "./a.out"
argv[1] = "-n"
argv[2] = "sample.txt"

ということになると思うのですが、fopenでテキストファイルでないargv[1] = "-n"を読み込んだ場合
ファイルポインタfpには何が入るのですか?

また、argv[2] = "saple.txt"を読み込んだ場合ファイルポインタfpが指しているのはsample.txtに
書いてある文章の一文字目のアドレスを指しているという認識で間違っていませんか?

fp = fopen(argv,"r")

初歩的な質問ですみません。
よろしくお願いします。

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

Re: 初歩的な質問なのですが・・・

#8

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

playhuman さんが書きました:fopenでテキストファイルでないargv[1] = "-n"を読み込んだ場合
ファイルポインタfpには何が入るのですか?
ファイル"-n"が存在すればfpにはそのファイルを開いたFILE構造体へのポインタが入ります。
ファイルの中身がテキストかバイナリかは関係ありません。
ファイル"-n"が存在しなければNULLが入ります。
playhuman さんが書きました:また、argv[2] = "saple.txt"を読み込んだ場合ファイルポインタfpが指しているのはsample.txtに
書いてある文章の一文字目のアドレスを指しているという認識で間違っていませんか?
typoには目をつむるとして、間違っています。
fpが指しているのはsample.txtを開いたFILE構造体へのポインタであり、ファイルの中身のデータは関係ありません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

usao

Re: catコマンドの自作

#9

投稿記事 by usao » 12年前

とりあえず各オプションとファイル名の並びのルールを
明確にしたほうがよいのでは? (課題の文章がそれだけなら,自分で決めてしまってもよい??)

引数が
ファイル名1 -t ファイル名2
だった場合,-tはどっちのファイルへの指定なのか.

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#10

投稿記事 by たかし » 12年前

みけCAT様、ご指南ありがとうございます。
また、usao様が言われたように、オプションとファイルの並びのルールを明確化させるべきでした。
混乱させてしまい申し訳ございません。

入力は以下のとおりです。

./a.out 《オプションの列》 《ファイル名の列》

です。
例を挙げると

./a.out -h3 -t5 -n Sample1.txt Sample2.txt

という感じです。
オプションの入力はファイルごとでなく最初に1回やるだけです。

あとちゃんと出力できなかったのですが、新たにプログラムを作ってみました。

コード:

#include <stdio.h>
#define BUFSIZE 1000


main(int argc, char *argv[])
{
	char buf[BUFSIZE];
	char *prog = argv[0];
	int line=1;
	
	FILE *fp = stdin;
	int i;
	
	
	if(*argv[1]==45){
			argv[1]++;
			switch(*argv[1]){
				case 110:
				printf("%d",line);
				line++;
			}
	}
	if(*argv[2]==45){
			argv[2]++;
			switch(*argv[1]){
				case 110:
				printf("%d",line);
				line++;
			}
	}
	if(*argv[3]==45){
			argv[3]++;
			switch(*argv[1]){
				case 110:
				printf("%d",line);
				line++;
			}
	}
	else{
		
		for(i=1; i<=argc; i++){
			
			if(argc > 1 ){
				if((fp = fopen(argv[i],"r")) == NULL){
					fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
					exit(1);
				}
			}
		
			while( fgets (buf, BUFSIZE, fp)!=NULL){
				fputs(buf,stdout);
			}
			if( fp != stdin){
			fclose( fp );
			}
		}
	}
}	
実行結果は以下のとおりになってしまいます。
$ ./a.out -n pen.txt
Segmentation fault

私の考えでは
オプションがくるのはファイル名より先なので、argv[1]かargv[2]かarg[3]
のどれかということになりますよね?
だから*argv[1~3]の指している文字が -(文字コード:45) の場合、
argv[1~3]のポインタを進めて次に指している文字が n か h か t
でswich文で変えられるようにしました。
今回はまだ n しか考えていないのですが、もし n だったら、int型のline、つまり行番号をを出力するというものです。

そしてargv[1~3]がオプションでなかった場合はファイルを読み込んで出力して閉じるようにしました。

何故セグメントエラーが起こってしまったのでしょうか?(コンパイルは通ったのですが・・・)
よろしくお願いします。

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

Re: catコマンドの自作

#11

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

$ ./a.out -n pen.txt
というコマンドが渡されたとき、argc==3であり、argv[3]は未定義です。
この状態でargv[3]が指すポインタにアクセスするとSegmentation faultとなる可能性があります。
for文のi<=argcのところも、配列の範囲外にアクセスしてSegmentation faultの原因となります。
switchが全てswitch(*argv[1])となっているのは大丈夫ですか?

また、45とか110とかいうマジックナンバーはわかりにくいです。'-'とすると-の文字コードになります。

ファイルの中身を出力するときに行番号を出力していません。(未完成ですよね)
if(argc>1)だけでは、オプションが指定された時に、ファイルが指定されていないのかどうかを正しく判定できません。
argv[3]がオプションでない場合のみファイルの操作をしています。逆に言うとargv[3]がオプションの時は、ファイルの操作は行われません。
ファイルの操作で、オプションの分のコマンドライン引数もファイル名として処理をしています。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#12

投稿記事 by たかし » 12年前

勝手にコメントを編集してしまって皆様を混乱させてしまったようです。
softya(ソフト屋)様の指示通り以前の質問を再び書かせていただきます。
申し訳ありませんでした。


「みけCAT様にご指摘いただいた部分を何箇所か直して新しいプログラムを書いてみました。

コード:

#include <stdio.h>
#define BUFSIZE 1000

int atoi(const char *nptr){
	int num = 0;
	int type = 0;
	
	if(*nptr == '-'){
		type = 1;
		nptr++;
	}

	
	while(*nptr != '\0'){
			if(*nptr>=97 && *nptr<=102){
				num = num + *nptr-87;
				num = num *16;
				nptr++;
			}
			
			else if(*nptr>=65 && *nptr<=70){
				num= num + *nptr - 55;
				num = num *16;
				nptr++;
			}

			else{
				num = num + *nptr -48;
				num = num * 16;
				nptr++;
			}


	}
	
	num=num/16;

	
	if(type){
		num = 0 - num;
	}
	return num;
}

main(int argc, char *argv[])
{
	char buf[BUFSIZE];
	char *prog = argv[0];
	int line=0;
	int start=0;
	int end=0;
	int count=1;

	
	FILE *fp = stdin;
	int i;

	for(i=1; i<argc; i++){
	

	
		if(*argv[i]=='-'){
				argv[i]++;
				switch(*argv[1]){
					case 'n':
					line=1;
					case 'h':
					argv[i]++;
					start = atoi(argv[i]);

				}
		}
		else{
			if(argc > 1 ){
				if((fp = fopen(argv[i],"r")) == NULL){
					fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
					exit(1);
				}
			}
			
			while( fgets (buf, BUFSIZE, fp)!=NULL){


				if(count>=start){
					if(line!=0){
						printf("%d",line);
						line++;
					}

					fputs(buf,stdout);
				}
				count++;

			}
			if( fp != stdin){
			fclose( fp );
			}
		}
	}
}

これで行番号の出力オプション -n の実装ができました。
次に出力開始行の指定オプション -h を考えてみたのですが、これだとすべて出力されてしまいました。

以下のように入力しました。

./a.out -n -h5 pen.txt eraser.txt

すると出力は以下のとおりです。

1This
2is
3a
4pen.
5This
6is
7a
8pen.
9This
11is
12a
13eraser.
14

開始行を決める引数startを出力して確認したところ値が0のままでした。
つまりatoi関数で問題が起きているのではないかと思います。
しかしこのatoi関数は前回の授業で私が作ったもので、そのときは問題なく正常に動いていました。
だからこのatoi関数には問題がないと思います。
原因がわかりません。
よろしくお願いします。
最後に編集したユーザー たかし on 2013年5月04日(土) 18:21 [ 編集 7 回目 ]

ただの屍のようだ

Re: catコマンドの自作

#13

投稿記事 by ただの屍のようだ » 12年前

すでに指摘された64行目のswitch文が問題です。
ついでにたとえば $ ./a.exe -n -h5 pen.txt とした場合
実行結果:
12345This
6is
7a
8pen.
9This
10is
11a
12eraser.
13

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

Re: catコマンドの自作

#14

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

64行目のswitch文とは何でしょうか?
私には58行目までしか見えません。

追記
おそらく編集により会話の流れの整合性が取れなくなっています。
確かフォーラムルール違反だったと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: catコマンドの自作

#15

投稿記事 by softya(ソフト屋) » 12年前

元の文章が不明なのですが、playhumanさんが途中で編集された回答があるため話の流れが分からなくなっているようです。
元が残らない文章改変や消すことは原則禁止しておりますので、元に戻していただきたいと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

usao

Re: catコマンドの自作

#16

投稿記事 by usao » 12年前

なんか話の流れがよくわからん状態になっていますが…
コマンドラインの解釈のあたりで躓いているのでしょうか?

とりあえず

コード:

for( int i=0; i<argc; i++ )
{
    argv[i]が何なのか{まともなオプション指定,ファイル名,それ以外}を
    判定してその結果を表示してみる
}
みたいなことからはじめてみたほうがよいかも.

たとえば,
./a.out -n -h3 file1.txt file2.txt -WildKitten
とかいう実行だったら

arg[1] = nオプション
arg[2] = hオプション : 3行目からという指定
arg[3] = ファイル名 : file1.txt
arg[4] = ファイル名 : file2.txt
arg[5] = エラー : -WildKitten

みたいなのをprintf()か何かで表示してみるとか.
これができたら,あとは,指定されたオプション内容に従ってファイル出力する部分をつくるだけ.

#オプションはファイル毎ではなくて,全体に効くのですね.
 だったら別にファイル名とオプションの順番は(決めてしまうとかえって難しくなるようなら)自由な並びでもよいのかも?

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#17

投稿記事 by たかし » 12年前

先ほどは混乱を招いてしまいすみませんでした。
完全に一言一句同じ文章に戻すことはできなかったのですが、ほぼ同じ内容のものを書かせていただきましたので、確認いただけたらと思います。

そして現在プログラムは以下のようになっております。

コード:

#include <stdio.h>
#define BUFSIZE 1000

int atoi(const char *nptr){
	int num = 0;
	int type = 0;
	
	if(*nptr == '-'){
		type = 1;
		nptr++;
	}
	
	while(*nptr != '\0'){
			if(*nptr>=97 && *nptr<=102){
				num = num + *nptr-87;
				num = num *16;
				nptr++;
			}
			
			else if(*nptr>=65 && *nptr<=70){
				num= num + *nptr - 55;
				num = num *16;
				nptr++;
			}

			else{
				num = num + *nptr -48;
				num = num * 16;
				nptr++;
			}
	}	
	num=num/16;
	
	if(type){
		num = 0 - num;
	}
	return num;
}

main(int argc, char *argv[])
{
	char buf[BUFSIZE];
	char *prog = argv[0];
	int line=0;
	int start=0;
	int end=0;
	int count=1;
	
	FILE *fp = stdin;
	int i;

	for(i=1; i<argc; i++){	
	
		if(*argv[i]=='-'){
				argv[i]++;
				switch(*argv[i]){
					case 'n':
					line=1;
					case 'h':
					argv[i]++;
					start = atoi(argv[i]);

				}
		}
		else{
			if(argc > 1 ){
				if((fp = fopen(argv[i],"r")) == NULL){
					fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
					exit(1);
				}
			}
			
			while( fgets (buf, BUFSIZE, fp)!=NULL){


				if(count>=start){
					if(line!=0){
						printf("%d",line);

					}

					fputs(buf,stdout);
				}
				count++;
				line++;
			}
			if( fp != stdin){
			fclose( fp );
			}
		}
	}
}

先ほど ただの屍のようだ様がご指摘していただいようにswitch文の中身を変更しました。
そしてプログラムを実行してみました。すると、

./a.out -n -h5 pen.txt pen.txt eraser.txt

と入力するとちゃんと

5This
6is
7a
8pen.
9This
10is
11a
12eraser.
13

というふうにちゃんと出力されたのですが、-h5 と -n を逆にして

./a.out -h5 -n pen.txt pen.txt eraser.txt

と入力すると
1This
2is
3a
4pen.
5This
6is
7a
8pen.
9This
10is
11a
12eraser.
13

になってしまい、全部出力されます。
なぜかよくわかりません。
オプションの順番が変わるだけだったらまずswitch文が実行されるので実行結果は変わらないと思ったのですが・・・
よろしくお願いします。

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

Re: catコマンドの自作

#18

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

switch文の中にbreak;が無いですが、大丈夫ですか?
また、文字コードをマジックナンバーで書く癖が(一部しか)直っていませんね。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#19

投稿記事 by たかし » 12年前

みけCAT様、ご指摘ありがとうございます。
atoi関数は前回作ったものをコピペだけだったので、マジックナンバーを直し忘れていました、すみません。
それでついに機能2の実装が完了しました。

コード:

#include <stdio.h>
#define BUFSIZE 1000

int atoi(const char *nptr){
	int num = 0;
	int type = 0;
	
	if(*nptr == '-'){
		type = 1;
		nptr++;
	}

	
	  while(*nptr != '\0'){
            if(*nptr>='a' && *nptr<='f'){
                num = num + *nptr-87;
                num = num *16;
                nptr++;
            }
            
            else if(*nptr>='A' && *nptr<='F'){
                num= num + *nptr - 55;
                num = num *16;
                nptr++;
            }
 
            else{
                num = num + *nptr -48;
                num = num * 16;
                nptr++;
            }
 

	}
	
	num=num/16;

	
	if(type){
		num = 0 - num;
	}
	return num;
}

main(int argc, char *argv[])
{
	char buf[BUFSIZE];
	char *prog = argv[0];
	int line=0;
	int start=0;
	int end=0;
	int count=1;

	
	FILE *fp = stdin;
	int i;

	for(i=1; i<argc; i++){
	

	
		if(*argv[i]=='-'){
				argv[i]++;
				switch(*argv[i]){
					case 'n':
					break;
					line=1;
					case 'h':
					argv[i]++;
					start = atoi(argv[i]);
					break;
					case 't':
					argv[i]++;
					end = atoi(argv[i]);
					break;

				}
		}
		else{
			if(argc > 1 ){
				if((fp = fopen(argv[i],"r")) == NULL){
					fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
					exit(1);
				}
			}
			
			while( fgets (buf, BUFSIZE, fp)!=NULL){


				if(count>start){
					if(line!=0){
						printf("%d",line);

					}

					fputs(buf,stdout);
					if(count==end+1){
					break;
					}
				}

				count++;
				line++;
				
				
			}
			if( fp != stdin){
			fclose( fp );
			}
		}
	}
}
	
入力
./a.out -h5 -n -t9 pen.txt pen.txt eraser.txt

出力
5is
6a
7pen.
8This
9is

しかしここで問題が...
今回の課題で
「文字列の比較には、srtcmp関数を使うこと。」(最初のコメントで書かせていただきました)
とかかれております。
私の書いたプログラムにはそれが使われておりません。
しかし文字列の比較はどこで使うのかよくわかりません。
どこで使ったらよいのか教えてほしいです。
よろしくお願いします。

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

Re: catコマンドの自作

#20

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

コード:

num = num + *nptr-87;
などの部分のマジックナンバーが残っています。

strncmp関数は

コード:

if(strncmp(argv[1],"-n",2)==0) {
    オプションとみなして処理
}
というように使用することが意図されていると思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

beatle
記事: 1281
登録日時: 13年前
住所: 埼玉
連絡を取る:

Re: catコマンドの自作

#21

投稿記事 by beatle » 12年前

playhuman さんが書きました:しかしこのatoi関数は前回の授業で私が作ったもので、そのときは問題なく正常に動いていました。
だからこのatoi関数には問題がないと思います。
atoiは自作したものを使うという指示なのでしょうか。
atoiはC言語の標準関数ですから、特に指示がなければそれを使ったほうがいいのではないかと思います。

それから、No.19のソースコードの67行目

コード:

                    line=1;
は実行されないのですが、それでいいですか?

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#22

投稿記事 by たかし » 12年前

beatle 様、ご指摘ありがとうございます。
プログラムを確認してみたらbreakが先に行われてしまっているのでline=1が実行されないわけですね。
あとatoi関数は自作ではなく普通に使うことにしました。
そしてみけCAT様に教わったstrncmp関数の使い方でプログラムを作りました。

コード:

#include <stdio.h>
#define BUFSIZE 1000

int strncmp( const char *s1, const char *s2, size_t n)
{
	int i;
	for(i=0; i<n; i++){

		if(*s1<*s2){
			
			return(-1);
		}

		else if(*s1>*s2){
			return(1);
		}

		else if(*s1==0 && *s2==0){
			break;
		}

		s1=s1+1;
		s2=s2+1;
	}
	return(0);
	
}

main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    char *prog = argv[0];
    int line=0;
    int start=0;
    int end=0;
    int count=1;
	int finish=1;
 
    
    FILE *fp = stdin;
    int i;
 
    for(i=1; i<argc; i++){
		if(strncmp(argv[i],"-",1)==0){		
		
			if(strncmp(argv[i]+1,"n",1)==0){
				line=1;
			}
			else if(strncmp(argv[i]+1,"h",1)==0){
				start = atoi(argv[i]+2);
			}
			else if(strncmp(argv[i]+1,"t",1)==0){
				end = atoi(argv[i]+2);
			}
			else{
				printf("Option Error!\n");
				break;
			}
		}
        else{
			
            if(argc > 1 ){
                if((fp = fopen(argv[i],"r")) == NULL){
                    fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
                    exit(1);
                }
            }
            
			if(start>end){
				printf("Option Error!");
				break;
			}
			if(finish){
            	while( fgets (buf, BUFSIZE, fp)!=NULL){
 
 
                	if(count>=start){
                    	if(line!=0){
                        	printf("%d",line);
 
                   	 }
 
                   	fputs(buf,stdout);
                   	 if(count==end){
							finish=0;
							break;
               		}
                }
 
                count++;
                line++;
                
            	}
            }
            if( fp != stdin){
            fclose( fp );
            }
        }
    }
}

入力1
$ ./a.exe -n -h4 -t8 pen.txt pen.txt eraser.txt
出力1
4pen.
5This
6is
7a
8pen.
8This


入力2
$ ./a.exe -3 -n -h3 pen.txt pen.txt eraser.txt
出力2
Option Error!


入力3
$ ./a.exe -n -h9 -t4 pen.txt pen.txt eraser.txt
出力3
Option Error!

入力4
$ ./a.exe -n -h4 -t4 pen.txt pen.txt eraser.txt
出力4
4pen.

オプションエラーは - が入力されたあとに n,h,t 以外の文字が入力されたときと、開始行の指定が終了行の指定よりも大きいときに出るようにしました。
また前回投稿したプログラムだと開始行と終了行が同じとき

入力
$ ./a.exe -n -h4 -t4 pen.txt pen.txt eraser.txt
出力
4pen.
4This
4This

のようになってしまっていました。
だからcount==end になったときfinishという引数に0を代入してからbreakして、for文で入力作業を行うwhile文の前に
if(finish)と書けば入力は止まるなと気づきました。

これでやっと完成しました。
皆さんのおかげです。
本当にありがとうございました。
来週も新しい課題が出されると思うので、もしかしたらまた質問させていただくかもしれませんが、そのときはまたよろしくお願いします。
あと、今回作ったプログラムで改善したほうがよい点があれば教えてください。

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

Re: catコマンドの自作

#23

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

●仕様違反

オプションがおかしい時はOption error!と表示するだけではなく、
playhuman さんが書きました:オプションが正しくない場合に、オプションの指定を知らせるようにする。
というのが課題ではなかったでしょうか?

オプション-nをつけない場合も行番号が表示され、しかも1ずれています。きちんとテストしましたか?

●その他

このプログラムだと、-ngというような変なオプションが来てもエラーになりません。

出力の行番号と行の中身の間に空白を入れたほうが見やすいと思います。

標準のatoi関数を使用するときは、stdlib.hをインクルードするべきです。
strncmp関数もatoiと同じように、string.hをインクルードして標準のものを使用するべきではないですか?

main関数の戻り値の型intは省略しないべきです。
main関数の最後にreturn 0;も入れるべきです。

コード:

while( fgets (buf, BUFSIZE, fp)!=NULL){
の近くのインデントが狂っていて、読みにくいです。

finishという変数名なのに、終わっていないときに真になっていて、わかりにくいです。

コード:

if(start>end){
    printf("Option Error!");
    break;
}
のところの出力に改行がありません。書式を使用していないのでputs関数の方がいいと思います。
また、エラー関連は標準エラー出力に出力するべきだと思います。この場合はputsは使えません。
この場合、fputsよりfprintfの方が個人的には好みです。

atoiの結果が0以下(0なら入力エラーと思われる)の場合もエラーとする方がいいと思います。

提示されたコードと出力が矛盾しています。入力1をこちらでテストすると、

コード:

4pen.
5This
6is
7a
8pen.
という正常な出力が得られました。
使用したpen.txt

コード:

This
is
a
pen.
使用したeraser.txt

コード:

This
is
a
eraser.
自作のatoi関数は16進数でしたが、標準のatoi関数は10進数です。きちんとテストしましたか?
標準関数で16進数を解釈するには、strtol関数(かsscanf関数)を使用します。
atoi関数はstrncmp関数と違って「利用してもよい」なので、利用する必要は無いと考えられます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#24

投稿記事 by たかし » 12年前

修正箇所がこんなにあるなんてびっくりしました。
みけCAT様、ありがとうございます。
ご指摘いただいた場所をいくつか直して以下のようなものを作りました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 1000

int main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    char *prog = argv[0];
    int line=0;
    int start=0;
    int end=0;
    int count=1;
	int notfinish=1;
 
    
    FILE *fp = stdin;
    int i;
 
    for(i=1; i<argc; i++){
		if(strncmp(argv[i],"-",1)==0){		
		
			if(strncmp(argv[i],"-n",3)==0){
				line=1;
			}
			
			else if(strncmp(argv[i],"-h",2)==0){
				start = atoi(argv[i]+2);
			}
			else if(strncmp(argv[i],"-t",2)==0){
				end = atoi(argv[i]+2);
			}
			else{
				printf("Option's type is -n or -h or -t .\n");
				break;
			}
		}
        else{
			
            if(argc > 1 ){
                if((fp = fopen(argv[i],"r")) == NULL){
                    fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
                    exit(1);
                }
            }
            
			if(start>end){
				fprintf(stderr,"ERROR\n");
				break;
			}
			else if(start<=0 || end<=0){
				fprintf(stderr,"ERROR\n");
				break;
			}
			if(notfinish){
            	while( fgets (buf, BUFSIZE, fp)!=NULL){
                	if(count>=start){
						if(line!=0){
                        	line=count;
							printf("%d ",line);
							line++;
						}
						fputs(buf,stdout);
						if(count==end){
							notfinish=0;
							break;
               			}
					}
 
                count++;
                
                
            	}
            }
            if( fp != stdin){
            fclose( fp );
            }
        }

    }
	return 0;
}

まず修正したのは
オプションで -ng と入力してもエラーにならない問題
これは以下のように比較する文字を3文字にして3文字目がNULL出なければelseでエラー出力するようにしました。

コード:

 if(strncmp(argv[i],"-n",3)==0){
                line=1;
}
つぎに -n を入力しなくても行番号が出力されてしまう問題。
これはline++のいちにもんだいがありましたね。
以前投稿したものだとline=0のとき1回目は行番号が出力されないけど、line++がif(line!=0)の外側にあったため、
line++されてからwhile文で戻って、2行目の出力時にif(line!=0)の条件を通ってしまって2行目から行番号が出力されてしまったようです。
1行ずれた原因もこれのせいですね。
なので以下のように書き直しました。

コード:

 
if(line!=0){
	line=count;
	printf("%d ",line);
	line++;
}
あとはエラー出力の内容と、printfをfprintfに直して、<stdlib.h>と<string.h>をincludeしました。
これで大丈夫でしょうか?
よろしくお願いします。

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

Re: catコマンドの自作

#25

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

for文の中に入っているif(argc > 1)という条件は、そもそもfor文の条件がfor(i=1; i<argc; i++)なので、
argcが1以下の時はfor文の中身は実行されず、意味が無いと思います。

細かいですが、

コード:

            if( fp != stdin){
            fclose( fp );
            }
の部分のインデントがずれています。

コード:

            if( fp != stdin){
                fclose( fp );
            }
とするべきだと思います。

startとendのチェックの時に、ただERRORだけだとわかりにくいので、
例えば"start/end line number incorrect"などのエラーメッセージを表示するべきだと思います。

コード:

printf("Option's type is -n or -h or -t .\n");
の部分も標準エラー出力に出力するべきだと思います。

オプションについて"Option's type is -n or -h or -t ."だけだとわかりにくいので、
各オプションの意味も表示したほうが親切だと思います。

このプログラムだと、開始行のみや終了行のみを指定した場合、そしてどっちも指定しない場合にERRORになってしまいます。
また、「ファイル名が1つも指定されない場合は、標準入力から入力する。」という仕様が満たされていません。
自分で各オプションの指定の組み合わせのテストをしましょう。

設定された仕様より、一度ファイル名が来たあとにオプションが来たらエラー、というチェックも入れたほうがいいと思います。
ファイル名が指定されない場合に標準入力から入力、という処理と合わせて、
ファイル名が来たかどうかのフラグを作る、という実装が考えられます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#26

投稿記事 by たかし » 12年前

みけCAT様、ご指摘ありがとうございます。
指摘していただいた部分を直して作り直しました。

ファイルかどうか判定をするcheck関数を作りました。
しかし標準入力されません。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 1000

int check(char *a)
{
	while(*a!='\0'){
		if(strncmp(a,".txt",5)==0){
			return(1);
		}
		a++;
	}
	return 0;
}

int main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    char *prog = argv[0];
    int line=0;
    int start=0;
    int end=0;
    int count=1;
	int notfinish=1;
 
    
    FILE *fp = stdin;
    int i;
 
    for(i=1; i<argc; i++){
		if(strncmp(argv[i],"-",1)==0){		
		
			if(strncmp(argv[i],"-n",3)==0){
				line=1;
			}
			
			else if(strncmp(argv[i],"-h",2)==0){
				start = atoi(argv[i]+2);
			}
			else if(strncmp(argv[i],"-t",2)==0){
				end = atoi(argv[i]+2);
			}
			else{
				fprintf(stderr,"Option's type is -n or -h or -t .\n");
				fprintf(stderr,"-n :Output line number.\n");
				fprintf(stderr,"-h :Choose start line number.\n");
				fprintf(stderr,"-t :Choose end line number.\n");
				break;
			}
		}
        
			
			if(check(argv[i])==1){
				if((fp = fopen(argv[i],"r")) == NULL){
					fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
					exit(1);
				}
			}
			
            if(start!=0 && end!=0){
				if(start>end){
					if(start<=0 || end<=0){
						fprintf(stderr,"start/end line number incorrect\n");
						break;
					}
					else{
						fprintf(stderr,"start line number is bigger than end line number.\n");
						break;
					}
				}
				else if(start<=0 || end<=0){
					fprintf(stderr,"start/end line number incorrect\n");
					break;
				}
			}
			if(*argv[i]!='-'){
				if(notfinish){
            		while( fgets (buf, BUFSIZE, fp)!=NULL){
                		if(count>=start){
							if(line!=0){
                	        	line=count;
								printf("%d ",line);
								line++;
							}
							fputs(buf,stdout);
							if(count==end){
								notfinish=0;
								break;
               				}
						}
 
            	    count++;
                
                
            		}
        	    }
    	        if( fp != stdin){
					fclose( fp );
        	    }
			}

 	   }
	return 0;
}
check関数でargvがファイルでなかった場合、fpは何も読み込まないで初期値のstdinのままのはずなので
標準入力できると思ったのですが、

./a.out
と入力すると終わってしまいます。

原因がわかりません。
おかしいところを教えてほしいです。
よろしくお願いします。

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#27

投稿記事 by たかし » 12年前

すみません、またプログラムを書き直しました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 1000

int check(char *a)
{
	while(*a!='\0'){
		if(strncmp(a,".txt",5)==0){
			return(1);
		}
		a++;
	}
	return 0;
}

int main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    char *prog = argv[0];
    int line=0;
    int start=0;
    int end=0;
    int count=1;
	int notfinish=1;
	int option=0;
	int file=0;
 
    
    FILE *fp = stdin;
    int i;

    for(i=1; i<argc; i++){
		if(strncmp(argv[i],"-",1)==0){

			if(strncmp(argv[i],"-n",3)==0){
				line=1;
			}
			
			else if(strncmp(argv[i],"-h",2)==0){
				start = atoi(argv[i]+2);
			}
			else if(strncmp(argv[i],"-t",2)==0){
				end = atoi(argv[i]+2);
			}
			else{
				fprintf(stderr,"Option's type is -n or -h or -t .\n");
				fprintf(stderr,"-n :Output line number.\n");
				fprintf(stderr,"-h :Choose start line number.\n");
				fprintf(stderr,"-t :Choose end line number.\n");
				break;
			}
			option=1;
		}
        
			
			if(check(argv[i])==1){
				if((fp = fopen(argv[i],"r")) == NULL){
					fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
					exit(1);
				
				}
				file=1;
			}
			else if(file){
				fprintf(stderr,"Please input file name.\n");
				break;
			}
		
            if(start!=0 && end!=0){
				if(start>end){
					if(start<=0 || end<=0){
						fprintf(stderr,"start/end line number incorrect\n");
						break;
					}
					else{
						fprintf(stderr,"start line number is bigger than end line number.\n");
						break;
					}
				}
				else if(start<=0 || end<=0){
					fprintf(stderr,"start/end line number incorrect\n");
					break;
				}
			}
			if(option==0){
				if(notfinish){
            		while( fgets (buf, BUFSIZE, fp)!=NULL){
                		if(count>=start){
							if(line!=0){
                	        	line=count;
								printf("%d ",line);
								line++;
							}
							fputs(buf,stdout);
							if(count==end){
								notfinish=0;
								break;
               				}
						}
 
            	    count++;
                
                
            		}
        	    }
    	        if( fp != stdin){
					fclose( fp );
        	    }
			}
		option=0;

 	   }
	return 0;
}
先ほどcheck(argv)を出力して確認したらtxtファイルを入力したのに0と出力されていました。
つまりcheck関数が正しく動作していないような気がします。
しかし

コード:

if(check(argv[i])==1){
if((fp = fopen(argv[i],"r")) == NULL){
	fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
	exit(1);
}
という条件をつけたので、もしcheck(argv)=0ならばif文の中にあるファイルの読み込みもされないのではと思ったのですが、
ファイルの読み込みはちゃんと行われております。
わけがわからなくなってしまいました。
時間がなくなってきたのであせってます。
ヒントでもいいので教えていただけないでしょうか。

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

Re: catコマンドの自作

#28

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

コード:

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

int check(char *a)
{
    while(*a!='\0'){
        if(strncmp(a,".txt",5)==0){
            return(1);
        }
        a++;
    }
    return 0;
}

int main(void) {
    printf("%d\n",check("pen.txt"));
    printf("%d\n",check("cat.c"));
    printf("%d\n",check("-n"));
    return 0;
}
というコードでこのcheck関数の単体テストをしたところ、正しく

コード:

1
0
0
と出力されました。うまく動かないのは勘違いかもしれません。

しかし、このcheck関数では、*.cなどの*.txt以外のテキストファイルに対応できません。
check関数の実装は「オプションでなければ1を返す」すなわち「1文字目が'-'でなければ1を返す」とするべきです。

また、この引数チェックだと、
$ cat -h3 -test pen.txt pen.txt
とした時にエラーになりません。
また、現状のプログラムでstart/endが0以下であるというエラーを起こすには、
$ cat -h3 -t-1 pen.txt pen.txt
のように一方に負の数を指定する必要があります。これでは本来の目的を達成できません。
atoiの結果が0以下でないかというチェックはatoiの直後に(strncmpの判定のif文の中で)するべきです。
オプションの処理の後のチェックではstart>endとなっていないかどうかのみを判定してください。
この時、if(start>end && end!=0)とするといいでしょう。
また、このチェックをファイルを開いたあとにすると、チェックに引っかかった時にファイルを開いたまま終了してしまいます。
オプションの処理のあと、ファイルを開く前にstart>endかどうかのチェックを行ってください。

ファイル名を指定しない時に標準入力が処理されない問題ですが、
(あまり褒められた実装ではないですが)一番外側のforループの後で、
file==0であった場合に、fp=stdinとして

コード:

while( fgets (buf, BUFSIZE, fp)!=NULL){
    if(count>=start){
        if(line!=0){
            line=count;
            printf("%d ",line);
            line++;
        }
        fputs(buf,stdout);
        if(count==end){
            notfinish=0;
            break;
        }
    }

    count++;


}
という処理を行ってください。

どうせline=count;とするので、line++;という処理は無駄かもしれません。

再三申し上げておりますが
自分できちんとテストをしてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

たかし
記事: 48
登録日時: 12年前

Re: catコマンドの自作

#29

投稿記事 by たかし » 12年前

みけCAT様、返事が遅れてしまい申し訳ありませんでした。
また、テストが不十分でした、すみません。これからは気をつけます。
一応完成したのでプログラムを載せます。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 1000

int check(char *a)
{
	if(*a!='-'){
		return 1;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	char buf[BUFSIZE];
	char *prog = argv[0];
	int line=0;
	int start=0;
	int end=0;
	int count=1;
	int notfinish=1;
	int option=0;
	int file=0;



	FILE *fp = stdin;
	int i;

	for(i=1; i<argc; i++){
		if(strncmp(argv[i],"-",1)==0){

			if(strncmp(argv[i],"-n",3)==0){
				line=1;
			}

			else if(strncmp(argv[i],"-h",2)==0){
				start = atoi(argv[i]+2);
				if(start<=0){
					fprintf(stderr,"start/end line number incorrect\n");
					exit(1);
				}
			}
			else if(strncmp(argv[i],"-t",2)==0){
				end = atoi(argv[i]+2);
				if(end<=0){
					fprintf(stderr,"start/end line number incorrect\n");
					exit(1);
				}
			}
			else{
				fprintf(stderr,"Option's type is -n or -h or -t .\n");
				fprintf(stderr,"-n :Output line number.\n");
				fprintf(stderr,"-h :Choose start line number.\n");
				fprintf(stderr,"-t :Choose end line number.\n");
				exit(1);
			}
			option=1;
		}
		if(start!=0 && end!=0){
			if(start>end &&end!=0){

				fprintf(stderr,"start line number is bigger than end line number.\n");
				exit(1);
			}

		}


		if(check(argv[i])==1){
			if((fp = fopen(argv[i],"r")) == NULL){
				fprintf(stderr, "%s: %s: Nosuch file or directory\n", prog, argv[i]);
				error=1;
				exit(1);

			}
			file=1;
		}
		else if(file){
			fprintf(stderr,"Please input file name.\n");
			exit(1);
		}


		if(option==0){
			if(notfinish){
				while( fgets (buf, BUFSIZE, fp)!=NULL){
					if(count>=start){
						if(line!=0){
							line=count;
							printf("%d ",line);

						}
						fputs(buf,stdout);
						if(count==end){
							notfinish=0;
							break;
						}
					}

					count++;


				}
			}
			if( fp != stdin){
				fclose( fp );
			}
		}
		option=0;

	}

	while( fgets (buf, BUFSIZE, fp)!=NULL){
		if(count>=start){
			if(line!=0){
				line=count;
				printf("%d ",line);
					
			}
			fputs(buf,stdout);
			if(count==end){
				notfinish=0;
				break;
			}
		}
		count++;
	}
	return 0;
}
ご指摘いただいた部分を直してみた結果正常に動いたようだったので、このプログラムを提出しました。

みなさまが、私の質問に何度も答えていただいて本当に助かりました。
ありがとうございました。
今後もよろしくお願いします。

閉鎖

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