我想编写像unix utlities一样的程序。特别是,我想将它们与管道一起使用,例如:
grep foo myfile | ./MyTransformation [--args] | cut -f2 | ...
三个方面让我想知道如何处理I / O:
根据像Useless Use of Cat Award这样的scource,最好同时支持stdin读取和从文件中读取(在管道的开头)。这怎么做得最好?我习惯使用<getopt.h>
/ <cgetopt>
来解析参数。我可以看到除了我的选项之外是否还有另一个文件参数并从中读取。如果没有,请从标准输入读取。这意味着如果提供inut文件,stdin将被忽略。这是否可取?
根据this question,C ++会将cout
和cin
与stdio
同步,因此无法很好地缓冲。这导致性能大幅下降。解决方案是禁用同步:cin.sync_with_stdio(false);
。管道中使用的程序是否始终禁用stdio
和cin
的{{1}}同步?或者它应该避免使用cout
和cin
而是使用他们自己的缓冲io形式?
由于cout
将用于程序输出(除非指定了输出文件),状态消息(像%done一样的冗长)必须转到其他地方。 cout
似乎是一个明显的选择。但是,状态不是错误。
总之,我想知道c ++中这些程序的io ahndling。尽管存在上述问题,仍可以使用cerr/stderr
和cin
吗? I / O应该以不同的方式处理吗?例如,从stdin和stdout读取和写入缓冲文件是默认文件吗?实施这种行为的推荐方法是什么?
答案 0 :(得分:2)
如果没有选项,标准惯用语是:
int returnCode = 0;
void
processFile( std::string const& filename )
{
if ( filename == "-" ) {
process( std::cin );
} else {
std::ifstream in( filename.c_str() );
if ( !in.is_open() ) {
std::cerr << argv[0] << ": cannot open " << filename << std::endl;
returnCode = 1;
} else {
process( in );
}
}
}
int
main( int argc, char** argv )
{
if ( argc == 1 ) {
processFile( "-" );
} else {
for ( int i = 1; i != argc; ++ i ) {
processFile( argv[i] );
}
}
std::cout.flush()
return std::cout ? returnCode : 2;
}
然而,有许多变种。我发现自己这样做了
通常我写了一个MultiFileInputStream
类
(模板&gt;构造函数接受一对迭代器;然后
执行或多或少与上面相同的代码。 (全部
像往常一样,重要的代码在相应的streambuf中。)
同样,我有一个类来解析选项(看起来像
像一个不可变的std::vector<std::string>
一样的选项
已被解析。所以上面会变成:
int
main( int argc, char** argv )
{
CommandLine& args = CommandLine::instance();
args.parse( argc, argv );
MultiFileInputStream src( args.begin(), args.end() );
process( src );
return ProgramStatus::instance().returnCode();
}
(ProgramStatus
是另一个有用的类,它处理错误
输出和返回码。并刷新std::cout
和
当您在其上调用returnCode()
时调整错误代码。)
我确信编写Unix过滤程序的人已经开发出来了 类似的课程。
关于问题2:sync_with_stdio
是静态的
std::ios_base
的成员,因此您可以在没有对象的情况下调用它:
std::ios_base::sync_with_stdio( false );
。我觉得这个少了
误导,因为调用会影响所有 iostream对象。
如果IO处理是一个阻塞点,那么一定要做,但是
大多数时候,我不打扰。这种节目很少见
需要任何类型的优化。 (注意,如果你打电话
sync_with_stdio
,那么你应该不使用任何C风格的IO。
但无论如何我都看不出有任何理由使用它。)
关于问题3:错误消息转到std::cerr
,
总是。您还希望确保返回非零回报
代码,即使错误不是致命的。类似的东西:
myprog file1 > tmp && mv tmp file1
一切都很常见,如果你有问题,那就不行了
产生输出,如果你不回来就是一场灾难
非零错误代码。 (这就是为什么我总是冲洗然后
检查std::cout
的状态。很久很久以前,一个用户
我的程序做了上面的,有一个非常大的文件,和
磁盘已满。事后并没有那么充分。自那时候起:
总是冲洗std::cout
,并检查它是否有效
返回OK。)
答案 1 :(得分:-1)
您确定要使用C ++吗?大多数操作系统比C ++更依赖于C和汇编。如果您要编写应用程序,那么C ++可能是一个不错的选择,但对于操作系统及其实用程序,shell和帮助程序,它们通常用C语言编写。您可以查看Linux或BSD实现以了解它用管道,标准输入和标准输出完成。如果您认为C适合您,您可以阅读Kernighan和Richie的C书“TH编程语言”,那里有很多例子,说明如何编写一个使用管道,std i / o和参数的优秀C程序。