標籤:enter 返回 ora 興趣 rar 指定 程式 啟動 分享
接上一篇文章的內容。
看了前面需求提到的複雜的命令列解析功能,很多人立馬開始發怵,其實大可不必。
我們都知道,Linux下的程式往往都提供了複雜的命令列參數處理機制,因為這是與
其他程式或使用者進行互動的主要手段,在這樣的情況下難能可貴的是,為了減輕開發
人員對命令列處理的負擔,Linux提供了系統函數getopt()或getopt_long()專門解析命令列參數。
在Linux系統中,函數getopt()/getopt_long()位於 unistd.h 系統標頭檔中,其原型分別為:
int getopt(int argc,char * const argv[],const char * optstring);
int getopt_long(int argc, char * const argv[],const char *optstring,
const struct option *longopts, int *longindex);
其中,參數argc和argv是由主函數main()傳遞的參數個數和內容。
參數optstring 則代表欲處理的選項字串。此函數會返回在argv 中下一個的選項字母,
此字母會對應參數optstring 中的字母。如果選項字串裡的字母后接著冒號“:”,則表示還有相關的參數,
全域變數optarg 即會指向此額外參數。如果getopt()找不到符合的參數則會列印出錯資訊,並將全域
變數optopt設為“?”字元,如果不希望getopt()列印出錯資訊,則只要將全域變數opterr設為0即可。
參數可簡單劃分為短參數和長參數兩種類型,getopt()使用optstring所指的字串作為短參數列表,
象“1ac:d::”就是一個短參數列表。短參數的定義是一個‘-‘後面跟一個字母或數字,象-a, -b就是一個
短參數,每個數字或字母定義一個參數。
而長參數則形如“--debug”,前面有2個‘-‘符號,後面可添加多個字母或數字。
getopt_long()函數包含了getopt()函數的功能,並且還可以指定“長參數”(或者說長選項),
與getopt()函數對比,getopt_long()比getopt()多了兩個參數。
此函數的基本用法如下(Linux下):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <stdio.h> #include <unistd.h> int main(int argc, int *argv[]) { int ch; opterr = 0; // getopt()可由getopt_long()替換 while ((ch = getopt(argc, argv, "a:bcde")) != -1) { switch(ch) { case ‘a‘: printf("option a:‘%s‘\n", optarg); break; case ‘b‘: printf("option b :b\n"); break; default: printf("other option :%c\n", ch); } } printf("optopt +%c\n", optopt); } |
以上作為參照,可見調用函數getopt()或getopt_long()可以非常方便地解析命令列。
但是,有一點遺憾的是,如此方便的函數在Windows下卻沒有提供,怎麼辦呢?當然有辦法了,
既然函數getopt()/getopt_long()是GNU C中的函數,那麼源碼可見就可以根據情況直接移植到Windows下。
說幹就幹,接下來簡要介紹一下移植方法,掌握一點新技能,如果對這部分沒有興趣,可以跳過,看後面的內容。
首先,訪問GNU C Library (glibc)的首頁http://www.gnu.org/software/libc/,並下載最新的glibc庫,
當前最新版是glibc-2.24.tar.gz,下載完畢並解壓。
提取加壓後的目錄\glibc-2.24\posix\下的4個源檔案getopt.h/getopt.c/getopt_int.h/
getopt_init.c,。
圖 提取getopt()相關檔案
啟動Visual Studio 2015,選擇菜單【File】->【New】->【Project...】,
準備建立一個新的預設工程項目,項目類型為【Visual C++】→【Win32Console Application】。
建立新的預設工程項目完畢之後,切換到資源管理員畫面,將以上4個檔案複製到新項目所在目錄,並添加到工程項目中,。
圖 添加getopt()源檔案
檔案添加完畢之後,我們試著編譯一下看看,果不其然,檔案getopt.c出現了編譯錯誤:
getopt.c(71): fatal errorC1083: Cannot open include file: ‘gettext.h‘:No such file or directory
首先需要修改的是沒有“gettext.h”這個標頭檔的問題。修改方法為直接將其注釋掉或刪除,然後修改後面的宏定義。
將下面的原始代碼(大概在70行):
1 2 3 4 5 6 |
#ifdef _LIBC # include <libintl.h> #else # include "gettext.h" # define _(msgid) gettext (msgid) #endif |
修改為:
1 2 3 4 5 |
#ifdef _LIBC # include <libintl.h> #else # define _(msgid) (msgid) #endif |
修改完畢,繼續編譯一下看看,出現如下編譯錯誤,。
圖 編譯錯誤alloca無法識別
錯誤的文字描述為:
getopt.c(568): warningC4013: ‘alloca‘ undefined; assumingextern returning int
error LNK2019: unresolved external symbol _allocareferenced in function __getopt_internal_r
可以發現,這裡出錯的原因是alloca這個函數沒有定義,那麼alloca函數是什麼意思呢?
原來alloca是一個記憶體配置函數,與malloc、calloc、realloc類似,但是注意一個重要的區別,
alloca函數是在棧(stack)上申請空間,用完馬上就釋放。
一般情況下,函數alloca包含在標頭檔malloc.h中,在某些系統中被定義為內建函式_alloca的宏定義。
既然已經知道原型了,那麼修改alloca為_alloca即可解決問題,。
圖 修改為_alloca解決編譯錯誤
繼續添加getopt_long()/getopt_long_only()的定義,這兩個函數在getopt.h檔案中聲明了,
但是其定義在getopt1.c中,可以直接將getopt1.c檔案也拿過來用,但是因為這個檔案中的內容不多,
為了減少檔案的數量,直接將其中有用的部分拷貝到getopt.c檔案中是個不錯的主意。
檔案getopt1.c中要拷貝的內容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
int getopt_long (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc,argv,options,long_options,opt_index,0,0); } int _getopt_long_r (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index, struct _getopt_data *d) { return _getopt_internal_r(argc,argv,options,long_options,opt_index, 0, d, 0); } /* Like getopt_long, but ‘-‘ as well as ‘--‘ can indicate a long option. If an option that starts with ‘-‘ (not ‘--‘) doesn‘t match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal(argc,argv,options,long_options,opt_index,1,0); } int _getopt_long_only_r (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index, struct _getopt_data *d) { return _getopt_internal_r(argc,argv,options,long_options,opt_index, 1, d, 0); } |
將以上代碼拷貝到檔案getopt.c中函數getopt()定義之後即可,修改完畢編譯,一切OK!
至此函數getopt()移植結束。經過上面的修改,可以進行一些簡單的測試進行驗證,
測試案例不用自己寫了,在檔案getopt.c和getopt1.c檔案中都有,直接拿過來用就可以。
至此,重建的4個檔案:getopt.h/getopt.c/getopt_int.h/getopt_init.c就是需要的命令列解析原始碼檔案,可以用在Windows系統下。
至此,針對自己開發modbus poll工具的命令列解析功能基本實現了。
接下來,將進行功能部分的程式碼分析和調試。
Modbus軟體開發實戰指南 之 開發自己的Modbus Poll工具 - 2