オプション・スイッチを調べる構造
このような仕様では、ユーザーは目的に応じてオプション・スイッチを選択するため、その数と内容は不定になります。プログラムは、数も内容も分からない状態に対応しなければなりません。
ループで1つずつ文字列を調べる
プログラムに与えられたパラメータの数と内容が不定の場合、前回紹介した『[ ]内の添字でargvの要素番号を指定する』という以下のような方法は採れません。オプション・スイッチの登場する順序が定まっていないためです。
char *filename, *word;
filename = argv[1];
word = argv[2];
forまたはwhileループでコマンドラインからパラメータ文字列を1つずつ取り出し、その都度内容(オプション・スイッチの記号)を読み取っていく形を採ることになります。
先頭に“-”記号の付くオプション・スイッチが4種類あるため、それをifで判別します。先頭が“-”なら、続く文字がcかnかfかwかを調べ、c以外(n、f、w)ならさらに続く値(number、filename、word)を調べます。
この仕様を満たす処理は、リスト1のようになります。
リスト1:コマンドライン・パラメータを順に取り出して調べる処理(ex2901.c, ex2901.exe)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define bool int /* 論理型を定義 */
#define _FALSE 0 /* 偽 */
#define _TRUE !_FALSE /* 真 */
int main(int argc, char * argv[])
{
int i; /* ループカウンタ */
char *p; /* 各パラメータ(argv[])の受け皿 */
/* 各パラメータを保持する変数 */
char *filename;
char *word;
bool sw_capt = _FALSE;
int maxnum = 0;
/* パラメータの数だけ繰り返す */
for (i=1; i<argc; i++) {
p = argv[i]; /* パラメータを順に受け取る */
if (p[0] == '-') { /* 先頭が'-'の場合 */
p++; /* 次の文字を調べる */
if (p[0] == 'c') { /* 1文字を調べるため添字を使う */
sw_capt = _TRUE;
}
/* さらに次の文字を先頭に文字列として扱うため
++p と前置インクリメントする */
else if (p[0] == 'n') {
maxnum = atoi(++p);
}
else if (p[0] == 'f') {
filename = ++p;
}
else if (p[0] == 'w') {
word = ++p;
}
else {
/* 先頭が'-'でなければ何もしない */
}
}
}
/* パラメータの示す内容を表示 */
printf("Caps : %s, MaxNumber : %d, File name : %s, Word : %s\n",
sw_capt ? "TRUE" : "FALSE", maxnum, filename, word);
}
argvの配列を順に調べていく
リスト1では、forループでパラメータの数(1~argcまで)だけ繰り返し処理を行い、ループを1回実行するごとに文字列を示すchar型ポインタpにargvの配列を順に代入して個々のパラメータを調べています。
for (i=1; i<argc; i++) {
p = argv[i];
本来なら、調べたパラメータの内容によって実際の処理を切り替えていく訳ですが、リスト1では以下のような変数に値を代入するだけにし、最後にそれら変数の内容(=パラメータの示す設定)をprintf関数で画面に出力します。
-c:bool(int)型の記号定数_TRUE(真)または_FALSE(偽)
-n<number>:numberの値をint型のmaxnumに保存
-f<file name>:文字列file nameのアドレスをchar型ポインタfilenameに保存
-w<word>:文字列wordのアドレスをchar型ポインタwordに保存
サンプル・プログラムの実行結果
このプログラムに4個のコマンドライン・パラメータを与えて実行すると、以下のように表示されます。
実行例1)
C:\CLANG\EXE>ex2901 -c -n8 -fabc.txt -whello
Caps : TRUE, MaxNumber : 8, File name : abc.txt, Word : hello
実行例2)
C:\CLANG\EXE>ex2901 -n12 -fxyz.mem -wwindows
Caps : FALSE, MaxNumber : 12, File name : xyz.mem, Word : windows
先頭の'-'と続く1文字を調べる
forループで順次パラメータ文字列を取り出していく部分は、ポインタ配列argvの添字にカウンタ変数iを充てていきます。この仕組みはお分かりでしょう。
次に、if文で各パラメータ文字列の先頭が'-'かどうかを調べます。このプログラムの仕様では、先頭に'-'記号の付いていないパラメータは存在しません('-'の付かないパラメータは無視されます)。
先頭が'-'であれば、続く文字がc、n、f、wのいずれかであるかを調べるため、pをインクリメントします。
if (p[0] == '-') { /* 先頭が'-'の場合 */
p++; /* 次の文字を調べる */
ポインタと配列の示す先
カウンタ変数iが1の場合、pがargv[1]──最初のパラメータを指しています。そのときp[0]はパラメータの先頭の1文字を示します。
そこでpをインクリメント(1増加)すると、*pは最初のパラメータの『2文字目』を指します。最初のパラメータ文字列が"-c"ならp[0]は'-'を、インクリメントされたpは'-'の次の'c'を示していることになります。
この状態で*pとすると文字列(char型配列)の"c"を示すため、1文字だけを調べるためにp[0]とします。
if (p[0] == 'c') { /* 1文字を調べるため添字を使う */
sw_capt = _TRUE;
}
'-n'に続く数値を取得する
パラメータ文字列が"-n"の場合、続いて「数値」が指定されています。そのため、さらにpをインクリメントして『数値を表す文字列』の銭湯を示させ、atoi関数でint型に変換します。
pをインクリメントしてからatoi関数の引数にするため、p++ではなく++pと前置インクリメントしているところに注意してください。p++ではインクリメントされる前のpをatoi関数の引数としますが、++pとするとインクリメントされたpが引数になります。
else if (p[0] == 'n') {
maxnum = atoi(++p);
}
'-f'と'-w'に続く文字列を取得する
'-f'と'-w'では、続く文字列をそのままファイル名と単語を示す文字列(char型配列)として扱わなければなりません。このときも++pとpを前置インクリメントし、pが'f'あるいは'w'の次の文字を先頭とした文字列を示すようにします。
else if (p[0] == 'f') {
filename = ++p;
}
else if (p[0] == 'w') {
word = ++p;
}
便利な三項演算子「?」
リスト1の最後では、各パラメータの値をprintf関数で表示しています。第1引数の書式化文字列の中では、大文字/小文字の区別をするかどうかを示すsw_captの値を%sとして文字列で表示するようにしています。
printf("Caps : %s, MaxNumber : %d, File name : %s, Word : %s\n",
sw_capt ? "TRUE" : "FALSE", maxnum, filename, word);
それに対応する第2引数には、以下のような式を充てています。
sw_capt ? "TRUE" : "FALSE"
?は次のような書式で用います。
<式> ? <値1> : <値2>
?は3つの項を採る三項演算子で、<式>の評価結果が「真」なら<値1>を、「偽」なら<値2>を返します。
リスト1の場合は、『bool型(実際にはint型)の変数sw_captの値が真(_TRUE)なら文字列"TRUE"を、偽(_FALSE)なら文字列"FALSE"を第1引数内の最初に現れる%sの値に対応させる』ことを意味しています。
三項演算子「?」は、if elseによる制御構造とよく似ています。しかし、if文が条件を調べた結果によって処理の切り替えを行うのに対して、?演算子は真偽を判定した結果によって『値を返し』ます。制御構造ではなく、あくまで演算子なのです。
|
|
|