程序参数解析C/C++库 The Lean Mean C++ Option Parser
开发中我们经常使用程序参数,根据参数的不同来实现不同的功能。POSIX和GNU组织对此都制定了一些标准,为了我们程序更为通用标准,建议遵循这些行业内的规范,本文介绍的开源库The Lean Mean C++ Option Parser就可以很好满足我们的需求。
The Lean Mean C++ Option Parser在线文档:(下载头文件和相关demo)The Lean Mean C++ Option Parser: Main Page (sourceforge.net)
一、优点:
1.该库只有一个头文件,只需要包含“#include "optionparser.h"即可”;
2.该库是独立的,没有任何依赖关系,甚至不依赖C或C++标准库;
3.与getopt()和派生函数不同,它不会强制您按顺序循环使用选项,使用更加便捷
二、选项语法:
1.The Lean Mean C++ Option Parser 遵循POSIX getopt()约定,支持GNU风格的getopt_long()长选项以及Perl风格的单减长选项(getopt_long_only());
2.短可选项的格式:-X ,X是任意字符;
3.短可选项的可以组合,比如:-X -Y 和 -XY是一样的;
4.短可选项可以采用单独的或者附加方式取参数,比如:-X foo -Xfoo 。如果注册X为长可选项 ,解析器就可以接受 -X=foo 的格式;
5.最后一个带参数的短可选项可以和其他的组合,比如: -ABCXfoo -ABCX foo (foo 是-X 可选项的参数);
6.长可选项的格式 -可选项名称 ;
7.长可选项格式的可选项名称可以是任意名称,包含任意字符,即使“=”也是可以的,但是不建议这样做;
8.长可选项可以缩写,只要缩写是明确的。此外还可以设置缩写的最小长度;
9.长可选项可以“-”,“--”开始;
10.长可选项可以采用单独的(-option arg)或者附加的(-option=arg)参数格式。附加格式中,必须要使用“=” ;
11.空字符串可以作为附加格式的长可选选项的参数:“-option= ”。作为参数的空字符串和完全没有参数之间的区别需要注意;
12.允许将空字符串作为长可选项和短可选项的单独参数;
13.短可选项和长可选项都可以以“-”字符开头。例如:-X-X, -X -X, -long-X=-X .这三种情况下 的参数都是 -X;
14.如果使用内置的Arg::Optional,则必须附加可选项的参数;
15.特殊选项–(即没有名称)终止选项列表。后面的所有内容都是非选项参数,即使它以“-”字符开头。–本身不会出现在解析结果中;
16.第一个不以“-”开头并且不属于前面的选项的参数将终止选项列表,并且是第一个非选项参数。接下来所有命令行参数都被视为非选项参数,即使它们以“-”开头。
注意:这种行为是由POSIX强制执行的,但GNU getopt()只有在明确请求时(例如通过设置POSIXLY_CORRECT)才会执行。您可以通过将true作为第一个参数传递给例如Parser::parse()来启用GNU行为。
17.看起来像选项(即“-”后面至少有1个字符)但实际上不是的参数不被视为非选项参数。它们被视为未知选项,并被收集到用于错误报告的未知选项列表中。这意味着,为了传递以减号字符开头的第一个非选项参数,需要使用– 特殊选项,例如:
program -x -- --strange-filename
在这个例子中, –strange-filename 是非选项参数。如果前面的‘-’ 没有,它将被视为未知选项。
三、使用:
熟悉以下几个类,即可快速使用该库,下面对这些类型进行介绍:
1.Descriptor
描述一个选项、它的帮助文本(用法)以及应该如何解析它。下面是一个示例:
enum OptionIndex {CREATE, ...};
enum OptionType {DISABLE, ENABLE, OTHER};
const option::Descriptor usage[] = {{ CREATE, // indexOTHER, // type"c", // shortopt"create", // longoptArg::None, // check_arg"--create Tells the program to create something." // help}, ...
};
index:解析器解析后该可选项所在数组的索引。建议使用枚举增强阅读性,如上示例。
描述符具有相同索引的命令行选项将按照它们在命令行上出现的顺序出现在相同的链表中。如果有多个长选项别名引用同一个选项,请为它们的描述符指定相同的索引。
如果你有意思完全相反的选项(例如–enable foo和–disable foo),你也应该给它们相同的索引,但通过不同的类型值来区分它们。这样,它们最终会出现在同一个列表中,你就可以只取列表的最后一个元素并使用其类型。通过这种方式,你可以稍后在命令行上的开关会覆盖以前的开关,而无需手动编码
type:用于区分相同index的可选项。
shortopt:此字符串中的每个字符都将被接受为短选项字符。该字符串不得包含减号字符“-”,否则将出现未定义的行为。如果此描述符不应包含短选项字符,请使用空字符串“”。此处不允许使用NULL!
longopt:长可选项名称。如果此描述符不应具有长选项名称,请使用空字符串“”。此处不允许使用NULL!虽然shortopt允许多个短选项字符,但每个描述符只能有一个长选项名称。如果有多个长选项名称引用同一个选项,请使用具有相同索引和类型的单独描述符。您可以在这样的别名描述符中重复短选项字符,但没有必要这样做。
check_arg:对于每个匹配shortopt或longopt的选项,将调用此函数来检查该选项的潜在参数。
2.Arg
每个选项参数都有一个如此函数设置进Descriptor,用于检查选项参数有效性。
typedef ArgStatus (*CheckArg)(const Option& option, bool msg);
它用于检查选项是否可以接受潜在的参数。即使没有参数,该函数也会被调用。在这种情况下,option.arg将为NULL。
如果msg为true,并且函数确定参数是不可接受的,并且这是一个致命错误,那么它应该在返回ARG_ILEGAL之前向用户输出一条消息。如果msg为false,则该函数应保持静音(否则将收到重复的消息)。
ArgStatus的定义如下:
| ARG_NONE | 选项没有参数。 |
| ARG_OK | 参数被选项接受。 |
| ARG_IGNORE | 该参数是不可接受的,但这不是致命的,因为该选项的参数是可选的。 |
| ARG_ILLEGAL | 参数不可以接受,会有致命错误。 |
目前已经预制了几个校验规则,需要更为精准的校验规则可以自己定制,例如:
struct Arg: public option::Arg
{static void printError(const char* msg1, const option::Option& opt, const char* msg2){fprintf(stderr, "ERROR: %s", msg1);fwrite(opt.name, opt.namelen, 1, stderr);fprintf(stderr, "%s", msg2);}static option::ArgStatus Unknown(const option::Option& option, bool msg){if (msg) printError("Unknown option '", option, "'\n");return option::ARG_ILLEGAL;}static option::ArgStatus Required(const option::Option& option, bool msg){if (option.arg != 0)return option::ARG_OK;if (msg) printError("Option '", option, "' requires an argument\n");return option::ARG_ILLEGAL;}static option::ArgStatus NonEmpty(const option::Option& option, bool msg){if (option.arg != 0 && option.arg[0] != 0)return option::ARG_OK;if (msg) printError("Option '", option, "' requires a non-empty argument\n");return option::ARG_ILLEGAL;}static option::ArgStatus Numeric(const option::Option& option, bool msg){char* endptr = 0;if (option.arg != 0 && strtol(option.arg, &endptr, 10)){};if (endptr != option.arg && *endptr == 0)return option::ARG_OK;if (msg) printError("Option '", option, "' requires a numeric argument\n");return option::ARG_ILLEGAL;}
};
3.Stats
确定用于Parser的缓冲区和选项数组的最小长度。因为Parser不使用动态内存,所以必须预先分配其输出数组。如果您不想使用固定大小的数组(可能会变得太小,导致命令行参数被丢弃),可以使用Stats来确定正确的大小。
buffer_max:真实参数个数;
options_max:依赖Descriptor,比Descriptor中的Index数量多一个。
4.Parser
检查参数的有效性,并将其解析为更易于使用的数据结构。
int main(int argc, char* argv[])
{argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if presentoption::Stats stats(usage, argc, argv);option::Option options[stats.options_max], buffer[stats.buffer_max];option::Parser parse(usage, argc, argv, options, buffer);if (parse.error())return 1;if (options[HELP])...
optionsCount():返回有效选项数量;
nonOptionsCount():返回非选项参数数量;
error():返回是否存在解析错误。选项的非法参数(即CheckArg返回ARG_ILEGAL)是一个不可恢复的错误,会中止解析。只有当CheckArg函数返回ARG_ILEGAL时,未知选项才是错误。否则,它们将被收集。
5.Parser
命令行中已解析的选项及其参数(如果有)。
Parser将具有相同Descriptor::索引的所有已解析选项链接在一起,形成一个链表。这使您能够轻松实现处理重复选项和启用/禁用对的所有常见方法。
desc:指向选项的Descriptor;
name:选项的名称;
arg:选项的参数;
namelen:选项名称长度。
示例:
//Test for presence of a switch in the argument vector:
if ( options[QUIET] ) ...
//Evaluate –enable-foo/–disable-foo pair where the last one used wins:
if ( options[FOO].last()->type() == DISABLE ) ...
//Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose):
int verbosity = options[VERBOSE].count();
//Iterate over all –file=<fname> arguments:
for (Option* opt = options[FILE]; opt; opt = opt->next())fname = opt->arg; ...
四、整体示例:
#include "optionparser.h"
#include <sstream>struct Arg : public option::Arg
{static void print_error(const char* msg1,const option::Option& opt,const char* msg2){fprintf(stderr, "%s", msg1);fwrite(opt.name, opt.namelen, 1, stderr);fprintf(stderr, "%s", msg2);}static option::ArgStatus Unknown(const option::Option& option,bool msg){if (msg){print_error("Unknown option '", option, "'\n");}return option::ARG_ILLEGAL;}static option::ArgStatus Required(const option::Option& option,bool msg){if (option.arg != 0 && option.arg[0] != 0){return option::ARG_OK;}if (msg){print_error("Option '", option, "' requires an argument\n");}return option::ARG_ILLEGAL;}static option::ArgStatus Numeric(const option::Option& option,bool msg){char* endptr = 0;if ( option.arg != nullptr ){strtol(option.arg, &endptr, 10);if (endptr != option.arg && *endptr == 0){return option::ARG_OK;}}if (msg){print_error("Option '", option, "' requires a numeric argument\n");}return option::ARG_ILLEGAL;}template<long min = 0, long max = std::numeric_limits<long>::max()>static option::ArgStatus NumericRange(const option::Option& option,bool msg){static_assert(min <= max, "NumericRange: invalid range provided.");char* endptr = 0;if ( option.arg != nullptr ){long value = strtol(option.arg, &endptr, 10);if ( endptr != option.arg && *endptr == 0 &&value >= min && value <= max){return option::ARG_OK;}}if (msg){std::ostringstream os;os << "' requires a numeric argument in range ["<< min << ", " << max << "]" << std::endl;print_error("Option '", option, os.str().c_str());}return option::ARG_ILLEGAL;}static option::ArgStatus String(const option::Option& option,bool msg){if (option.arg != 0){return option::ARG_OK;}if (msg){print_error("Option '", option, "' requires an argument\n");}return option::ARG_ILLEGAL;}};enum optionIndex
{UNKNOWN_OPT,HELP,SAMPLES,INTERVAL,ENVIRONMENT,
};const option::Descriptor usage[] = {{ UNKNOWN_OPT, 0, "", "", Arg::None,"Usage: HelloWorldExample <publisher|subscriber>\n\nGeneral options:" },{ HELP, 0, "h", "help", Arg::None, " -h \t--help \tProduce help message." },{ UNKNOWN_OPT, 0, "", "", Arg::None, "\nPublisher options:"},{ SAMPLES, 0, "s", "samples", Arg::NumericRange<>," -s <num>, \t--samples=<num> \tNumber of samples (0, default, infinite)." },{ INTERVAL, 0, "i", "interval", Arg::NumericRange<>," -i <num>, \t--interval=<num> \tTime between samples in milliseconds (Default: 100)." },{ ENVIRONMENT, 0, "e", "env", Arg::None, " -e \t--env \tLoad QoS from environment." },{ 0, 0, 0, 0, 0, 0 }
};int main(int argc,char** argv)
{argc -= (argc > 0);argv += (argc > 0); // skip program name argv[0] if presentoption::Stats stats(true, usage, argc, argv);std::vector<option::Option> options(stats.options_max);std::vector<option::Option> buffer(stats.buffer_max);option::Parser parse(true, usage, argc, argv, &options[0], &buffer[0]);try{if (parse.error()){throw 1;}if (options[HELP] || options[UNKNOWN_OPT]){throw 1;}const char* type_name = parse.nonOption(0);if (parse.optionsCount() && type_name >= buffer[0].name){throw 2;}}catch(int error){//deal error}return 0;
}
相关文章:
程序参数解析C/C++库 The Lean Mean C++ Option Parser
开发中我们经常使用程序参数,根据参数的不同来实现不同的功能。POSIX和GNU组织对此都制定了一些标准,为了我们程序更为通用标准,建议遵循这些行业内的规范,本文介绍的开源库The Lean Mean C Option Parser就可以很好满足我们的需求…...
Java中的深拷贝和浅拷贝
目录 🍎引出拷贝 🍎浅拷贝 🍎深拷贝 🍎总结 引出拷贝 现在有一个学生类和书包类,在学生类中有引用类型的书包变量: class SchoolBag {private String brand; //书包的品牌private int size; //书…...
大文件上传
上图就是大致的流程一、标题图片上传课程的标题图片Ajax发送请求到后端后端接收到图片使用IO流去保存图片,返回图片的信息对象JS回调函数接收对象通过$("元素id").val(值),方式给页面form表达img标签src属性值,达到上传图片并回显二…...
Python每日一练(20230327)
目录 1. 最大矩形 🌟🌟🌟 2. 反转链表 II 🌟🌟 3. 单词接龙 II 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日…...
Centos7 升级内核到5.10mellanox 编译安装
升级5.10内核 #uname -r 重启后 进入新的内核 进入新的内核信息 直接查看是看不到gcc版本 5.10需要高版本gcc 才可以进行编译...
冯诺依曼,操作系统以及进程概念
文章目录一.冯诺依曼体系结构二.操作系统(operator system)三.系统调用和库函数四.进程1.进程控制块(PCB)2.查看进程3.系统相关的调用4.fork介绍(并发引入)五.总结一.冯诺依曼体系结构 计算机大体可以说是…...
7.网络爬虫—正则表达式详讲
7.网络爬虫—正则表达式详讲与实战Python 正则表达式re.match() 函数re.search方法re.match与re.search的区别re.compile 函数检索和替换检索:替换:findallre.finditerre.split正则表达式模式常见的字符类正则模式正则表达式模式量词正则表达式举例前言&…...
关于位运算的巧妙性:小乖,你真的明白吗?
一.位运算的概念什么是位运算?程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。位运算就是直接操作二进制数,那么有哪些种类的位运算呢?常见的运算符有与(&)、或(|)、异或(^)、…...
【Android车载系列】第5章 AOSP开发环境配置
1 硬件支持 建议空闲内存16G以上,同时硬盘400G以上 内存不够可以使用 Linux 的交换分区2 VMware Workstation安装 https://download3.vmware.com/software/wkst/file/VMware-workstation-full-16.1.1-17801498.exe2.1 Ubuntu镜像 http://mirrors.aliyun.com/ubun…...
个人时间管理网站—Git项目管理
🌟所属专栏:献给榕榕🐔作者简介:rchjr——五带信管菜只因一枚😮前言:该专栏系为女友准备的,里面会不定时发一些讨好她的技术作品,感兴趣的小伙伴可以关注一下~👉文章简介…...
2023最新ChatGPT整理的40道Java高级面试题
2023 年最火的就是 ChatGPT 了,很多同事使用他完成一些代码上的智能提示,也有人使用它发了财《「用ChatGPT年入百万!」各博主发布生财之道,网友:答辩搬运工》、《“躺着就能赚大钱”?ChatGPT火了,有人早就动起坏脑筋》等。 最近我也使用 ChatGPT 写技术文章了,比如:《…...
单机分布式一体化是什么?真的是数据库的未来吗,OceanBase或将开启新的里程碑
一. 数据 我们先说说数据这个东西,这段时间的ChatGPT在全世界的爆火说明了一件事,数据是有用的,并且大量的数据如果有一个合适的LLM大规模语言模型训练之后,可以很高程度的完成很多意想不到的事情。 我们大多数的时候的注意力只…...
100天精通Python丨基础知识篇 —— 03、Python基础知识扫盲(第一个Python程序,13个小知识点)
文章目录🐜 1、Python 初体验Pycharm 第一个程序交互式编程第一个程序🐞 2、Python 引号🐔 3、Python 注释🦅 4、Python 保留字符🐯 5、Python 行和缩进🐨 6、Python 空行🐹 7、Python 输出&…...
springboot逍遥大药房管理系统
084-springboot逍遥大药房管理系统演示录像开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包&a…...
ZYNQ中的GPIO与AXI GPIO
GPIO GPIO—一种外设,对器件进行观测和控制MIO—将来自PS外设和静态存储器接口的访问多路复用到PS引脚上处理器控制外设的方法—通过一组寄存器包括状态寄存器和控制寄存器,这些寄存器都是有地址的,通过这些寄存器的读写进行外设的控制sessi…...
接口导入功能
1.接口api export function import(param) { return fetch({ url: XXX.import, method: POST, headers: { Content-Type: multipart/form-data; }, data: param }) } 2.页面vue 和 js逻辑 <el-button :loading"disable&qu…...
网络安全知识点总结 期末总结
1、信息安全从总体上可以分成5个层次,密码技术 是信息安全中研究的关键点。 2、握手协议 用于客户机与服务器建立起安全连接之前交换一系列信息的安全信道。 3、仅设立防火墙系统,而没有 安全策略 ,防火墙就形同虚设。 4、应用代理防火墙 …...
linux挂载远程目录
服务端操作 # 1、安装NFS程序 yum -y install nfs* rpcbind,在centos6以前自带的yum源中为portmap。 使用yum安装nfs时会下载依赖,因此只要下载nfs即可,无需再下载rpcbind. # 2、查看是否安装了nfs与rpcbind rpm -qa | grep nfs rpm -qa | grep rpc…...
ChatGPT—初识
ChatGPT初识 由于ChatGPT 注册相关的文章被平台限制了,所以有注册相关的问题可以私聊,或者可以代注册 Chat GPT是一款基于GPT模型的对话型AI模型,能够模拟真实的对话风格和行为方式,让人与AI的交互变得更加自然顺畅。下面将从Chat…...
【ArcGIS Pro二次开发】(18):地理处理工具类【Geoprocessing】补遗
ArcGIS Pro SDK 3.0中的Geoprocessing类是用于执行地理处理工具的核心类。地理处理工具是用于执行空间分析、数据转换、数据管理等任务的工具集,包括常见的空间分析工具、栅格处理工具、矢量处理工具、地图制图工具等。 之前有简单记录了下Geoprocessing工具的用法…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
[特殊字符] 手撸 Redis 互斥锁那些坑
📖 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作,想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁,也顺便跟 Redisson 的 RLock 机制对比了下,记录一波,别踩我踩过…...
