Stefan Ram wrote:
> I am thinking about writing a program that reads from a text file.
>
> I wonder what the best practices are to do this in C++.
>
> For example, I could use »getchar« and assume that the program
> is used from the command line with shell redirection of stdin
> via »<« or I could accept the path of the file via »argc« and
> then construct an »ifstream« object. Which possibility is better?
Use command options (or special file names such as '-') to select the
input and output source.
> Here is an example of a program I wrote to remove all »x«
> while copying the contents of a file:
>
> #include <iostream>
> #include <istream>
> #include <ostream>
> #include <fstream>
> #include <iterator>
>
> int main()
> { ::std::ifstream i{ "C:\\example\\source.txt" }; if( i )
> { ::std::ofstream o{ "C:\\example\\target.txt" }; if( o )
> { ::std::istreambuf_iterator< char >O;
> ::std::istreambuf_iterator< char >p{ i };
> ::std::ostreambuf_iterator< char >q{ o };
> while( p != O )
> { char const ch = *p;
> if( ch != 'x' )*q = ch;
> ++q; ++p; }}}}
>
> You might find my placements of »if( i )« strange, but apart
> from the the formatting, are there any deficits in the code?
The joint most important characteristic (along with correctness) for
code is clarity! So drop the superfluous colons and add in some whitespace.
You should make used of the standard library where possible, so I would
have written this more like:
int main( int argc, char** argv )
{
if( argc < 2 ) return EXIT_FAILURE;
std::ifstream in {argv[1]};
if( in )
{
std::ofstream out {argv[2]};
if( out )
{
using inIterator = std::istreambuf_iterator<char>;
using outIterator = std::ostreambuf_iterator<char>;
copy_if( inIterator(in), inIterator(), outIterator(out),
[]( char c ){ return c != 'x'; } );
}
}
return EXIT_SUCCESS;
}
Using copy_if makes it clear what the code is doing.
> (When I was young I used to carefully wrap stream
> constructions into »try« blocks and release resources
> and closed files explicitly. Then I learned more about
> RAII. I now hope the the above program with out
> »try« blocks or »close« calls still handles all
> resources correctly. Please let me know if it
> would be better to call »close« explictly.)
Streams are a manifestation of RAII, so everything will get released.
> I wish there was a collection of C++ snippets that can
> be used as templates for common situations! But those
> snippets should really reflect best practices. Rosetta
> code might be such a collection, but it might not reflect
> best practices (for C++17). Sometimes, I try to look up
> what I want to do in a book by Stroustrup or Sutter, but
> they might have simplified some example code for
> pedagogical reasons. Boost should be a good read for
> high-quality example code, but I am not sure whether
> I can find code for a simple file I/O situation there.
The standard algorithms are a good place to start.
> I think user code should never call library functions
> directly, but always wrap them. For example, don't call
> »getchar«, but »mygetchar«, and implement »mygetchar«
> to forward to »getchar« (we got perfect forwarding!).
> Then, when on decides later to want to read from a string
> than from a file, it is easy to just change »mygetchar«.
Why? If you need to do that there must be something unsuitable with the
library function.
> But above, I do not use a function, but an operator.
You used neither, just normal code.
> So the preceding paragraph then would say that one
> should never use a library /class/ directly, but always
> a custom wrapper for it. Writing such a custom wrapper
> seems to be more difficult for a class than for a function,
> but with the new possibility to inherit constructors,
> it might be quite easy.
No matter how easy, it would still be pointless.
--
Ian Collins