argparse基本介绍
熟悉Python的工作者可能知道argparse库用于解析程序参数非常方便。而在C语言这里同样也有一些开源工作者开发C语言的argparse库(有不止一个),用于解析参数命令。这篇文章中,笔者基于对开源工作Github cofyc 的 argparse的学习,写一篇教程供读者上手。由于该作者并没有提供详细的教程和说明,笔者通过对该库的调试,将要点分享给大家。
注:本文仅以上手为目标,对于argparse中callback等更多功能暂不予以介绍
本文默认读者已经熟悉C语言,可以独立编写代码,并掌握编译器的使用
还有一个C++的argparse库,Github主页,只有一个头文件,功能相对丰富。开发者的教程也较为详细,读者可以根据链接自行学习。
模板
对于已经熟悉C语言参数argc argv知识、可以自行从CMake源码构建和安装该库的读者,可以直接用笔者下方的模板开发使用argparse库的程序,节约读者时间。注意编译的时候链接argparse库。
#include<stdio.h>
#include<stdlib.h>
#include<argparse.h>
int main(int argc, const char *argv[])
{
/*
在--help|-h帮助信息中打印在开头,解释程序用法
usages[]数组最后一个元素必须为NULL
*/
const char *usages[] = {
"<your program name> [--options]",
"<your program name> [-options]",
"<your program name> -h|--help",
NULL
};
/*
准备相关参数,接收参数输入
*/
int param_integer = 3; /* 当没有接收到参数的时候,以变量提前的定义作为默认值 */
float param_float = 0.;
char *param_string = "default string";
int param_bool = 0;
/* 如果不熟悉位运算,关于bit相关的参数可以忽略,它可以用bool参数代替 */
int param_bit1 = 0, param_bit2 = 0;
const int bit2_shift0 = 1<<0, bit2_shift1 = 1<<1, bit2_shift2 = 1<<2;
/*
参数列表,必须以OPT_HELP()开头、OPT_END()结尾
--help参数说明部分以此列表为序
*/
struct argparse_option options[] = {
OPT_HELP(),
/* 在--help信息解释参数信息里,作为小段标题 */
OPT_GROUP("basic options"),
/*
输入类型:
字符 字符串 指针 字符串
分别表示:短参数名 长参数名 指向接收参数的指针 帮助信息里的解释
如果短参数名设置为数字0,则表示不设置短参数名
*/
OPT_INTEGER('n', "integer", ¶m_integer, "integer number", NULL),
OPT_FLOAT('f', "float", ¶m_float, "float number", NULL),
OPT_STRING('s', "string", ¶m_string, "string value", NULL),
OPT_BOOLEAN('b', "do", ¶m_bool, "true or false", NULL),
/*
使用位运算的参数
*/
OPT_GROUP("bit options"),
/* 如果--bit1选项存在,则param_bit1与第6个参数进行位或运算 */
OPT_BIT(0, "bit1", ¶m_bit1, "bit 1", NULL, 1<<0),
/* 最后一个参数OPT_NONEG表示不存在--no-bit2-0 */
OPT_BIT(0, "bit2-0", ¶m_bit2, "bit 2 left shift 0", NULL, bit2_shift0, OPT_NONEG),
/* 如没有OPT_NONEG,则可以设置--no-bit2-1 */
OPT_BIT(0, "bit2-1", ¶m_bit2, "bit 2 left shift 1", NULL, bit2_shift1),
OPT_BIT(0, "bit2-2", ¶m_bit2, "bit 2 left shift 2", NULL, bit2_shift2),
OPT_END(),
};
/*
string_start: 帮助信息打印在开头的内容
string_end: 帮助信息打印在结尾的内容
*/
char *string_start="description start",
*string_end="description end";
/* 参数解析部分 */
struct argparse argparse;
argparse_init(&argparse, options, usages, 0);
argparse_describe(&argparse, string_start, string_end);
argc = argparse_parse(&argparse, argc, argv);
/* 使用参数内容,以下可删除 */
printf("integer:\t%d\n", param_integer);
printf("float:\t%f\n", param_float);
printf("string:\t%s\n", param_string);
printf("bool:\t%d\n", param_bool);
printf("bit1:\t%d\n", param_bit1);
printf("bit2:\t%d\n", param_bit2);
return 0;
}
工作、开发需求
对于参数命令解析,我们通常对解析库有如下要求
- 使用
-h或’–help’,打印程序信息、参数说明,即帮助说明 - 自动处理错误。比如识别不存在的参数、输入错误类型
- 可以添加任意数量的自定义参数
- 可以引入功能相同的长参数名和短参数名,并以
--和-为开头前缀作为区分。比如--input和-i - 可以自定义参数的默认值|缺省值,即在程序没有接收到参数的时候,能自动给该参数添加值
- 跨平台。可以支持Windows、Linux、Mac系统
不过argparse似乎并没有记录参数输入顺序的功能。
基本知识
已经熟悉这一部分的读者可以略过,直接开始下文argparse的安装、代码学习
argc & argv
C语言主函数的定义一般要用到argc和argv,如下所示。这里char采用了const char,是为了避免编译器关于char类型报一个warning的麻烦。
int main(int argc, const char *argv[])
其中argc表示argv中接收的参数数目,而argv为接收的一系列字符串的字符串数组。可以使用以下程序来尝试。
/* main.c */
#include<stdio.h>
int main(int argc, char *argv[])
{
for (i=0; i<argc; i++){
printf("%s\n", argv[i]);
}
return 0;
}
编译上述代码为可执行文件main。我们以下述命令为例(假设在Linux运行)
./main -a --b 1 --c
则输出
./main
-a
--b
1
--c
可以看到C语言程序将./main -a --b 1 --c按空格分隔为5个部分。我们解析这些参数的目标,就是以加-和--的参数选项和输入为程序的控制参数,方便我们运行程序。而不用因为每次运行的控制条件不同,修改代码后重新编译程序。
选项参数的一般用法
在开发中,我们一般用-连接一个短的参数名,用--连接一个长的参数名。比如-a -b -c,--enable-xxx。有时候我们希望一个长参数名和一个短参数名用法一样。比如-i和--input都能表示输入,-o和--output都表示输出。
有些参数我们希望表示
1
^1
1接收后面一个参数,比如-i file。不过本文介绍的argparse好像并不能同时存在较多的相同参数,比如-i file1 -i file2这种。本文开头提到的C++的一个argparse库是有这个功能的。有些我们希望表示
2
^2
2控制选项,与后一个参数无关。比如--enable-xxx --disable-xxx。
当程序接收到-h或--help选项,开发人员通常停止运行程序其他部分,转而打印程序帮助信息。这些信息通常包括程序用法usage、程序介绍describe、选项option解释等。
argparse库的编译安装
argparse库没有现成的二进制文件,需要通过源码编译安装。编译安装需要如下条件
-
编译器,如Windows的
Visual Studio、MinGW等,Linux的GCC等,Mac的Xcode(包含clang)等。 -
CMake,参见CMake网站下载安装。
按本文开头argparse库的Github链接,下载源码。
cd <src>
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build .
编译后,将静态链接库复制到你需要的lib路径下,头文件argparse.h放在你的头文件搜索路径include下。按需编译安装动态链接库。
熟悉CMake的读者应该会意识到argparse的安装代码是不完善的,无法运行cmake --install .,只能手动复制文件。
argparse库介绍
基本选项
不同参数类型的选项接收有如下几种
OPT_INTEGER
OPT_FLOAT
OPT_STRING
OPT_BOOL
OPT_BIT
整数、浮点、字符串类型的选项
接收整数。第1个'n'和第2个"integer"表示可以同时存在相同功能的-n和--integer选项。其后接收的整数会被保存在整数变量param_integer中。在帮助信息里,会根据第4个选项打印这个选项的介绍。第5个参数为函数指针,作为callback,本文不作介绍。
int param_integer = 3;
OPT_INTEGER('n', "integer", ¶m_integer, "integer number", NULL);
默认值:当程序没有接收到-n或--integer选项,则param_integer采用已经定义的值3。
取消短参数名:可以将第一个'n'替换为0,则将不存在-n这个选项。
取消长参数名:将第2个"integer"替换为NULL。
其他类型选项以此类推
float param_float = 0.;
OPT_FLOAT('f', "float", ¶m_float, "float number", NULL);
char *param_string = "default string";
OPT_STRING('s', "string", ¶m_string, "string value", NULL);
Bool类型的选项
int param_bool = 0;
OPT_BOOLEAN('b', "do", ¶m_bool, "true or false", NULL);
以上定义与整数、浮点、字符串选项相同。不过OPT_BOOLEAN最后可以添加OPT_NONEG,如下所示。如果没有设置OPT_NONEG,则可以选用--do或--no-do(即--do选项中间加入了no-,表示否定这个选项的功能)两种选项之一;如果设置了OPT_NONEG,则只能用--do。
OPT_BOOLEAN('b', "do", ¶m_bool, "true or false", NULL, OPT_NONEG);
位类型的选项
读者若不熟悉C语言的位运算,则不必学习这一部分。这一部分的功能基本可以被OPT_BOOLEAN功能替代。
由于在二进制中,一个整数可以包含多个0和1(如32位整数就有32个0和1),那么一个整数变量相当于可以记录多个Bool类型,以此节约内存空间、减少变量数目。位运算功能就可以作为很好的替代。
int param_bit = 0;
OPT_BIT(0, "bit-0", ¶m_bit, "bit left shift 0", NULL, 1<<0, OPT_NONEG);
OPT_BIT(0, "bit-1", ¶m_bit, "bit left shift 1", NULL, 1<<1);
OPT_BIT(0, "bit-2", ¶m_bit, "bit left shift 2", NULL, 1<<2);
当程序接收到相应的选项,则param_bit与第5个参数作按位或运算。
位选项同样可以使用OPT_NONEG。OPT_NONEG的用法参见Bool类型的选项的介绍。
其他必要参数
以下变量事先定义,方便后面argparse在初始化、解析的时候直接调用。
构建选项列表
按照上文整数、浮点、字符串、Bool类型、位类型的选项,我们输入到struct argparse_option类型的数组options,如下所示。帮助信息里这些选项的介绍顺序就是按照变量options的列举顺序。其中OPT_HELP()和OPT_END()放在开头和结尾。OPT_GROUP是在帮助信息里打印的小标题,比如"bit options"就会打印在--do选项和--bit1选项之间。
struct argparse_option options[] = {
OPT_HELP(),
OPT_GROUP("basic options"),
OPT_INTEGER('n', "integer", ¶m_integer, "integer number", NULL),
OPT_FLOAT('f', "float", ¶m_float, "float number", NULL),
OPT_STRING('s', "string", ¶m_string, "string value", NULL),
OPT_BOOLEAN('b', "do", ¶m_bool, "true or false", NULL),
OPT_GROUP("bit options"),
OPT_BIT(0, "bit1", ¶m_bit1, "bit 1", NULL, 1<<0),
OPT_BIT(0, "bit2-0", ¶m_bit2, "bit 2 left shift 0", NULL, bit2_shift0, OPT_NONEG),
OPT_BIT(0, "bit2-1", ¶m_bit2, "bit 2 left shift 1", NULL, bit2_shift1),
OPT_BIT(0, "bit2-2", ¶m_bit2, "bit 2 left shift 2", NULL, bit2_shift2),
OPT_END(),
};
usages
打印在帮助信息开头的信息,让使用者了解程序的用法。这个字符串数组可以添加任意数量的字符串,但最后必须以NULL结尾(否则打印帮助信息会出bug中断)。打印帮助的时候,argparse会将数组usages中的字符串一行一行打印,读到NULL则终止这一段的打印。
const char *usages[] = {
"<program name> [--options]",
"<program name> [-options]",
NULL
}
describe
char *string_start="description start",
*string_end="description end";
这两个字符串会用于帮助信息打印。其中string_start会打印在usages之后,string_end打印在帮助信息的末尾。
argparse的初始化、解析
以上变量options、usages、string_start、string_end可以放在下方代码中。其中argparse_parse函数开始对argc和argv内容进行解析。
struct argparse argparse;
argparse_init(&argparse, options, usages, 0);
argparse_describe(&argparse, string_start, string_end);
argc = argparse_parse(&argparse, argc, argv);
完整代码参见本文开头的模板。
编译、链接
以编译器GCC为例
gcc main.c -o main -largparse
注:如果gcc加上-Wall -Wextra参数,会报一个warning,说这些OPT_XXX最后应该有一个intptr_t data参数。实际上这个参数只对OPT_BIT有效,因为argparse源码里只有对OPT_BIT的处理中用到了这个data。因此这个warning可以不用管。
现在,你可以测试上述编译程序。以下以Linux为例。
打印帮助信息
./main -h
./main --help
成功的参数
./main
./main -n 1
./main -n 2
./main --integer 4
./main -f 4.5
./main -s new-string
./main --do
./main --no-do
失败的参数
./main --no-bit1
3207

被折叠的 条评论
为什么被折叠?



