C++参数解析参数
《C/C++参数解析》
1. getopt库的使用
getopt用于解析程序命令行输入的参数,可解析短参数和长参数;解析短参数使用getopt()函数,解析长参数可使用getopt_long();
1.1 getopt()解析短参数
getopt()函数用来解析短参数,例如-a
或者-b 100
这样的参数,不能解析长参数,如--ip 198.11.23.45
这样的参数;
函数原型:
int getopt(int argc, char * const argv[], const char *optstring); // -- optstring: 选项字符串,告知getopt()可以处理哪些参数选项以及哪些参数选项有参数值
optstring参数详解:
const char* optstring = "ab:c::";
参数 | 含义 | 格式 |
---|---|---|
a | 单个字符a, 表示参数选项a可有可无,有的话不能加参数值, | -a |
b: | 单个字符加上冒号,表示参数选项b可有可无,如果有的话必须加参数值 | -b100 |
c:: | 单个字符加上两个冒号,表示参数c可以有,也可以无, 有的话既可以加参数,也可以不加参数 (没参数的时候optarg==NULL) |
-c100 |
在getopt函数接收到optstring参数后,会依次检查参数a,b,c是否被传入,当对应的参数被指定以后,就会返回对应的参数名,如果参数带有值的话,可通过以下方法获取参数的值 (getopt提供了一些全局变量,可通过这些全局变量来获取参数值):
- optarg 指向当前参数的值的指针,NULL时表示参数选项没有值
- optind —— 用来记录argv中下一个检索的位置。
- optopt —— 存储不在选项字符串optstring中的参数。
- opterr —— 如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可
getopt()函数在命令解析完毕时候,会返回-1,如果解析到选项参数字符串中没有的参数,则会返回'?'.
示例代码:
#include <stdio.h> #include "getopt.h" int main(int argc, char** argv) { opterr = 0; // 是否输出错误信息 int opt = 0; char const *optStr = "a::b:c"; while ((opt=getopt(argc, argv, optStr)) != -1) { if (opt == 'a') //可选参数,可传入或者不传入参数值 { printf("Parameter a is get.\n"); if (optarg==NULL) printf("No value"); else printf("Value is %s", (char*)optarg); } else if (opt == 'b') // 可选参数,必须传入参数值 { printf("Parameter b is %s.\n", (char*)optarg); } else if (opt == 'c') // 可选参数,不需要参数值 { printf("Parameter c is get.\n"); } else if (opt == '?') // 未知参数,打印参数值 { printf("unknown para %c.\n", (char)optopt); } } return 0; }
1.2 解析长参数
getopt_long()包含了getopt()的功能,且可以解析长参数;
函数原型:
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
函数参数:
- optstring 选项参数名
- longopts 选项参数列表 (结构体)
- longindex
longopts的类型为struct option,其定义如下所示:
// longopts指明了长参数的名称和属性,它是一个结构体指针 // 通常传一个结构体数组进去,每个元素代表一个参数,每个参数都是由下面的结构组成。 struct option { const char *name; /* 选项参数名称 */ int has_arg; /* 指明选项参数是否带有参数值 0/1/2 */ int *flag; int val; }; // name: 选项参数的参数名 // has_arg: 选项参数是否有参数值 0/1/2 --> 无,有,可选 // *flag : 为NULL时,getopt_long()函数会返回val中指定的值作为选项标识;否则,getopt_long()函数会将val中指定的标识值存储在flag指向的内存中,且返回0。 // val: 指定选项的标识(int) // longindex 如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值。即符合条件的长选项的下标值
长参数的格式建议写为:
--reqarg1=werc16
这种格式;避免出错;
代码示例:
#include <stdio.h> #include "getopt.h" // 定义长选项的标识 #define START_MODE_1 1 #define START_MODE_2 2 #define START_MODE_3 3 int main(int argc, char** argv) { opterr = 1; // 是否输出错误信息 int opt = 0; char const *optStr = "a::b:c"; // flag 为NULL, START_MODE直接由getopt_long()函数返回 struct option longoptions[] = { {"reqarg1", required_argument, NULL, START_MODE_1}, {"reqarg2", no_argument, NULL, START_MODE_2}, {"reqarg3", optional_argument, NULL, START_MODE_3} }; while ((opt = getopt_long(argc, argv, optStr, longoptions, NULL)) != -1) { // 长参数处理 if (opt == START_MODE_1) { printf("Start on mode 1 with para: %s.\n", (char*)optarg); } else if (opt == START_MODE_2) { printf("Start on mode 2.\n"); } else if (opt == START_MODE_3) { if (optarg == NULL) { printf("Start on mode 3.\n"); } else { printf("Start on mode 3 with para %s.\n", (char*)optarg); } } // 短参数处理 else if (opt == 'a') //可选参数,可传入或者不传入参数值 { printf("Parameter a is get.\n"); if (optarg==NULL) printf("No value"); else printf("Value is %s", (char*)optarg); } else if (opt == 'b') // 可选参数,必须传入参数值 { printf("Parameter b is %s.\n", (char*)optarg); } else if (opt == 'c') // 可选参数,不需要参数值 { printf("Parameter c is get.\n"); } else if (opt == '?') // 未知参数,打印参数值 { printf("unknown para %c.\n", (char)optopt); } } return 0; }
测试,flag设置为非空:
#include <stdio.h> #include "getopt.h" // 定义长选项的标识 #define START_MODE_1 1 #define START_MODE_2 2 #define START_MODE_3 3 int main(int argc, char** argv) { opterr = 1; // 是否输出错误信息 int opt = 0; char const *optStr = "a::b:c"; int cmd_code = 0; // flag 不为NULL, START_MODE的值会存储在cmd_code中; struct option longoptions[] = { {"reqarg1", required_argument, &cmd_code, START_MODE_1}, {"reqarg2", no_argument, &cmd_code, START_MODE_2}, {"reqarg3", optional_argument, &cmd_code, START_MODE_3} }; while ((opt = getopt_long(argc, argv, optStr, longoptions, NULL)) != -1) { if (cmd_code == START_MODE_1) { printf("Start on mode 1 with para: %s.\n", (char*)optarg); } else if (cmd_code == START_MODE_2) { printf("Start on mode 2.\n"); } else if (cmd_code == START_MODE_3) { if (optarg == NULL) { printf("Start on mode 3.\n"); } else { printf("Start on mode 3 with para %s.\n", (char*)optarg); } } } return 0; }
1.3 解析长参数
getopt_long_only()函数也可用来解析长参数;但是与getopt_long()函数有区别;getopt_long()函数会将-namre当作短参数来解为-n -a -m -e,而getopt_long_only会将-name和--name都当作长参数来解析;
2. getopt()源代码
为解决getopt()能够跨平台使用,从网上找到经过修改的getopt源码,可将其编译成不同平台的库,实现跨平台使用。
2.1 getopt.h文件
# ifndef __GETOPT_H_ # define __GETOPT_H_ # ifdef _GETOPT_API # undef _GETOPT_API # endif //------------------------------------------------------------------------------ # if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT) # error "The preprocessor definitions of EXPORTS_GETOPT and STATIC_GETOPT \ can only be used individually" # elif defined(STATIC_GETOPT) # pragma message("Warning static builds of getopt violate the Lesser GNU \ Public License") # define _GETOPT_API # elif defined(EXPORTS_GETOPT) # pragma message("Exporting getopt library") # define _GETOPT_API __declspec(dllexport) # else # pragma message("Importing getopt library") # define _GETOPT_API __declspec(dllimport) # endif # include <tchar.h> // Standard GNU options # define null_argument 0 /*Argument Null*/ # define no_argument 0 /*Argument Switch Only*/ # define required_argument 1 /*Argument Required*/ # define optional_argument 2 /*Argument Optional*/ // Shorter Versions of options # define ARG_NULL 0 /*Argument Null*/ # define ARG_NONE 0 /*Argument Switch Only*/ # define ARG_REQ 1 /*Argument Required*/ # define ARG_OPT 2 /*Argument Optional*/ // Change behavior for C\C++ # ifdef __cplusplus # define _BEGIN_EXTERN_C extern "C" { # define _END_EXTERN_C } # define _GETOPT_THROW throw() # else # define _BEGIN_EXTERN_C # define _END_EXTERN_C # define _GETOPT_THROW # endif _BEGIN_EXTERN_C extern _GETOPT_API TCHAR *optarg; extern _GETOPT_API int optind; extern _GETOPT_API int opterr; extern _GETOPT_API int optopt; struct option { /* The predefined macro variable __STDC__ is defined for C++, and it has the in- teger value 0 when it is used in an #if statement, indicating that the C++ l- anguage is not a proper superset of C, and that the compiler does not confor- m to C. In C, __STDC__ has the integer value 1. */ # if defined (__STDC__) && __STDC__ const TCHAR* name; # else TCHAR* name; # endif int has_arg; int *flag; TCHAR val; }; extern _GETOPT_API int getopt( int argc, TCHAR *const *argv , const TCHAR *optstring ) _GETOPT_THROW; extern _GETOPT_API int getopt_long ( int ___argc, TCHAR *const *___argv , const TCHAR *__shortopts , const struct option *__longopts , int *__longind ) _GETOPT_THROW; extern _GETOPT_API int getopt_long_only ( int ___argc, TCHAR *const *___argv , const TCHAR *__shortopts , const struct option *__longopts , int *__longind ) _GETOPT_THROW; // harly.he add for reentrant 12.09/2013 extern _GETOPT_API void getopt_reset() _GETOPT_THROW; _END_EXTERN_C // Undefine so the macros are not included # undef _BEGIN_EXTERN_C # undef _END_EXTERN_C # undef _GETOPT_THROW # undef _GETOPT_API # endif // __GETOPT_H_
2.2 getopt.cpp文件
# ifndef _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS # endif # include <stdlib.h> # include <stdio.h> # include <tchar.h> # include "getopt.h" # ifdef __cplusplus # define _GETOPT_THROW throw() # else # define _GETOPT_THROW # endif enum ENUM_ORDERING { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER }; struct _getopt_data { int optind; int opterr; int optopt; TCHAR* optarg; int __initialized; TCHAR* __nextchar; int __ordering; int __posixly_correct; int __first_nonopt; int __last_nonopt; }; static struct _getopt_data getopt_data = { 0, 0, 0, NULL, 0, NULL, 0, 0, 0, 0 }; TCHAR* optarg = NULL; int optind = 1; int opterr = 1; int optopt = _T( '?' ); static void exchange( TCHAR** argv, struct _getopt_data* d ) { int bottom = d->__first_nonopt; int middle = d->__last_nonopt; int top = d->optind; TCHAR* tem; while ( top > middle && middle > bottom ) { if ( top - middle > middle - bottom ) { int len = middle - bottom; register int i; for ( i = 0; i < len; i++ ) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - ( middle - bottom ) + i]; argv[top - ( middle - bottom ) + i] = tem; } top -= len; } else { int len = top - middle; register int i; for ( i = 0; i < len; i++ ) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } bottom += len; } } d->__first_nonopt += ( d->optind - d->__last_nonopt ); d->__last_nonopt = d->optind; } static const TCHAR* _getopt_initialize( const TCHAR* optstring , struct _getopt_data* d , int posixly_correct ) { d->__first_nonopt = d->__last_nonopt = d->optind; d->__nextchar = NULL; d->__posixly_correct = posixly_correct | !!_tgetenv( _T( "POSIXLY_CORRECT" ) ); if ( optstring[0] == _T( '-' ) ) { d->__ordering = RETURN_IN_ORDER; ++optstring; } else if ( optstring[0] == _T( '+' ) ) { d->__ordering = REQUIRE_ORDER; ++optstring; } else if ( d->__posixly_correct ) { d->__ordering = REQUIRE_ORDER; } else { d->__ordering = PERMUTE; } return optstring; } int _getopt_internal_r( int argc , TCHAR *const * argv , const TCHAR* optstring , const struct option* longopts , int* longind , int long_only , struct _getopt_data* d , int posixly_correct ) { int print_errors = d->opterr; if ( argc < 1 ) { return -1; } d->optarg = NULL; if ( d->optind == 0 || !d->__initialized ) { if ( d->optind == 0 ) { d->optind = 1; } optstring = _getopt_initialize( optstring, d, posixly_correct ); d->__initialized = 1; } else if ( optstring[0] == _T( '-' ) || optstring[0] == _T( '+' ) ) { optstring++; } if ( optstring[0] == _T( ':' ) ) { print_errors = 0; } if ( d->__nextchar == NULL || *d->__nextchar == _T( '\0' ) ) { if ( d->__last_nonopt > d->optind ) { d->__last_nonopt = d->optind; } if ( d->__first_nonopt > d->optind ) { d->__first_nonopt = d->optind; } if ( d->__ordering == PERMUTE ) { if ( d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind ) { exchange( ( TCHAR * * ) argv, d ); } else if ( d->__last_nonopt != d->optind ) { d->__first_nonopt = d->optind; } while ( d->optind < argc && ( argv[d->optind][0] != _T( '-' ) || argv[d->optind][1] == _T( '\0' ) ) ) { d->optind++; } d->__last_nonopt = d->optind; } if ( d->optind != argc && !_tcscmp( argv[d->optind], _T( "--" ) ) ) { d->optind++; if ( d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind ) { exchange( ( TCHAR * * ) argv, d ); } else if ( d->__first_nonopt == d->__last_nonopt ) { d->__first_nonopt = d->optind; } d->__last_nonopt = argc; d->optind = argc; } if ( d->optind == argc ) { if ( d->__first_nonopt != d->__last_nonopt ) { d->optind = d->__first_nonopt; } return -1; } if ( ( argv[d->optind][0] != _T( '-' ) || argv[d->optind][1] == _T( '\0' ) ) ) { if ( d->__ordering == REQUIRE_ORDER ) { return -1; } d->optarg = argv[d->optind++]; return 1; } d->__nextchar = ( argv[d->optind] + 1 + ( longopts != NULL && argv[d->optind][1] == _T( '-' ) ) ); } if ( longopts != NULL && ( argv[d->optind][1] == _T( '-' ) || ( long_only && ( argv[d->optind][2] || !_tcschr( optstring, argv[d->optind][1] ) ) ) ) ) { TCHAR* nameend; const struct option* p; const struct option* pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for ( nameend = d->__nextchar; *nameend && *nameend != _T( '=' ); nameend++ ) ; for ( p = longopts, option_index = 0; p->name; p++, option_index++ ) { if ( !_tcsncmp( p->name, d->__nextchar, nameend - d->__nextchar ) ) { if ( ( unsigned int ) ( nameend - d->__nextchar ) == ( unsigned int ) _tcslen( p->name ) ) { pfound = p; indfound = option_index; exact = 1; break; } else if ( pfound == NULL ) { pfound = p; indfound = option_index; } else if ( long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val ) { ambig = 1; } } } if ( ambig && !exact ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '%s' is ambiguous\n" ) , argv[0] , argv[d->optind] ); } d->__nextchar += _tcslen( d->__nextchar ); d->optind++; d->optopt = 0; return _T( '?' ); } if ( pfound != NULL ) { option_index = indfound; d->optind++; if ( *nameend ) { if ( pfound->has_arg ) { d->optarg = nameend + 1; } else { if ( print_errors ) { if ( argv[d->optind - 1][1] == _T( '-' ) ) { _ftprintf( stderr , _T( "%s: option '--%s' doesn't allow " ) _T( "an argument\n" ) , argv[0] , pfound->name ); } else { _ftprintf( stderr , _T( "%s: option '%c%s' doesn't allow " ) _T( "an argument\n" ) , argv[0] , argv[d->optind - 1][0] , pfound->name ); } } d->__nextchar += _tcslen( d->__nextchar ); d->optopt = pfound->val; return _T( '?' ); } } else if ( pfound->has_arg == 1 ) { if ( d->optind < argc ) { d->optarg = argv[d->optind++]; } else { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '--%s' requires an " ) _T( "argument\n" ) , argv[0] , pfound->name ); } d->__nextchar += _tcslen( d->__nextchar ); d->optopt = pfound->val; return optstring[0] == _T( ':' ) ? _T( ':' ) : _T( '?' ); } } d->__nextchar += _tcslen( d->__nextchar ); if ( longind != NULL ) { *longind = option_index; } if ( pfound->flag ) { *( pfound->flag ) = pfound->val; return 0; } return pfound->val; } if ( !long_only || argv[d->optind][1] == _T( '-' ) || _tcschr( optstring , *d->__nextchar ) == NULL ) { if ( print_errors ) { if ( argv[d->optind][1] == _T( '-' ) ) { /* --option */ _ftprintf( stderr , _T( "%s: unrecognized option '--%s'\n" ) , argv[0] , d->__nextchar ); } else { /* +option or -option */ _ftprintf( stderr , _T( "%s: unrecognized option '%c%s'\n" ) , argv[0] , argv[d->optind][0] , d->__nextchar ); } } d->__nextchar = ( TCHAR * ) _T( "" ); d->optind++; d->optopt = 0; return _T( '?' ); } } { TCHAR c = *d->__nextchar++; TCHAR* temp = ( TCHAR* ) _tcschr( optstring, c ); if ( *d->__nextchar == _T( '\0' ) ) { ++d->optind; } if ( temp == NULL || c == _T( ':' ) || c == _T( ';' ) ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: invalid option -- '%c'\n" ) , argv[0] , c ); } d->optopt = c; return _T( '?' ); } if ( temp[0] == _T( 'W' ) && temp[1] == _T( ';' ) ) { TCHAR* nameend; const struct option* p; const struct option* pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; if ( *d->__nextchar != _T( '\0' ) ) { d->optarg = d->__nextchar; d->optind++; } else if ( d->optind == argc ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option requires an argument -- '%c'\n" ) , argv[0] , c ); } d->optopt = c; if ( optstring[0] == _T( ':' ) ) { c = _T( ':' ); } else { c = _T( '?' ); } return c; } else { d->optarg = argv[d->optind++]; } for ( d->__nextchar = nameend = d->optarg; *nameend && *nameend != _T( '=' ); nameend++ ) ; for ( p = longopts, option_index = 0; p->name; p++, option_index++ ) { if ( !_tcsncmp( p->name , d->__nextchar , nameend - d->__nextchar ) ) { if ( ( unsigned int ) ( nameend - d->__nextchar ) == _tcslen( p->name ) ) { pfound = p; indfound = option_index; exact = 1; break; } else if ( pfound == NULL ) { pfound = p; indfound = option_index; } else if ( long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val ) { ambig = 1; } } } if ( ambig && !exact ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '-W %s' is ambiguous\n" ) , argv[0] , d->optarg ); } d->__nextchar += _tcslen( d->__nextchar ); d->optind++; return _T( '?' ); } if ( pfound != NULL ) { option_index = indfound; if ( *nameend ) { if ( pfound->has_arg ) { d->optarg = nameend + 1; } else { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '-W %s' doesn't allow " ) _T( "an argument\n" ) , argv[0] , pfound->name ); } d->__nextchar += _tcslen( d->__nextchar ); return _T( '?' ); } } else if ( pfound->has_arg == 1 ) { if ( d->optind < argc ) { d->optarg = argv[d->optind++]; } else { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option '-W %s' requires an " ) _T( "argument\n" ) , argv[0] , pfound->name ); } d->__nextchar += _tcslen( d->__nextchar ); return optstring[0] == _T(':') ? _T(':') : _T('?'); } } else { d->optarg = NULL; } d->__nextchar += _tcslen( d->__nextchar ); if ( longind != NULL ) { *longind = option_index; } if ( pfound->flag ) { *( pfound->flag ) = pfound->val; return 0; } return pfound->val; } d->__nextchar = NULL; return _T( 'W' ); } if ( temp[1] == _T( ':' ) ) { if ( temp[2] == _T( ':' ) ) { if ( *d->__nextchar != _T( '\0' ) ) { d->optarg = d->__nextchar; d->optind++; } else { d->optarg = NULL; } d->__nextchar = NULL; } else { if ( *d->__nextchar != _T( '\0' ) ) { d->optarg = d->__nextchar; d->optind++; } else if ( d->optind == argc ) { if ( print_errors ) { _ftprintf( stderr , _T( "%s: option requires an " ) _T( "argument -- '%c'\n" ) , argv[0] , c ); } d->optopt = c; if ( optstring[0] == _T( ':' ) ) { c = _T( ':' ); } else { c = _T( '?' ); } } else { d->optarg = argv[d->optind++]; } d->__nextchar = NULL; } } return c; } } int _getopt_internal( int argc , TCHAR *const * argv , const TCHAR* optstring , const struct option* longopts , int* longind , int long_only , int posixly_correct ) { int result; getopt_data.optind = optind; getopt_data.opterr = opterr; result = _getopt_internal_r( argc , argv , optstring , longopts , longind , long_only , &getopt_data , posixly_correct ); optind = getopt_data.optind; optarg = getopt_data.optarg; optopt = getopt_data.optopt; return result; } int getopt( int argc, TCHAR *const * argv, const TCHAR* optstring) _GETOPT_THROW { return _getopt_internal( argc , argv , optstring , ( const struct option * ) 0 , ( int * ) 0 , 0 , 0 ); } int getopt_long( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index ) _GETOPT_THROW { return _getopt_internal( argc , argv , options , long_options , opt_index , 0 , 0 ); } int _getopt_long_r( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index , struct _getopt_data* d ) { return _getopt_internal_r( argc , argv , options , long_options , opt_index , 0 , d , 0 ); } int getopt_long_only( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index ) _GETOPT_THROW { return _getopt_internal( argc , argv , options , long_options , opt_index , 1 , 0 ); } int _getopt_long_only_r( int argc , TCHAR *const * argv , const TCHAR* options , const struct option* long_options , int* opt_index , struct _getopt_data* d ) { return _getopt_internal_r( argc , argv , options , long_options , opt_index , 1 , d , 0 ); } void getopt_reset() _GETOPT_THROW { optarg = NULL; optind = 1; opterr = 1; optopt = _T( '?' ); // getopt_data.optind = 0; getopt_data.opterr = 0; getopt_data.optopt = 0; getopt_data.optarg = NULL; getopt_data.__initialized = 0; getopt_data.__nextchar = NULL; getopt_data.__ordering = 0; getopt_data.__posixly_correct = 0; getopt_data.__first_nonopt = 0; getopt_data.__last_nonopt = 0; }
getopt库源代码以及编译方法可参考:CMake实战
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)