命令行参数
在写出这个程序和测试之后,我发现首先启动程序,而后键入表达式,最后结束,这些也是很麻烦的事情。我最常用的就是对一个表达式求值。如果能让这个表达式表现为命令行参数,就可以避免一些键入。
一个程序开始于对函数main()的调用(3.2节、9.4节)。在这样做的时候,有两个参数被送给main(),其中的一个描述了命令行参数的个数,通常称为argc;另一个是命令行参数的数组,通常称为argv。命令行参数都是字符串,所以argv的类型是char*[argc+1]。该程序的名字(如它在命令行里出现的那样)也作为argv[0]传进来,所以argc至少是1。这个参数的表总以0结束,也就是说,argv[argc]==0。例如,对于命令
dc 150/1.1934
命令行参数具有如下的值:
由于调用main()的规定是与C共享的,所以这里用的是C风格的数组和字符串。
取得命令行参数并不难,问题是怎样通过最少的重新编程去使用它。这里的想法是按照我们从输入流读入的同样方式从命令行读入。从字符串读入的流被称为istringstream,这一点也不奇怪。不幸的是,并不存在某种优雅的方式使cin转去引用一个istringstream。所以我们必须找到一种方式,让计算器的输入函数去引用一个istringstream。进一步说,我们必须找到一种方式,使计算器程序能够根据所提供的命令行的情况,确定是去引用一个istringstream,还是引用cin。
一种简单的方法是引进一个全局的指针input,让它指向要使用的输入流,并让每个输入例程都使用它:
istream* input; // 指向输入流
int main(int argc, char* argv[])
{
switch(argc)
{
case 1: // 从标准输入流
input = &cin;
break;
case 2: // 从参数字符串读
input = new istringstream(argv[1]);
break;
default:
error("too many arguments");
return 1;
}
table["pi"] = 3.1415926535897932385;
table["e"] = 2.7182818284590452354;
while(*input)
{
get_token();
if(curr_tok == END) break;
if(curr_tok == PRINT) continue;
cout << expr(false) << '\n';
}
if(input != &cin) delete input;
return no_if_errors;
}
一个istringstream也是一类istream,它从自己的字符串参数读入(21.5.3节)。在遇到自己的字符串的结束时,istringstream将以其他流遇到输入结束的同样方式失败(3.6节、21.3.3节)。如果要使用istringstream,你就必须包含 < sstream >。
修改main(),使之能接受几个命令行参数也不难,但看起来并灭有这种必要性。特别是几个表达式完全可以作为一个参数传递:
dc "rate=1.1934;150/rate;19.75/rate;217/rate"
我在这里使用了引号,这是因为“;”在我的UNIX系统里是命令行分隔符。其他系统对于在启动时程序所提供参数也可能有不同的规定。
要修改所有的输入例程,让它们都使用*input而不是cin,以便取得替换输入源的灵活性,这种做法并不优雅。如果我早有先见之明,在一开始就引进某种类似input的东西,实际上就可以避免这种改变。存在着一种更一般且有用的观点:应注意到输入源实际上应该作为这个计算器模块的参数。这也就是说,有关这个计算器实例的基本问题是,我说的所谓“计算器”不过是一组函数和数据。并不存在明确表示计算器的模块(2.4节)或者对象(2.5.2节)。如果我原来提出的问题是设计一个计算器模块或者计算器类型,我可能早就很自然地会去考虑以什么作为它的参数了(8.5[3]、10.6[16])。
🔚