getoptを使って書き直す
では、先ほどの例をgetoptを使って書き直してみます(リスト3)。
/* getoptを利用して-nオプションが指定されたら、改行を無視するようにしたプログラム */
#include <stdio.h>
#include "getopt.h" //==(1)
int main(int argc, char *argv[])
{
int c;
int opt; //==(2)
int flg = 0;
FILE *fp;
while( ( opt = getopt(argc, argv, "n") ) != -1 ) { //==(3)
switch( opt ) {
case 'n' : flg = 1; break;
}
}
//==(4)
argc -= optind;
argv += optind;
if( (fp = fopen( *argv, "r" ) ) == NULL ) {
fprintf( stderr, "Can't opened fle.\n" );
} else {
while( ( c = getc(fp) ) != EOF ) {
if( flg ) {
if( c != '\n' ) putchar(c);
} else {
putchar(c);
}
}
putchar('\n');
fclose( fp );
}
return 0;
}
いかがでしょう? たった一つの関数を導入しただけですが、ずっと簡潔な書き方になったと思いませんか? 加えてgetoptを使うと、先ほどの例では実現できなかった
$ プログラム名 [filename] -n
のような、オプションを後付けする機能も実現できます。
もしリスト2に示したようなアプローチを使うとすると、後付けのオプションに対応するのは大変な労力です。しかし、getoptを使うことで、そのような面倒な処理はすべてgetoptが行うため、気軽にオプションを増やしたり減らしたりといった、プログラムに合わせた効果的なコーディングが可能です。
getoptを使ったプログラムでよく見られるテクニック
リスト3に示したプログラムでは、getoptを使ったプログラムに見られる独特の表記方法やルールがいくつか隠れています。より詳細なgetoptの使い方を紹介する前に、そのことについて触れておきたいと思います。
最初に見ていただきたいのは、リスト3の(1)で示した部分です。PC-UNIX系のシステムでは、getoptはunistd.hに含まれているので、それをincludeするよう指示しています。
Windows系のOSでは、使用するライブラリに合わせたファイルをincludeします。
つぎに、リスト3の(2)と(3)で示した部分をご覧ください。
(2)で宣言しているのは、getoptの解析結果を受け取る変数です。(3)では、実際にgetopt関数を使い、オプションの解析結果を受け取っています。
getoptは、呼び出されるたびに見つかった次のオプションを一文字ずつ返します。返せる文字がなくなった時点で-1を返す約束になっているため、(3)のようなループを書くことで、いくつもオプションを取るようなプログラムや将来的にオプションが増える予定のあるプログラムであっても、拡張性と保守性を両立させたコードに仕上げることができます。この時getoptに渡す引数は、main関数の引数2つと、オプションを表す文字列です(後述)。あとはループ内に分岐を設け、オプションに対応したフラグを起こしたり、関数を呼び出したりといった操作を行っていきます。
こうして一通りオプションの解析が済むとリスト3の(4)に進むわけですが、いきなり宣言されていない変数を用いて、加算と減算を行っている様子が見て取れます。実はこの変数はgetoptが暗黙的に用意している変数で、「変数optindは、argvの次に処理される要素のインデックス」を保持するint型の変数です。
この変数を用いたテクニックが(4)のような処理です。この処理以降、argv[0]~argv[argc-1]には、オプションを除いたプログラムの引数が入ります(※注1)。
例えばオプション以外に複数のファイル名を受け取る必要があるコードなら、argv[0]~argv[argc-1]までに指定されたファイル名が格納されるといった具合です(※注2)。
通常のプログラムのように、argv[0]がプログラムの名称ではないので注意が必要です。
また、(4)の処理によって、同様にargcはオプション以外の引数の数を表す数に変化するので、こちらも注意が必要です。
ここで登場したoptind以外にも同様に、getoptはchar型ポインタ変数のoptargとint型変数のopterr、optoptという三種類の変数を提供してくれます。例えばoptargはオプションが引数を取る場合に活躍する変数で、opterrはその値を0にすることで、認識できない文字オプションが存在したときのエラーメッセージの表示を抑制する変数です。また、認識できない文字オプションが存在した場合には、その文字オプションがoptoptに入ります。この仕組みを利用して、エラーに対してプログラムごとの対処を行うことが可能です。
このほか、getoptの特別な動作として、解析できないオプションが見つかったときには「?」を返すという動作が挙げられます。独自の方法でエラーに対処するときには、このことも覚えておくと良いでしょう。
