c/c++——getopt()函数的用法

 该函数位于头文件<unistd.h>中,函数的原型为int getopt(int argc,char * argv[],const char* optstring);

看到该函数的前两个参数,我们可以立刻联想到main()函数,虽然有时候main()函数的前两个参数会省略,但如果我们需要通过命令行传参的话,就得用上它两了;

在正式介绍getopt()函数之前,我们还得说下与它有关的四个参数:

extern char* optarg;  //该参数指向选项的参数 比如:a.out -a "c++" ,optarg指向的就是"c++"
extern int optind;    //该参数指向下一次检索的位置
extern int opterr;    //该参数表示是否将错误消息输出到stderr,为0时表示不输出,初始值为1
extern int optopt;    //该参数表示不在选项字符串optstring中的选项

首先,我们看个选项字符串的例子,并给出其中的规则:

"ab:c:d::e",该选项字符串对应到命令行的就是
main.out  -a  -b b_argument -c c_argument -d [d_argument] -e 
或者 main.out -a -bb_argument -cc_argument -d [d_argument] -e
通过上述的命令行我们可以看出,选项后面没有冒号的是不用接任何参数,即它对应的参数为null;选项后面有冒号的必须接参数,否则会报错,但是选项和参数可以连在一起;选项后面有两个冒号的,后面可接可不接参数,但是如果接参数的话,则选项和参数之间一定要有空格.

这里得补充一个小知识点,就是如果字符与之对应的整型是相等,至于打印到终端的形式,取决于我们怎么看,%c,就是字符,%d,就是整型。

下面,通过实例进行更加详细的讲解;

实验环境为linux,用的编译器为g++,文件名为test.cpp。

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
    
    int ch;
    printf("optind:%d,opterr:%d\n\n",optind,opterr);
       while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
       {
	
        printf("optind: %d\n", optind);
           switch (ch) 
        {
               case 'a':
                       printf("HAVE option: -a\n\n");  
		       printf("The argument of -a is %s\n\n",optarg); 
                       break;
               case 'b':
                       printf("HAVE option: -b\n"); 
                       printf("The argument of -b is %s\n\n", optarg);
                       break;
               case 'c':
                       printf("HAVE option: -c\n");
                       printf("The argument of -c is %s\n\n", optarg);
                       break;
               case 'd':
                   printf("HAVE option: -d\n");
                     break;
              case 'e':
                    printf("HAVE option: -e\n");
                    printf("The argument of -e is %s\n\n", optarg);
                  break;
              case '?':
                       printf("Unknown option: %c\n\n",(char)optopt);
		       printf("The arguement of unknown option is %s\n\n",optarg);
                       break;
               }
       }

首先,编译源文件,g++ test.cpp

默认输出为 a.out

执行如下命令: ./a.out   -a   "a_argument "   -b  "b_argument"  -cc_argument   -e  "e_argument"   -f"f_argumetn"-z

输出如下:

optind:1,opterr:1

optind: 2
HAVE option: -a

The argument of -a is (null)

optind: 5
HAVE option: -b
The argument of -b is b_argument

optind: 6
HAVE option: -c
The argument of -c is c_argument

./a.out: invalid option -- 'e'
optind: 7
Unknown option: e

The arguement of unknown option is (null)

optind: 9
Have option: -f
The argument of -f is f_argument

./a.out: invalid option -- 'z'
optind: 10
Unknown option: z

The arguement of unknown option is (null)

 

 

结果分析:首先,optind和opterr的初始值都为1,命令行的第一个参数为可执行文件本身,而optind会指向选项的位置,会跳过第一个参数,所以初始值为1,opterr的默认初始值就为1;进入while循环,optind指向选项 -a ,但从命令行,可以看出该选项后面是不接参数的,即便该选项后面接的是形如参数的样式,其对应的参数也为空;当getopt()返回'a'时,此时的optind会指向下一个检索位置,于是它的值就变为2,但我们知道argv[2]是参数,不是选项,于是getopt()函数会寻找下一个选项,于是找到了 -b 选项,这个后面是必须接参数的,那么其参数就为"b_argument",此时optind指向的位置为5,即 -c,该选项后面必须接参数,即为"c_argument",此时,optind更新为6,指向了选项 -e,从optstring选项字符串中可以看到,其后面接两个冒号(::),从代码的运行结果可以看到其对应的参数也为null,(这里和我在网上查找的资料有出入,好几份资料都显示,对于两个冒号的选项,其后如果接参数的话,不能含有空格,否则会报错);然后继续寻找下一个选项,找到了选项 -f ,其与后面的参数之间没有空格,于是后面的参数就为其参数;紧接着,继续寻找下一个参数,这时找到了-z ,但该参数并没有出现了选项字符串中,于是就有了后面的错误提示,注意,此时getopt()函数返回的是字符问号'?';此时后面已经没有选项,于是函数返回-1,循环结束。

以下部分直接从参考资料中搬过来了。

最后,有个小知识要说明下,getopt()函数会改变argv[]中参数的顺序,经过多次调用该函数,argv[]中的选项和选项的参数会被放置在前,并且optind会指向非选项的位置,通过例子可以更加直观的理解。

 

#include <unistd.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
    
    int i;
    printf("--------------------------\n");
    for(i=0;i<argc;i++)
    {
        printf("%s\n",argv[i]);
    }
    printf("--------------------------\n");
       //int aflag=0, bflag=0, cflag=0;
    
       int ch;
    printf("\n\n");
    printf("optind:%d,opterr:%d\n",optind,opterr);
    printf("--------------------------\n");
       while ((ch = getopt(argc, argv, "ab:c:de::")) != -1)
       {
        printf("optind: %d\n", optind);
           switch (ch) 
        {
               case 'a':
                       printf("HAVE option: -a\n\n");   
                       break;
               case 'b':
                       printf("HAVE option: -b\n"); 
                       printf("The argument of -b is %s\n\n", optarg);
                       break;
               case 'c':
                       printf("HAVE option: -c\n");
                       printf("The argument of -c is %s\n\n", optarg);
                       break;
               case 'd':
                   printf("HAVE option: -d\n");
                     break;
              case 'e':
                    printf("HAVE option: -e\n");
                    printf("The argument of -e is %s\n\n", optarg);
                  break;
              case '?':
                       printf("Unknown option: %c\n",(char)optopt);
                       break;
               }
       }
    
       printf("----------------------------\n");
      printf("optind=%d,argv[%d]=%s\n",optind,optind,argv[optind]);

    printf("--------------------------\n");
    for(i=0;i<argc;i++)
    {
        printf("%s\n",argv[i]);
    }
    printf("--------------------------\n");
    

}

命令行:./main zheng -b "qing er" han -c123 qing

 

输出结果为:

--------------------------
./main
zheng
-b
qing er
han
-c123
qing
--------------------------


optind:1,opterr:1
--------------------------
optind: 4
HAVE option: -b
The argument of -b is qing er

optind: 6
HAVE option: -c
The argument of -c is 123

----------------------------
optind=4,argv[4]=zheng
--------------------------
./main
-b
qing er
-c123
zheng
han
qing
--------------------------

 

可以看到最开始argv[]内容为:

./main
zheng
-b
qing er
han
-c123
qing

 

在执行了多次getopt后变成了

./main
-b
qing er
-c123
zheng
han
qing

我们看到,被getopt挑出的选项和对应的参数都按顺序放在了数组的前面,而那些既不是选项又不是参数的会按顺序放在后面。而此时optind为4,即指向第一个非选项也非选项的参数,zheng。

写的有些乱,也是作为资料供自己参考,如果哪里写的不好,还请指出!!

来源: http://www.cnblogs.com/qingergege/p/5914218.html

### C++ 实现命令行参数解析 #### 使用 `Boost.Program_options` 库解析命令行参数 为了使用 Boost 程序选项库,需要包含 `<boost/program_options.hpp>` 头文件。下面是一个简单的例子展示如何定义和解析命令行参数: ```cpp #include <iostream> #include <boost/program_options.hpp> namespace po = boost::program_options; int main(int ac, char* av[]) { try { // 定义选项描述对象 po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("compression", po::value<int>(), "set compression level"); // 存储解析的结果到 vm 变量中 po::variables_map vm; store(po::parse_command_line(ac, av, desc), vm); notify(vm); if (vm.count("help")) { std::cout << desc << "\n"; return 1; } if (vm.count("compression")) { std::cout << "Compression level was set to " << vm["compression"].as<int>() << ".\n"; } else { std::cout << "Compression level not set.\n"; } } catch(std::exception& e) { std::cerr << "error: " << e.what() << "\n"; return 1; } catch(...) { std::cerr << "Exception of unknown type!\n"; } return 0; } ``` 此代码展示了如何利用 Boost 提供的功能来轻松管理复杂的命令行接口[^3]。 #### 使用标准库中的 `getopt()` 或 `getopt_long()` 对于较为简单的需求或者希望保持轻量化的情况下,可以选择不依赖外部库而采用 POSIX 标准提供的 `getopt()` 函数来进行基本的命令行参数解析工作。这里给出一段基于 `getopt_long()` 的实例代码片段用于支持长短混合形式的开关选项: ```cpp #include <unistd.h> /* For getopt */ #include <stdlib.h> /* For exit */ struct option longopts[] = { {"verbose", no_argument, NULL, 'v'}, {"output-file", required_argument, NULL, 'o'}, {NULL, 0, NULL, 0 } }; int main(int argc, char **argv) { int opt; while ((opt = getopt_long(argc, argv, "vo:", longopts, NULL)) != -1) { switch(opt){ case 'v': printf("--verbose or -v option used\n"); break; case 'o': printf("--output-file=%s or -o %s specified output file\n", optarg, optarg); break; default: fprintf(stderr,"Unknown argument encountered\n"); exit(EXIT_FAILURE); } } // 打印剩余非选项参数 for (; optind < argc; ++optind) printf("Non-option argument %s\n", argv[optind]); return EXIT_SUCCESS; } ``` 这段代码演示了怎样通过 `getopt_long()` 来处理既定模式下的命令行输入,并能够识别特定标记及其可能携带的数据值[^2]。 #### 自定义方式遍历 `argc` 和 `argv` 最基础也是最为灵活的方式就是直接访问传入给 `main` 函数的两个形参——`argc`(argument count)以及指向字符串数组指针的`argv`(argument vector),以此手动分析每一个传递过来的位置参数。这种方式虽然原始但却提供了最大的灵活性去适应各种特殊场景的要求[^5]: ```cpp #include <iostream> int main(int argc, char const *argv[]) { std::cout << "Number of arguments provided: " << argc << '\n'; for (size_t i = 0; i < static_cast<size_t>(argc); ++i) std::cout << "Argument #" << i << ": " << argv[i] << '\n'; return 0; } ``` 上述三种方法各有优劣,在实际项目开发过程中可以根据具体需求和个人偏好做出合适的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值