C语言参数解析库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", &param_integer, "integer number", NULL),
        OPT_FLOAT('f', "float", &param_float, "float number", NULL),
        OPT_STRING('s', "string", &param_string, "string value", NULL),
        OPT_BOOLEAN('b', "do", &param_bool, "true or false", NULL),
        /*
        使用位运算的参数
        */
        OPT_GROUP("bit options"),
        /* 如果--bit1选项存在,则param_bit1与第6个参数进行位或运算 */
        OPT_BIT(0, "bit1", &param_bit1, "bit 1", NULL, 1<<0),
        /* 最后一个参数OPT_NONEG表示不存在--no-bit2-0 */
        OPT_BIT(0, "bit2-0", &param_bit2, "bit 2 left shift 0", NULL, bit2_shift0, OPT_NONEG),
        /* 如没有OPT_NONEG,则可以设置--no-bit2-1 */
        OPT_BIT(0, "bit2-1", &param_bit2, "bit 2 left shift 1", NULL, bit2_shift1),
        OPT_BIT(0, "bit2-2", &param_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;
}

工作、开发需求

对于参数命令解析,我们通常对解析库有如下要求

  1. 使用-h或’–help’,打印程序信息、参数说明,即帮助说明
  2. 自动处理错误。比如识别不存在的参数、输入错误类型
  3. 可以添加任意数量的自定义参数
  4. 可以引入功能相同的长参数名和短参数名,并以---为开头前缀作为区分。比如--input-i
  5. 可以自定义参数的默认值|缺省值,即在程序没有接收到参数的时候,能自动给该参数添加值
  6. 跨平台。可以支持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库没有现成的二进制文件,需要通过源码编译安装。编译安装需要如下条件

  1. 编译器,如Windows的Visual StudioMinGW等,Linux的GCC等,Mac的Xcode(包含clang)等。

  2. 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", &param_integer, "integer number", NULL);

默认值:当程序没有接收到-n--integer选项,则param_integer采用已经定义的值3

取消短参数名:可以将第一个'n'替换为0,则将不存在-n这个选项。

取消长参数名:将第2个"integer"替换为NULL

其他类型选项以此类推

float param_float = 0.;
OPT_FLOAT('f', "float", &param_float, "float number", NULL);

char *param_string = "default string";
OPT_STRING('s', "string", &param_string, "string value", NULL);

Bool类型的选项

int param_bool = 0;
OPT_BOOLEAN('b', "do", &param_bool, "true or false", NULL);

以上定义与整数、浮点、字符串选项相同。不过OPT_BOOLEAN最后可以添加OPT_NONEG,如下所示。如果没有设置OPT_NONEG,则可以选用--do--no-do(即--do选项中间加入了no-,表示否定这个选项的功能)两种选项之一;如果设置了OPT_NONEG,则只能用--do

OPT_BOOLEAN('b', "do", &param_bool, "true or false", NULL, OPT_NONEG);

位类型的选项

读者若不熟悉C语言的位运算,则不必学习这一部分。这一部分的功能基本可以被OPT_BOOLEAN功能替代。

由于在二进制中,一个整数可以包含多个01(如32位整数就有32个01),那么一个整数变量相当于可以记录多个Bool类型,以此节约内存空间、减少变量数目。位运算功能就可以作为很好的替代。

int param_bit = 0;
OPT_BIT(0, "bit-0", &param_bit, "bit left shift 0", NULL, 1<<0, OPT_NONEG);
OPT_BIT(0, "bit-1", &param_bit, "bit left shift 1", NULL, 1<<1);
OPT_BIT(0, "bit-2", &param_bit, "bit left shift 2", NULL, 1<<2);

当程序接收到相应的选项,则param_bit与第5个参数作按位或运算。

位选项同样可以使用OPT_NONEGOPT_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", &param_integer, "integer number", NULL),
        OPT_FLOAT('f', "float", &param_float, "float number", NULL),
        OPT_STRING('s', "string", &param_string, "string value", NULL),
        OPT_BOOLEAN('b', "do", &param_bool, "true or false", NULL),
        OPT_GROUP("bit options"),
        OPT_BIT(0, "bit1", &param_bit1, "bit 1", NULL, 1<<0),
        OPT_BIT(0, "bit2-0", &param_bit2, "bit 2 left shift 0", NULL, bit2_shift0, OPT_NONEG),
        OPT_BIT(0, "bit2-1", &param_bit2, "bit 2 left shift 1", NULL, bit2_shift1),
        OPT_BIT(0, "bit2-2", &param_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的初始化、解析

以上变量optionsusagesstring_startstring_end可以放在下方代码中。其中argparse_parse函数开始对argcargv内容进行解析。

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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值