哪个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(如果你用新的FixedPoint
replacematrix成员呢?)周围。
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函数,那么在我看来就没有问题了。