Which C I/O library should be used in C++ code? [closed]

可紊 提交于 2019-12-17 02:23:07

问题


In new C++ code, I tend to use the C++ iostream library instead of the C stdio library.

I've noticed some programmers seem to stick to stdio, insisting that it's more portable.

Is this really the case? What is better to use?


回答1:


To answer the original question:
Anything that can be done using stdio can be done using the iostream library.

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

The step forward the C++ made over C was type safety.

  • iostreams was designed to be explicitly type safe. Thus assignment to an object explicitly checked the type (at compiler time) of the object being assigned too (generating an compile time error if required). Thus prevent run-time memory over-runs or writing a float value to a char object etc.

  • scanf()/printf() and family on the other hand rely on the programmer getting the format string correct and there was no type checking (I believe gcc has an extension that helps). As a result it was the source of many bugs (as programmers are less perfect in their analysis than compilers [not going to say compilers are perfect just better than humans]).

Just to clarify comments from Colin Jensen.

  • The iostream libraries have been stable since the release of the last standard (I forget the actual year but about 10 years ago).

To clarify comments by Mikael Jansson.

  • The other languages that he mentions that use the format style have explicit safeguards to prevent the dangerous side effects of the C stdio library that can (in C but not the mentioned languages) cause a run-time crash.

N.B. I agree that the iostream library is a bit on the verbose side. But I am willing to put up with the verboseness to ensure runtime safety. But we can mitigate the verbosity by using 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;
}



回答2:


It's just too verbose.

Ponder the iostream construct for doing the following (similarly for 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);

That would requires something like:

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 formatting is a case where object-orientedness can, and should be, sidestepped in favour of a formatting DSL embedded in strings. Consider Lisp's format, Python's printf-style formatting, or PHP, Bash, Perl, Ruby and their string intrapolation.

iostream for that use case is misguided, at best.




回答3:


The Boost Format Library provides a type-safe, object-oriented alternative for printf-style string formatting and is a complement to iostreams that does not suffer from the usual verbosity issues due to the clever use of operator%. I recommend considering it over using plain C printf if you dislike formatting with iostream's operator<<.




回答4:


Back in the bad old days, the C++ Standards committee kept mucking about with the language and iostreams was a moving target. If you used iostreams, you were then given the opportunity to rewrite parts of your code every year or so. Because of this, I always used stdio which hasn't changed significantly since 1989.

If I were doing stuff today, I would use iostreams.




回答5:


If, like me, you learned C before learning C++, the stdio libraries seem more natural to use. There are pros and cons for iostream vs. stdio but I do miss printf() when using iostream.




回答6:


In principle I would use iostreams, in practice I do too much formatted decimals, etc that make iostreams too unreadable, so I use stdio. Boost::format is an improvement, but not quite motivating enough for me. In practice, stdio is nearly typesafe since most modern compilers do argument checking anyway.

It's an area where I'm still not totally happy with any of the solutions.




回答7:


For binary IO, I tend to use stdio's fread and fwrite. For formatted stuff I'll usually use IO Stream although as Mikael said, non-trival (non-default?) formatting can be a PITA.




回答8:


I'll be comparing the two mainstream libraries from the C++ standard library.

You shouldn't use C-style-format-string-based string-processing-routines in C++.

Several reasons exist to mit their use:

  • Not typesafe
  • You can't pass non-POD types to variadic argument lists (i.e., neither to scanf+co., nor to printf+co.), or you enter the Dark Stronghold of Undefined Behaviour
  • Easy to get wrong:
    • You must manage to keep the format string and the "value-argument-list" in sync
    • You must keep in sync correctly

Subtle bugs introduced at remote places

It is not only the printf in itself that is not good. Software gets old and is refactored and modified, and errors might be introduced from remote places. Suppose you have

.

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

and somewhere ...

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

And three years later you find that foo should be of some custom type ...

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

but somewhere ...

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

... then your old printf/scanf will still compile, except that you now get random segfaults and you don't remember why.

Verbosity of iostreams

If you think printf() is less verbose, then there's a certain probability that you don't use their iostream's full force. Example:

  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));

Compare that to using iostreams right:

cout << mat << '\n';

You have to define a proper overload for operator<< which has roughly the structure of the printf-thingy, but the significant difference is that you now have something re-usable and typesafe; of course you can also make something re-usable for printf-likes, but then you have printf again (what if you replace the matrix members with the new FixedPoint?), apart from other non-trivialities, e.g. you must pass FILE* handles around.

C-style format strings are not better for I18N than iostreams

Note that format-strings are often thought of being the rescue with internationalization, but they are not at all better than iostream in that respect:

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"

I.e., old style C format strings lack positional information as much as iostreams do.

You might want to consider boost::format, which offers support for stating the position in the format string explicitly. From their examples section:

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

Some printf-implementations provide positional arguments, but they are non-standard.

Should I never use C-style format strings?

Apart from performance (as pointed out by Jan Hudec), I don't see a reason. But keep in mind:

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified” - Knuth

and

“Bottlenecks occur in surprising places, so don't try to second guess and put in a speed hack until you have proven that's where the bottleneck is.” - Pike

Yes, printf-implementations are usually faster than iostreams are usually faster than boost::format (from a small and specific benchmark I wrote, but it should largely depend on the situation in particular: if printf=100%, then iostream=160%, and boost::format=220%)

But do not blindly omit thinking about it: How much time do you really spend on text-processing? How long does your program run before exiting? Is it relevant at all to fall back to C-style format strings, loose type safety, decrease refactorbility, increase probability of very subtle bugs that may hide themselves for years and may only reveal themselves right into your favourites customers face?

Personally, I wouldn't fall back if I can not gain more than 20% speedup. But because my applications spend virtually all of their time on other tasks than string-processing, I never had to. Some parsers I wrote spend virtually all their time on string processing, but their total runtime is so small that it isn't worth the testing and verification effort.

Some riddles

Finally, I'd like to preset some riddles:

Find all errors, because the compiler won't (he can only suggest if he's nice):

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

If nothing else, what's wrong with this one?

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



回答9:


While there are a lot of benefits to the C++ iostreams API, one significant problem is has is around i18n. The problem is that the order of parameter substitutions can vary based on the culture. The classic example is something like:

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

While that works for English, in Chinese the family name is comes first.

When it comes to translating your code for foreign markets, translating snippets is fraught with peril so new l10ns may require changes to the code and not just different strings.

boost::format seems to combine the best of stdio (a single format string that can use the parameters in a different order then they appear) and iostreams (type-safety, extensibility).




回答10:


I use iostreams, mainly because that makes it easier to fiddle with the stream later on (if I need it). For example, you could find out that you want to display the output in some trace window -- this is relatively easy to do with cout and cerr. You can, off course, fiddle with pipes and stuff on unix, but that is not as portable.

I do love printf-like formatting, so I usually format a string first, and then send it to the buffer. With Qt, I often use QString::sprintf (although they recommend using QString::arg instead). I've looked at boost.format as well, but couldn't really get used to the syntax (too many %'s). I should really give it a look, though.




回答11:


What I miss about the iolibraries is the formatted input.

iostreams does not have a nice way to replicate scanf() and even boost does not have the required extension for input.




回答12:


stdio is better for reading binary files (like freading blocks into a vector<unsigned char> and using .resize() etc.). See the read_rest function in file.hh in http://nuwen.net/libnuwen.html for an example.

C++ streams can choke on lots of bytes when reading binary files causing a false eof.




回答13:


Since iostreams have become a standard you should use them knowing that your code will work for sure with newer versions of compiler. I guess nowadays most of the compilers know very well about iostreams and there shouldn't be any problem using them.

But if you want to stick with *printf functions there can be no problem in my opinion.



来源:https://stackoverflow.com/questions/119098/which-c-i-o-library-should-be-used-in-c-code

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!