接上一篇文章的內容。
看了前面需求提到的復雜的命令行解析功能,很多人立馬開始發怵,其實大可不必。
我們都知道,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 |
#include <stdio.h> |
以上作為參照,可見調用函數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++】→【Win32 Console Application】。
創建新的默認工程項目完畢之后,切換到資源管理器畫面,將以上4個文件復制到新項目所在目錄,並添加到工程項目中,如圖所示。
圖 添加getopt()源文件
文件添加完畢之后,我們試着編譯一下看看,果不其然,文件getopt.c出現了編譯錯誤:
getopt.c(71): fatal error C1083: Cannot open include file: 'gettext.h': No such file or directory
首先需要修改的是沒有“gettext.h”這個頭文件的問題。修改方法為直接將其注釋掉或刪除,然后修改后面的宏定義。
將下面的原始代碼(大概在70行):
1 |
#ifdef _LIBC |
修改為:
1 |
#ifdef _LIBC |
修改完畢,繼續編譯一下看看,出現如下編譯錯誤,如圖所示。
圖 編譯錯誤alloca無法識別
錯誤的文字描述為:
getopt.c(568): warning C4013: 'alloca' undefined; assuming extern returning int
error LNK2019: unresolved external symbol _alloca referenced 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 |
int |
將以上代碼拷貝到文件getopt.c中函數getopt()定義之后即可,修改完畢編譯,一切OK!
至此函數getopt()移植結束。經過上面的修改,可以進行一些簡單的測試進行驗證,
測試用例不用自己寫了,在文件getopt.c和getopt1.c文件中都有,直接拿過來用就可以。
至此,重新生成的4個文件:getopt.h/getopt.c/getopt_int.h/getopt_init.c就是需要的命令行解析源代碼文件,可以用在Windows系統下。
至此,針對自己開發modbus poll工具的命令行解析功能基本實現了。
接下來,將進行功能部分的代碼分析和調試。