在C ++代码中应该使用哪个C I / O库?
在新的C ++代码中,我倾向于使用C ++ iostream库而不是C stdio库。
我注意到有些程序员似乎坚持使用stdio,坚持认为它更具可移植性。
真的是这样吗?有什么更好用的?
解决方案
如果像我一样,我们在学习C ++之前就学习过C,那么stdio库的使用似乎更自然。 iostream vs. stdio有优点和缺点,但是使用iostream时我确实会错过printf()。
回顾过去的糟糕日子,C ++标准委员会一直在与语言混为一谈,iostreams是一个移动的目标。如果我们使用iostream,则每年大约有机会重写代码的一部分。因此,我一直使用stdio,自1989年以来就没有太大变化。
如果我今天在做事情,我会使用iostream。
太冗长了。
考虑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;
字符串格式化是一种情况,在这种情况下,可以并且应该避免面向对象,而倾向于嵌入字符串中的格式化DSL。考虑一下Lisp的format
,Python的printf样式格式或者PHP,Bash,Perl,Ruby及其字符串内插。
该用例的iostream充其量是被误导的。
要回答原始问题:
使用stdio可以完成的任何事情都可以使用iostream库完成。
Disadvantages of iostreams: verbose Advantages of iostreams: easy to extend for new non POD types.
C ++超越C语言的进步是类型安全。
- iostreams被设计为明确类型安全的。因此,分配给对象也明确检查了分配对象的类型(在编译时)(如果需要,则生成编译时错误)。因此,可以防止运行时内存溢出或者将浮点值写入char对象等。
- 另一方面,scanf()/ printf()和family依赖程序员获取正确的格式字符串,并且没有类型检查(我相信gcc具有扩展名可以)。结果,它是许多错误的源头(因为程序员的分析能力不如编译器[更不用说编译器比人类更完美了])。
只是为了澄清来自Colin Jensen的评论。
- 自上一个标准发布以来,iostream库一直保持稳定(我忘了实际年份,但大约是10年前)。
澄清Mikael Jansson的评论。
- 他提到的其他使用格式样式的语言都有明确的保护措施,可以防止C stdio库的危险副作用,这些副作用(使用C语言而不是所提到的语言)可能导致运行时崩溃。
N.B.我同意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; }
对于二进制IO,我倾向于使用stdio的fread和fwrite。对于格式化的东西,我通常会使用IO Stream,尽管正如Mikael所说,非普通(非默认值)格式化可以是PITA。
stdio更适合读取二进制文件(例如将块读取到vector <unsigned char>中并使用.resize()等)。有关示例,请参见http://nuwen.net/libnuwen.html中file.hh中的read_rest函数。
当读取二进制文件导致错误的eof时,C ++流可能会阻塞很多字节。
Boost格式库为printf样式的字符串格式提供了一种类型安全的,面向对象的替代方法,并且是对iostream的补充,该iostream不会由于通常使用operator%而遭受常见的冗长性问题。如果我们不喜欢使用iostream的operator <<进行格式化,我建议我们考虑使用纯C printf。
我使用iostreams,主要是因为这样可以在以后(如果需要的话)更轻松地摆弄流。例如,我们可能发现要在某个跟踪窗口中显示输出-使用cout和cerr相对容易做到。当然,我们可以在Unix上摆弄管道和其他东西,但这并不那么可移植。
我确实喜欢类似printf的格式,因此通常通常先格式化字符串,然后将其发送到缓冲区。使用Qt时,我经常使用QString :: sprintf(尽管他们建议使用QString :: arg代替)。我也看过boost.format,但是不能真正习惯语法(太多的%)。不过,我真的应该看看。
由于iostream已成为一种标准,因此我们应该使用它们,因为我们知道代码肯定会与较新版本的编译器一起使用。我想现在大多数编译器都非常了解iostream,因此使用它们应该没有任何问题。
但是,如果我们想使用* printf函数,我认为不会有任何问题。
原则上,我将使用iostream,在实践中,我会使用太多格式化的小数,等等,这会使iostream太难以理解,因此我使用stdio。 Boost :: format是一种改进,但对我而言却不足以激发他们的积极性。实际上,由于大多数现代编译器仍会进行参数检查,因此stdio几乎是类型安全的。
在这个领域,我对任何解决方案都不满意。
我想念的图书馆是格式化的输入。
iostreams没有复制scanf()的好方法,甚至boost也没有输入所需的扩展名。
虽然C ++ iostreams API有很多好处,但一个重要的问题是i18n。问题在于,参数替换的顺序会根据区域性而变化。经典示例如下所示:
// i18n UNSAFE std::cout << "Dear " << name.given << ' ' << name.family << std::endl;
尽管该功能适用于英语,但中文姓氏排在第一位。
在为国外市场翻译代码时,翻译摘要充满了风险,因此新的10ns可能需要更改代码,而不仅仅是字符串。
boost :: format似乎结合了stdio(可以使用与显示顺序不同的参数使用单个格式的字符串)和iostream(类型安全,可扩展性)的优点。