スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[C言語Tips]パイプが連結可能なコマンドの実装と条件 (1/2)

人にゃ出来ないことがある。でも自分がやりたいと望むことぐらいならできる。
出来ないというのはやろうとすることを望まなくなることだから。

どうも、最近、生活していて実感していることを冒頭に書くことにし始めた砂塵です。

今日は仕事中に意外と情報が少なくて悩んだパイプ連結可能なコマンドの実装について書きたいと思います。
ちなみに私はまだ成人してそこそこの若造ですが、書ける言語の中でC言語にはほんの少しの自信と溢れんばかりの愛情を持っています。
ですので、今回はいつもよりは恐らく、コアな話になると思います。

今、ちょっと気持ち悪いと思った人。

そりゃ私とC言語の馴れ初めを聞いたらそりゃもう、
そりゃ愛するわ!抱きしめたくもなるわ!と納得していただけるに違い無い訳ですよ。

思い起こせば6年前・・・・

ハイハイ、馬鹿やってないで、本題に入りましょうか。
まあ、C言語に思い入れがあるのは本当ですけどね。思い出話はまた今度。

まず、この話はコマンドプロンプトやコンソールを叩かない人達にはあまり関係がありません。
なぜならパイプとはそういういわゆるCUI(Charcter User Interface)の環境下で「標準入力をプログラムにすり替える」技術だからです。

ここから先はソースコードを見ながらの方が圧倒的に分り易いはずですのでバンバン書いていきます。





まず、以下のソースコードはキーボードから文字列を一行読み込んで出力するプログラムです。
ちなみに、厳密にはC++のプログラムです。C言語では変数であるstatic const int の値を配列の要素数にすることはできません。
どうしてもC環境しかない場合は#defineかenumで代用して下さい。


#include <stdio.h>

static const int BUF_SIZE = 256;

int main ()
{
char buf[BUF_SIZE];

fgets(buf , sizeof(buf) , stdin);
fputs(buf , stdout);

return 0;
}


C言語を学びたての方なんかは、あれ!?scanf()は!?とか、printf()は!?とか思うかと思いますが、
printfはまだ良いにしても、scanf()なんて危なかっしいものが使えるか!!あのscanf()推奨主義は学校機関の大罪だと思います。

理由は

・入力を受け取るサイズが不明確なのでバッファオーバーフローで容易くプログラム死ぬ。
・想定した入力以外の入力を受けた時に危ない挙動を示す。
・改行を読み捨てる。

などがあります。

しかし、このプログラムの肝はfgets()に指定されたstdinとfputs()に指定されたstdoutです。
これは標準入力と標準出力と言ってコンピュータへの入出力を抽象化したものです。

デフォルトでは入力はキーボードから、出力は画面へと設定されているため、意識しないで使っている限りscanf()はいつだってキーボードから入力を受け付けますし、printf()は画面で出力されます。

しかし、この標準入出力はファイルや他のプログラムに切り替えることが可能なのです。
具体的には先程のプログラムに対してこうしてやります。
先程のプログラムをビルドしたらコマンドプロンプトから以下のようにしてやります。
ビルドしたプログラム名は仮にhoge.exeだとします。

hoge.exe < test.txt

test.txtには最低一行何かを記述してやって下さい。
そうするとファイルの一行目の文字列画面に出力されたはずです。

先程はキーボード入力が会ったのに今度はありませんでしたね?
これは標準入力がファイルに切り替わったためです。

更に標準出力をファイルに切り替えるには以下のようにします。

hoge.exe > test.txt

両方を組み合わせて

hoge.exe < test.txt > test2.txt


とすると新しいファイルtest2.txtにtest.txtから読み込まれた1行目の内容が書き込まれます。
更にプログラムを以下のように書き換えればファイルポインタがどうのこうのってを気にせずに
ファイルの内容を別のファイルにコピーするプログラムが実行可能です。


#include <stdio.h>

static const int BUF_SIZE = 256;

int main ()
{
char buf[BUF_SIZE];

while (fgets(buf , sizeof(buf) , stdin))
{
fputs(buf , stdout);
}
return 0;
}


このループが成立するのはfgetsがファイルの末尾に到達した時にNULLを返却するからです。

そうそう!私はこういう知識を使って学校の先生を困らせちゃう悪ガキは大好きですので
「ファイルの内容をコピーするプログラムを書きなさい!」と言われたらこれをやって
たっぷり絞られて下さい。

ちなみに正攻法(?)は以下のようになります。


#include <stdio.h>

static const int BUF_SIZE = 256;

static const char *READ_FNAME = "test.txt";
static const char *WRITE_FNAME = "test3.txt";

int main ()
{
FILE *read;
FILE *write;
char buf[BUF_SIZE];

//読み込みモードでファイルをオープン
if ((read = fopen(READ_FNAME , "r")) == NULL)
{
fputs("ファイルのオープンに失敗しました。" ,stderr);
}

//書き込み(上書き)モードでファイルをオープン
if ((write = fopen(WRITE_FNAME , "w")) == NULL)
{
fputs("ファイルの作成に失敗しました。" ,stderr);
}

//test.txtから一行読み込み、test3.txtに書き込む。
while (fgets(buf , sizeof(buf) , read))
{
fputs(buf , write);
}

//ファイルのクローズ
fclose(read);
fclose(write);

return 0;
}


上記と比較するとちょっと面倒ですし、本質的でない部分のコード量が増大しています。
もっと言うとコマンドライン引数などをを使わない限り柔軟性も低いので、私的には上記のソースのほうが正解だと思います。プログラムの鉄則は「早く、柔軟に、そして、美しく」です。

まあ、それを言うならこんなのもありでしょう。


#include <windows.h>

int main ()
{
return system("copy test.txt test3.txt");
}


うん。流石に冗談です。でもまあ、上記の正攻法のファイル書き込みプログラムをちょこっと書き換えれば
copyコマンドのサブセットぐらいは余裕で書けると思います。

おっとっと、ちょっと脱線していたら時間が無くなってしまったので本題は次回にお預けです。
・・・んー。やっぱりC言語を書いてると遊び過ぎてしまいますね。いかんいかん。
うーんでも、C言語書いてる時はやっぱり幸せだなぁ。自分、キモイなぁ。あーでも、やっぱり幸せ。


でも、まあ、ここまではほんのお遊び。ウォーミングアップです。
というのも、ここまでの話はCの入門サイトなどでも大抵されますしね。

次回は標準入出力をパイプを使ってプログラムにリダイレクトして
実行結果を連結させる方法について述べます。お楽しみに。

それではまた次回。

(2/2)へ移動する
スポンサーサイト

コメントの投稿

非公開コメント

twitter
    follow me on Twitter
    プロフィール

    砂塵

    Author:砂塵
    GIMP2でお絵描きしています。
    主にイラスト練習、プログラミングなどを扱っているブログです。

    Skype始めました。
    SkypeID:sazinn-gimp
    出没時間:平日21~26時、休日(土・日)

    Pixivはじめました
    微エロな絵を載せることがありますので、そういうのが苦手な方や嫌悪感を覚える方はご注意ください。

    カテゴリ
    リンク
    最新記事
    月別アーカイブ
    最新コメント
    RSSリンクの表示
    FC2カウンター
    FC2ブログランキング

    FC2Blog Ranking

    参加ブログカテゴリ
    にほんブログ村 IT技術ブログ プログラム・プログラマーへ
    にほんブログ村 イラストブログ イラスト練習へ
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。