最近在寫程序的過程中,把一部分時間都花費在程序對參數的處理上。今天聽了學長說到getopt函數,才發現原來c裡面還有一個專門解決參數處理的函數,查詢了相關資料,這裡簡單總結一下。
使用int main( int argc, char *argv[] )(或int main( int argc, char **argv ))時,系統將把用戶輸入的參數通過argc和argv引入程序中,argc為參數的個數,argv是指向參數的指針數組,其中第一個參數為自身程序文件名。
這裡我們使用getopt()
函數對傳入的參數進行處理,getopt()位於 unistd.h 系統頭文件中,函數原型可以在man中查到,大家可以使用下面指令查看getopt的man手冊
man 3 getopt
getopt的函數原型如下:
#include <unistd.h> int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt;
這裡引用IBM對於其的解釋,原文鏈接在這。我對其中部分錯誤做了更改,
給定了命令參數的數量 (
argc
)、指向這些參數的數組 (argv
) 和選項字符串 (optstring
) 後,getopt()
將返回第一個選項,並設置一些全局變量。使用相同的參數再次調用該函數時,它將返回下一個選項,並設置相應的全局變量。如果不再有識別到的選項,將返回-1
,此任務就完成了。
getopt()
所設置的全局變量包括:extern char *optarg; //選項的參數指針
extern int optind, //下一次調用getopt的時,從optind存儲的位置處重新開始檢查選項。
extern int opterr, //當opterr=0時,getopt不向stderr輸出錯誤信息。
extern int optopt; //當命令行選項字符不包括在optstring中或者選項缺少必要的參數時,該選項存儲在optopt中,getopt返回'?'對於每個選項,選項字符串 (
optstring
) 中都包含一個對應的字符。具有參數的選項後面跟有一個:
字符。如果一個選項後面緊跟2個冒號(::),表示該選項和參數之間不使用空格隔開;可以重復調用
getopt()
,直到其返回-1
為止;任何剩下的命令行參數通常視為文件名或程序相應的其他內容。
我們知道,一般程序的選項分為兩種:帶關聯值的和不帶關聯值的,
在解析參數的時候,我們希望的是:從一堆參數中把我們要的選項和選項關聯值分別找到並存到相對應的地方供我們使用,並且把其他參數找出來供我們調用。
getopt完美實現這一點。
我們在測試的時候,定義了一個全局變量來存儲參數值,
typedef struct parameter { int a; //參數a int b; //參數b char *b_pri; //參數b的關聯值 int c; //參數c char *c_pri; //參數c的關聯值 }par;
如果遇到了其他選項,getopt()
將顯示一個錯誤消息,程序將在顯示了使用方法消息後退出。因此optString應該為
static const char *optstring = "ab:c::?";
程序運行之前,我們先對參數初始化,設定其初始值。
//參數初始化 opt.a = 0; opt.b = 0; opt.b_pri = NULL; opt.c = 0; opt.c_pri = NULL;
接下來就簡單的把參數使用getopt處理,最後輸出最後的參數就行了,看看我們的測試程序。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 5 typedef struct parameter 6 { 7 int a; //參數a 8 int b; //參數b 9 char *b_pri; //參數b的關聯值 10 int c; //參數c 11 char *c_pri; //參數c的關聯值 12 }par; 13 14 15 int main( int argc, char **argv ) 16 { 17 int i; //循環變量 18 par opt; //參數 19 int flag = 0; //循環退出標志位 20 21 //參數初始化 22 opt.a = 0; 23 opt.b = 0; 24 opt.b_pri = NULL; 25 opt.c = 0; 26 opt.c_pri = NULL; 27 28 //參數檢測 29 if (argc == 1) 30 { 31 printf("您沒有設置選項!\n"); 32 exit(0); 33 34 } 35 //輸出未處理的參數 36 printf("系統傳入的參數為:"); 37 for ( i = 1; i < argc; i++) 38 { 39 printf("%s ",argv[i]); 40 } 41 printf("\n"); 42 43 //循環處理傳入參數 44 while(flag != -1) 45 { 46 //調用getopt處理參數 47 switch(getopt( argc, argv, "ab:c::?")) 48 { 49 case 'a': 50 opt.a = 1; 51 break; 52 case 'b': 53 opt.b = 1; 54 opt.b_pri = optarg; 55 break; 56 case 'c': 57 opt.c = 1; 58 opt.c_pri = optarg; 59 break; 60 case -1: 61 flag = -1; 62 break; 63 default: 64 break; 65 } 66 } 67 68 if( optind != argc) 69 { 70 printf("參數中非程序選項的有:"); 71 for (i = optind; i < argc; i++) 72 { 73 printf("%s\t",argv[i]); 74 } 75 printf("\n"); 76 } 77 78 //輸出解析結果 79 printf("解析到程序啟動後啟用的選項有:"); 80 if (opt.a == 1) 81 printf("a,"); 82 if (opt.b == 1) 83 printf("b(參數為:%s),",opt.b_pri); 84 if (opt.c == 1) 85 printf("c(參數為:%s),",opt.c_pri); 86 printf("\n"); 87 88 89 //處理後,輸出全部參數與原來對比 90 printf("使用getopt後,系統參數變為為:"); 91 for ( i = 1; i < argc; i++) 92 { 93 printf("%s ",argv[i]); 94 } 95 printf("\n"); 96 97 return 0; 98 }
保存後,編譯運行。
$ ./a.out -a -b 123 3 22 -h -c1 系統傳入的參數為:-a -b 123 3 22 -h -c1 ./a.out: invalid option -- 'h' 參數中非程序選項的有:3 22 解析到程序啟動後啟用的選項有:a,b(參數為:123),c(參數為:1), 使用getopt後,系統參數變為為:-a -b 123 -h -c1 3 22
其中,我們傳入的參數被全部解析出來,非正常選項"-h"直接顯示出來,如果大家不希望非正常參數顯示出來,或者希望自己處理非正常參數,可以在設置getopt中全域變量opterr = 0,在optstring的時候不加入?,例如
opterr = 0; static const char *optstring = "ab:c::";
那麼程序在檢測到未知選項會返回?,我們可以這樣處理
opterr = 0; //getopt不輸出錯誤參數 ...其他程序段... //循環處理傳入參數 while(flag != -1) { //調用getopt處理參數 switch(getopt( argc, argv, "ab:c::")) { case 'a': opt.a = 1; break; case 'b': opt.b = 1; opt.b_pri = optarg; break; case 'c': opt.c = 1; opt.c_pri = optarg; break; case '?': printf("出現非正常選項:%c\n",optopt); break; case -1: flag = -1; break; default: break; } }
程序源代碼如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 5 typedef struct parameter 6 { 7 int a; //參數a 8 int b; //參數b 9 char *b_pri; //參數b的關聯值 10 int c; //參數c 11 char *c_pri; //參數c的關聯值 12 }par; 13 14 15 int main( int argc, char **argv ) 16 { 17 int i; //循環變量 18 par opt; //參數 19 int flag = 0; //循環退出標志位 20 21 //參數初始化 22 opt.a = 0; 23 opt.b = 0; 24 opt.b_pri = NULL; 25 opt.c = 0; 26 opt.c_pri = NULL; 27 opterr = 0; //getopt不輸出錯誤參數 28 29 //參數檢測 30 if (argc == 1) 31 { 32 printf("您沒有設置選項!\n"); 33 exit(0); 34 35 } 36 //輸出未處理的參數 37 printf("系統傳入的參數為:"); 38 for ( i = 1; i < argc; i++) 39 { 40 printf("%s ",argv[i]); 41 } 42 printf("\n"); 43 44 //循環處理傳入參數 45 while(flag != -1) 46 { 47 //調用getopt處理參數 48 switch(getopt( argc, argv, "ab:c::")) 49 { 50 case 'a': 51 opt.a = 1; 52 break; 53 case 'b': 54 opt.b = 1; 55 opt.b_pri = optarg; 56 break; 57 case 'c': 58 opt.c = 1; 59 opt.c_pri = optarg; 60 break; 61 case '?': 62 printf("出現非正常選項:%c\n",optopt); 63 break; 64 case -1: 65 flag = -1; 66 break; 67 default: 68 break; 69 } 70 } 71 72 if( optind != argc) 73 { 74 printf("參數中非程序選項的有:"); 75 for (i = optind; i < argc; i++) 76 { 77 printf("%s\t",argv[i]); 78 } 79 printf("\n"); 80 } 81 82 //輸出解析結果 83 printf("解析到程序啟動後啟用的選項有:"); 84 if (opt.a == 1) 85 printf("a,"); 86 if (opt.b == 1) 87 printf("b(參數為:%s),",opt.b_pri); 88 if (opt.c == 1) 89 printf("c(參數為:%s),",opt.c_pri); 90 printf("\n"); 91 92 93 //處理後,輸出全部參數與原來對比 94 printf("使用getopt後,系統參數變為為:"); 95 for ( i = 1; i < argc; i++) 96 { 97 printf("%s ",argv[i]); 98 } 99 printf("\n"); 100 101 return 0; 102 }
這樣的話,上面同樣的參數下,運行結果如下:
$ ./a.out -a -b 123 3 22 -h -c1 系統傳入的參數為:-a -b 123 3 22 -h -c1 出現非正常選項:h 參數中非程序選項的有:3 22 解析到程序啟動後啟用的選項有:a,b(參數為:123),c(參數為:1), 使用getopt後,系統參數變為為:-a -b 123 -h -c1 3 22
ok了,其實還有一個比getopt更好的一個選擇,就是getopt_long,它可以支持長選項,因為這篇博文呢針對getopt的,所以我就不多做介紹,大家可以查man手冊,或者等待我不知道什麼時候的下次更新吧。
這裡的char **argv是一個指針數組,是存放你所輸入的命令行的各個單詞的,int argc存放單詞的個數。例如,你輸入的是copy file1 file2,則有argc裡面是3,argv中是存放的就是指向這3個字符串的指針,即為一個有三個指針元素的數組{"copy","file1","file2"}。
既然你參數是可選的(::),按照規定只能option和value之間不能有空格。用getopt之類的函數無法實現。
你要實現這樣的目標,可以自己寫一個。也不是很困難。
因為現成有很多類似的實現。到網上找一個即可。
或者參見這篇文檔, 其中附件有getopt_long的例子可以下載。我看過。符合你的需求。
www.ibm.com/...t.html
幫你下載好了,參見附件。