哪个CI / O库应该用在C ++代码中?

在新的C ++代码中,我倾向于使用C ++ iostream库而不是C stdio库。

我注意到一些程序员似乎坚持stdio,坚持它更便携。

这是真的吗? 有什么更好的使用?

回答原来的问题:
任何可以使用stdio完成的事情都可以使用iostream库来完成。

Disadvantages of iostreams: verbose Advantages of iostreams: easy to extend for new non POD types. 

C语言所做的C ++的一步就是types安全。

  • iostreams被devise为明确的types安全。 因此,分配给一个对象明确地检查了被分配的对象的types(在编译器时间)(如果需要,产生一个编译时错误)。 从而防止运行时内存溢出或将一个浮点值写入char对象等

  • scanf()/ printf()和家族另一方面依靠程序员得到的格式string正确,没有types检查(我相信gcc有一个扩展,帮助)。 因此,它是许多错误的源头(因为程序员的分析不如编译器完美([编译器不会比人类完美])。

只是为了澄清Colin Jensen的评论。

  • 自从最后一个标准发布以来,iostream库一直保持稳定(我忘记了实际年份,但是大约在10年前)。

澄清Mikael Jansson的评论。

  • 他提到使用格式风格的其他语言具有明确的安全措施,以防止C stdio库的危险的副作用,可以(在C中,但不是所提及的语言)导致运行时崩溃。

NB我同意iostream库在冗长的一面。 但我愿意忍受冗长的工作以确保运行时的安全。 但是我们可以通过使用Boost Format Library来减轻冗长。

 #include <iostream> #include <iomanip> #include <boost/format.hpp> struct X { // this structure reverse engineered from // example provided by 'Mikael Jansson' in order to make this a running example char* name; double mean; int sample_count; }; int main() { X stats[] = {{"Plop",5.6,2}}; // nonsense output, just to exemplify // stdio version fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n", stats, stats->name, stats->mean, stats->sample_count); // iostream std::cerr << "at " << (void*)stats << "/" << stats->name << ": mean value " << std::fixed << std::setprecision(3) << stats->mean << " of " << std::setw(4) << std::setfill(' ') << stats->sample_count << " samples\n"; // iostream with boost::format std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n") % stats % stats->name % stats->mean % stats->sample_count; } 

这太冗长了

思考iostream构造执行以下操作(类似于scanf):

 // nonsense output, just to examplify fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n", stats, stats->name, stats->mean, stats->sample_count); 

这将需要像这样的东西:

 std::cerr << "at " << static_cast<void*>(stats) << "/" << stats->name << ": mean value " << std::precision(3) << stats->mean << " of " << std::width(4) << std::fill(' ') << stats->sample_count << " samples " << std::endl; 

string格式化是面向对象可以并且应该避开embedded在string中的格式化DSL的一种情况。 考虑Lisp的format ,Python的printf样式格式,或者PHP,Bash,Perl,Ruby和它们的string内插。

这个用例的iostream最好是被误导了。

Boost格式库为printf风格的string格式提供了一个types安全的,面向对象的替代scheme,是对iostreams的一种补充,它不会因为聪明地使用operator%而受到通常冗长的问题的困扰。 如果你不喜欢用iostream的运算符<<格式化,我build议考虑使用普通的C printf。

早在糟糕的时代,C ++标准委员会一直在喋喋不休地使用这种语言,stream言终究是一个动人的目标。 如果你使用了iostream,那么你每年都有机会重写你的代码的一部分。 因此,我一直使用自1989年以来没有明显改变的stdio。

如果我今天做的东西,我会用iostreams。

如果像我一样,在学习C ++之前学习了C,那么stdio库看起来更自然。 iostream和stdio有优点和缺点,但是当使用iostream的时候,我确实错过了printf()。

原则上我会使用iostreams,实际上我做了太多的格式化小数等,使iostreams太难读,所以我使用stdio。 Boost :: format是一种改进,但对我来说还不够激励。 实际上,stdio几乎是types安全的,因为大多数现代编译器总是进行参数检查。

这是我对任何解决scheme都不满意的地方。

对于二进制IO,我倾向于使用stdio的fread和fwrite。 对于格式化的东西,我通常会使用IO Stream,尽pipeMikael说,非trival(非默认的)格式可以是PITA。

虽然C ++ iostreams API有很多好处,但是一个重要的问题是i18n。 问题在于参数replace的顺序可能因文化而异。 典型的例子是这样的:

 // i18n UNSAFE std::cout << "Dear " << name.given << ' ' << name.family << std::endl; 

虽然这对英文起作用,但在中文中,姓氏是第一位的。

在为外国市场翻译代码时,翻译片段充满了危险,所以新的l10ns可能需要更改代码,而不仅仅是不同的string。

boost :: format似乎将stdio(可以使用参数的单个格式string,然后他们出现)和iostreams(types安全,可扩展性)的最好组合。

我将比较C ++标准库中的两个主stream库。

你不应该在C ++中使用基于C风格格式string的string处理例程。

存在几个原因来解决它们的使用:

  • 不是types安全的
  • 您不能将非PODtypes传递给可变参数列表(即,既不扫描+合成也不传递给printf + co。),或者input未定义行为的黑暗要点
  • 容易出错:
    • 您必须设法保持格式string和“值参数列表”同步
    • 您必须正确保持同步

在偏远地区引入微妙的错误

printf本身不是不好的。 软件变老,被重构和修改,错误可能从远程引入。 假设你有

 // foo.h ... float foo; ... 

和某处

 // bar/frob/42/icetea.cpp ... scanf ("%f", &foo); ... 

三年后,你会发现foo应该是一些自定义的types。

 // foo.h ... FixedPoint foo; ... 

但是在某个地方

 // bar/frob/42/icetea.cpp ... scanf ("%f", &foo); ... 

…那么你的旧的printf / scanf仍然会编译,除了你现在得到了随机段错误,你不记得为什么。

iostreams的详细程度

如果你认为printf()不那么冗长,那么你有一定的可能性不使用他们的iostream的全部力量。 例:

  printf ("My Matrix: %f %f %f %f\n" " %f %f %f %f\n" " %f %f %f %f\n" " %f %f %f %f\n", mat(0,0), mat(0,1), mat(0,2), mat(0,3), mat(1,0), mat(1,1), mat(1,2), mat(1,3), mat(2,0), mat(2,1), mat(2,2), mat(2,3), mat(3,0), mat(3,1), mat(3,2), mat(3,3)); 

比较一下使用iostreams的权利:

 cout << mat << '\n'; 

你必须为运算符<<定义一个适当的重载,它大体上具有printf-thingy的结构,但是重要的不同之处在于你现在有一些重用和types安全的东西。 当然你也可以为printf-likes重新使用某些东西,但是如果除了其他FixedPoint ,例如你必须通过FILE *手柄,那么你再次使用printf(如果你用新的FixedPointreplacematrix成员呢?)周围。

I18N的C风格格式string比Iostream更好

请注意,格式化string通常被认为是国际化的救援,但在这方面它们并不比iostream更好:

 printf ("Guten Morgen, Sie sind %f Meter groß und haben %d Kinder", someFloat, someInt); printf ("Good morning, you have %d children and your height is %f meters", someFloat, someInt); // Note: Position changed. // ^^ not the best example, but different languages have generally different // order of "variables" 

也就是说,旧式的C格式string缺less像iostream一样多的位置信息。

你可能要考虑boost :: format ,它提供了对明确指定格式string中位置的支持。 从他们的例子部分:

 cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style. 

一些printf实现提供了位置参数,但是它们是非标准的。

应该使用C风格的格式string?

除了performance(正如扬·胡德克所指出的那样),我没有看到一个理由。 但请记住:

“我们应该忘记小效率,大约97%的时间:不成熟的优化是万恶之源。 但是,我们不应该把这个关键的3%放在一边。 一个好的程序员不会被这样的推理所迷惑,他会聪明地仔细看看关键的代码; 但只有在代码被确定后“ – Knuth

“瓶颈发生在令人惊讶的地方,所以不要试图再次猜测,并加速,直到你已经certificate是瓶颈的地方。” – 派克

是的,printf的实现通常比iostream的速度快于boost :: format(从我写的一个小而特殊的基准testing中,但它应该在很大程度上取决于具体情况:如果printf = 100%,那么iostream = 160% ,和boost :: format = 220%)

但是不要盲目地忽略它:在文本处理上花了多less时间? 您的程序在退出之前运行了多久? 回溯到C风格的string,松散的types安全性,减less重构性,增加可能隐藏多年的非常微妙的错误的可能性,并且可能仅仅将自己展现在您最喜爱的客户面前,是否相关?

就个人而言,如果我不能获得超过20%的加速,我就不会倒退。 但是,因为我的应用程序几乎把所有的时间都花在了string处理以外的其他任务上,所以我从来不需要。 我写的一些parsing器几乎把所有的时间花在string处理上,但是它们的总体运行时间非常短,不值得进行testing和validation。

一些谜语

最后,我想预先设置一些谜语:

find所有的错误,因为编译器不会(他只能build议,如果他很好):

 shared_ptr<float> f(new float); fscanf (stdout, "%u %s %f", f) 

如果没有别的,这个有什么问题?

 const char *output = "in total, the thing is 50%" "feature complete"; printf (output); 

我使用iostreams,主要是因为这样可以更容易地处理stream(如果我需要的话)。 例如,你可以发现你想要在一些跟踪窗口中显示输出 – 这对于cout和cerr来说是相对容易的。 当然,你可以在unix上拨弄pipe道和东西,但这不是那么简单。

我喜欢printf格式,所以我通常首先格式化一个string,然后发送到缓冲区。 有了Qt,我经常使用QString :: sprintf (尽pipe他们推荐使用QString :: arg )。 我也研究过boost.format ,但是实际上并不习惯语法(太多的%)。 不过,我真的应该去看看。

我想念的iolibraries是格式化的input。

iostreams没有一个很好的方式来复制scanf(),甚至提升没有所需的扩展input。

stdio更适合读取二进制文件(例如将freading块转换为<unsigned char>向量和使用.resize()等)。 有关示例,请参见http://nuwen.net/libnuwen.html中的file.hh中的read_rest函数。;

读取导致错误的eof的二进制文件时,C ++stream可能扼住大量的字节。

由于iostreams已经成为一个标准,所以你应该使用它们,因为知道你的代码肯定能用新版本的编译器工作。 我想现在大部分的编译器都很了解iostream,使用它们不应该有任何问题。

但是,如果你想坚持* printf函数,那么在我看来就没有问题了。