ページ 1 / 1
catコマンドの自作
Posted: 2013年5月03日(金) 17:58
by たかし
大学の課題で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
Re: catコマンドの自作
Posted: 2013年5月03日(金) 18:15
by みけCAT
オプションはファイルの中ではなく、コマンドラインで渡されるのではないですか?
あと、ファイルポインタ変数を++するのは意味がないと思います。fgetsなどでファイルを読み込めばファイルポインタは勝手に進みます。
意図的にファイルポインタの位置を変更するには、fseek関数を使います。(必要ないと思いますが)
操作の手順としては、まず最初にコマンドライン引数を一通り見てオプションを探し、
その後コマンドライン引数をもう一度最初から見てファイルの処理をするべきだと思います。
Re: catコマンドの自作
Posted: 2013年5月03日(金) 18:20
by ただの屍のようだ
コード読んだ感想をいうと『?』ですね。
何ステップに分けて考えましょう。
ステップ1 cmd引数で指定されたファイルを出力
ステップ2 オプションを一個ずつ実装してみる
ステップ3 cmd引数なしのとき、stdinで入力された文字列を配列に格納する。
ステップ4 配列を解析してみる
*気になったこと:
変数progって本当に必要ですか?
FILE*ってファイルにアクセスするためのポインタでしかないと認識してましたが?
main()はintの戻り値を返す必要があるはずですが?
全部mainに詰め込むとバグ探しでストレスたまると思いますが?
Re: catコマンドの自作
Posted: 2013年5月03日(金) 19:08
by ISLe
cat -h2 foo.txt -h3 bar.txt
ってコマンドラインだと
foo.txtは2行目から、bar.txtは3行目から出力するのでは?
だとすると頭から順番に解析していくことになるけど、オプションの誤りを事前にチェックするとなると…。
なにげに難易度高いですよね。
そんな深く考えずに出された問題でしょうけど。
Re: catコマンドの自作
Posted: 2013年5月03日(金) 19:18
by ただの屍のようだ
みけねこさんの指摘しているやり方とまったくかみ合ってなくて質問者が混乱すると思いました。
とりあえず自分の目指すcatを説明します。
例:cat ファイル1 -n -h4 -t15 ファイル2 -n -t3 ファイル3 -t4 -h2
意味:ファイル1を行番号表示させながら、4-15行を出力して、ファイル2を行番号表示させながら3行目まで出力し、ファイル3を2-4行目まで出力します。
構文チェックをしっかりやれば案外簡単です。が
簡単そうにみえて割とハードル高いです。これができたら先生にほめられると思います。
最初のステップをうまく作れば、あとは配列から単語を切り出してポインタの配列に格納してargvと同様に渡すだけなので、最初が肝心です。
Re: catコマンドの自作
Posted: 2013年5月03日(金) 19:49
by h2so5
おそらく h, t オプションは結合後の出力に対して適用されるのではないかと思います。
入力ごとに h, t を指定するのは流石にちょっと複雑ですから。
オフトピック
実際に使うとなると、head & tail で代用できる?
初歩的な質問なのですが・・・
Posted: 2013年5月04日(土) 11:11
by たかし
皆さんたくさん回答を下さってありがとうございます。
まだできていないのですが、ちょっと質問させてください。
./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")
初歩的な質問ですみません。
よろしくお願いします。
Re: 初歩的な質問なのですが・・・
Posted: 2013年5月04日(土) 11:32
by みけCAT
playhuman さんが書きました:fopenでテキストファイルでないargv[1] = "-n"を読み込んだ場合
ファイルポインタfpには何が入るのですか?
ファイル"-n"が存在すればfpにはそのファイルを開いたFILE構造体へのポインタが入ります。
ファイルの中身がテキストかバイナリかは関係ありません。
ファイル"-n"が存在しなければNULLが入ります。
playhuman さんが書きました:また、argv[2] = "saple.txt"を読み込んだ場合ファイルポインタfpが指しているのはsample.txtに
書いてある文章の一文字目のアドレスを指しているという認識で間違っていませんか?
typoには目をつむるとして、間違っています。
fpが指しているのはsample.txtを開いたFILE構造体へのポインタであり、ファイルの中身のデータは関係ありません。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 11:34
by usao
とりあえず各オプションとファイル名の並びのルールを
明確にしたほうがよいのでは? (課題の文章がそれだけなら,自分で決めてしまってもよい??)
引数が
ファイル名1 -t ファイル名2
だった場合,-tはどっちのファイルへの指定なのか.
Re: catコマンドの自作
Posted: 2013年5月04日(土) 12:18
by たかし
みけ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]がオプションでなかった場合はファイルを読み込んで出力して閉じるようにしました。
何故セグメントエラーが起こってしまったのでしょうか?(コンパイルは通ったのですが・・・)
よろしくお願いします。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 12:28
by みけCAT
$ ./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]がオプションの時は、ファイルの操作は行われません。
ファイルの操作で、オプションの分のコマンドライン引数もファイル名として処理をしています。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 15:42
by たかし
勝手にコメントを編集してしまって皆様を混乱させてしまったようです。
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関数には問題がないと思います。
原因がわかりません。
よろしくお願いします。
」
Re: catコマンドの自作
Posted: 2013年5月04日(土) 16:12
by ただの屍のようだ
すでに指摘された64行目のswitch文が問題です。
ついでにたとえば $ ./a.exe -n -h5 pen.txt とした場合
実行結果:
12345This
6is
7a
8pen.
9This
10is
11a
12eraser.
13
Re: catコマンドの自作
Posted: 2013年5月04日(土) 17:18
by みけCAT
64行目のswitch文とは何でしょうか?
私には58行目までしか見えません。
追記
おそらく編集により会話の流れの整合性が取れなくなっています。
確かフォーラムルール違反だったと思います。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 17:42
by softya(ソフト屋)
元の文章が不明なのですが、playhumanさんが途中で編集された回答があるため話の流れが分からなくなっているようです。
元が残らない文章改変や消すことは原則禁止しておりますので、元に戻していただきたいと思います。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 17:53
by usao
なんか話の流れがよくわからん状態になっていますが…
コマンドラインの解釈のあたりで躓いているのでしょうか?
とりあえず
コード:
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()か何かで表示してみるとか.
これができたら,あとは,指定されたオプション内容に従ってファイル出力する部分をつくるだけ.
#オプションはファイル毎ではなくて,全体に効くのですね.
だったら別にファイル名とオプションの順番は(決めてしまうとかえって難しくなるようなら)自由な並びでもよいのかも?
Re: catコマンドの自作
Posted: 2013年5月04日(土) 18:20
by たかし
先ほどは混乱を招いてしまいすみませんでした。
完全に一言一句同じ文章に戻すことはできなかったのですが、ほぼ同じ内容のものを書かせていただきましたので、確認いただけたらと思います。
そして現在プログラムは以下のようになっております。
コード:
#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文が実行されるので実行結果は変わらないと思ったのですが・・・
よろしくお願いします。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 18:24
by みけCAT
switch文の中にbreak;が無いですが、大丈夫ですか?
また、文字コードをマジックナンバーで書く癖が(一部しか)直っていませんね。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 19:00
by たかし
みけ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関数を使うこと。」(最初のコメントで書かせていただきました)
とかかれております。
私の書いたプログラムにはそれが使われておりません。
しかし文字列の比較はどこで使うのかよくわかりません。
どこで使ったらよいのか教えてほしいです。
よろしくお願いします。
Re: catコマンドの自作
Posted: 2013年5月04日(土) 19:08
by みけCAT
コード:
num = num + *nptr-87;
などの部分のマジックナンバーが残っています。
strncmp関数は
コード:
if(strncmp(argv[1],"-n",2)==0) {
オプションとみなして処理
}
というように使用することが意図されていると思います。
Re: catコマンドの自作
Posted: 2013年5月05日(日) 08:10
by beatle
playhuman さんが書きました:しかしこのatoi関数は前回の授業で私が作ったもので、そのときは問題なく正常に動いていました。
だからこのatoi関数には問題がないと思います。
atoiは自作したものを使うという指示なのでしょうか。
atoiはC言語の標準関数ですから、特に指示がなければそれを使ったほうがいいのではないかと思います。
それから、No.19のソースコードの67行目
は実行されないのですが、それでいいですか?
Re: catコマンドの自作
Posted: 2013年5月05日(日) 11:37
by たかし
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)と書けば入力は止まるなと気づきました。
これでやっと完成しました。
皆さんのおかげです。
本当にありがとうございました。
来週も新しい課題が出されると思うので、もしかしたらまた質問させていただくかもしれませんが、そのときはまたよろしくお願いします。
あと、今回作ったプログラムで改善したほうがよい点があれば教えてください。
Re: catコマンドの自作
Posted: 2013年5月05日(日) 16:46
by みけCAT
●仕様違反
オプションがおかしい時は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
使用したeraser.txt
自作のatoi関数は16進数でしたが、標準のatoi関数は10進数です。きちんとテストしましたか?
標準関数で16進数を解釈するには、strtol関数(かsscanf関数)を使用します。
atoi関数はstrncmp関数と違って「利用してもよい」なので、利用する必要は無いと考えられます。
Re: catコマンドの自作
Posted: 2013年5月06日(月) 10:22
by たかし
修正箇所がこんなにあるなんてびっくりしました。
みけ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しました。
これで大丈夫でしょうか?
よろしくお願いします。
Re: catコマンドの自作
Posted: 2013年5月06日(月) 10:45
by みけCAT
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つも指定されない場合は、標準入力から入力する。」という仕様が満たされていません。
自分で各オプションの指定の組み合わせのテストをしましょう。
設定された仕様より、一度ファイル名が来たあとにオプションが来たらエラー、というチェックも入れたほうがいいと思います。
ファイル名が指定されない場合に標準入力から入力、という処理と合わせて、
ファイル名が来たかどうかのフラグを作る、という実装が考えられます。
Re: catコマンドの自作
Posted: 2013年5月06日(月) 15:04
by たかし
みけ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
と入力すると終わってしまいます。
原因がわかりません。
おかしいところを教えてほしいです。
よろしくお願いします。
Re: catコマンドの自作
Posted: 2013年5月06日(月) 17:33
by たかし
すみません、またプログラムを書き直しました。
コード:
#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文の中にあるファイルの読み込みもされないのではと思ったのですが、
ファイルの読み込みはちゃんと行われております。
わけがわからなくなってしまいました。
時間がなくなってきたのであせってます。
ヒントでもいいので教えていただけないでしょうか。
Re: catコマンドの自作
Posted: 2013年5月06日(月) 20:51
by みけCAT
コード:
#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関数の単体テストをしたところ、正しく
と出力されました。うまく動かないのは勘違いかもしれません。
しかし、この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++;という処理は無駄かもしれません。
再三申し上げておりますが
自分できちんとテストをしてください。
Re: catコマンドの自作
Posted: 2013年5月08日(水) 18:36
by たかし
みけ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;
}
ご指摘いただいた部分を直してみた結果正常に動いたようだったので、このプログラムを提出しました。
みなさまが、私の質問に何度も答えていただいて本当に助かりました。
ありがとうございました。
今後もよろしくお願いします。