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

时间:2020-03-06 14:34:47  来源:igfitidea点击:

在新的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(类型安全,可扩展性)的优点。