Reverse-A-File Utility

Back in this thread http://www.cplusplus.com/forum/lounge/59766/ I suggested that OP try to write a file reversing utility. And then my conscience kicked in, so I took my own advice.

This is the C++11 source code for that thing.
I felt like my IQ dropped a little while writing it, so I'd like to know if anyone can spot bugs, design issues, or has suggestions for another (simpler) approach. Thank you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* reverse_file.cpp - Reverse-A-File Utility */

#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char **argv)
{
    if (argc != 3)
    {
        std::cerr << "Usage: program.exe input_file output_file" << std::endl;
        return 1;
    }

    const std::string input_filename(argv[1]);
    const std::string output_filename(argv[2]);

    if (input_filename.compare(output_filename) == 0)
    {
        std::cerr << "The files MUST be different." << std::endl;
        return 2;
    }

    std::ifstream input_file(input_filename, std::ios_base::binary);
    std::ofstream output_file(output_filename, std::ios_base::binary);

    if (!input_file.is_open())
    {
        std::cerr << "Could not open input file `" << input_filename << "'." << std::endl;
        return 3;
    }

    if (!output_file.is_open())
    {
        std::cerr << "Could not open output file `" << output_filename << "'." << std::endl;
        return 4;
    }

    input_file.exceptions(std::ios_base::badbit);
    output_file.exceptions(std::ios_base::badbit | std::ios_base::failbit);

    std::vector<char> buffer(10 * 1024 * 1024);

    try
    {
        input_file.seekg(0, std::ios_base::end);

        const auto first_chunk_size = input_file.tellg() % buffer.size();
        auto remaining_bytes = input_file.tellg();

        input_file.seekg(-first_chunk_size, std::ios_base::end);
        input_file.read(&buffer.front(), first_chunk_size);
        std::reverse(buffer.begin(), buffer.begin() + first_chunk_size);
        output_file.write(buffer.data(), first_chunk_size);
        input_file.seekg(-first_chunk_size, std::ios_base::end);
        remaining_bytes -= first_chunk_size;

        while (remaining_bytes != 0)
        {
            input_file.seekg(-buffer.size(), std::ios_base::cur);
            input_file.read(&buffer.front(), buffer.size());
            std::reverse(buffer.begin(), buffer.end());
            output_file.write(buffer.data(), buffer.size());
            input_file.seekg(-buffer.size(), std::ios_base::cur);
            remaining_bytes -= buffer.size();
        }
    }
    catch (std::ios_base::failure &sif)
    {
        std::cerr << "File exception: " << sif.what() << "." << std::endl;
        return 5;
    }
    catch (...)
    {
        std::cerr << "Something." << std::endl;
        return 6;
    }

    return 0;
}

Why do some people use the three-way string::compare() when they only need to check equality? I keep seeing this on the net here and there, and it seems odd.
@ Cubbi: Thanks. Honestly, I totally forgot about the == operator.
And last time I checked it wasn't a member of std::string, I guess that may answer your question.
As for making this reversal efficient, add a little boost (or OS-specific API if you like)

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <fstream>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/range.hpp>
#include <boost/range/algorithm.hpp>
namespace io = boost::iostreams;
int main()
{
    io::mapped_file_source in("test.bin");
    std::ofstream out("test.out");
    reverse_copy(boost::make_iterator_range(in.data(), in.data()+in.size()),
                 std::ostreambuf_iterator<char>(out));
}


>
1
2
std::ifstream input_file(input_filename, std::ios_base::binary);
 input_file.seekg(0, std::ios_base::end);


Wouldn't this lead to undefined behavoiur?

C++ specifies that the effect of a seekoff() on a filebuf is the same as a call to std::fseek() with basic_ios::end being mapped to SEEK_END. And C89 specifies that setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream.
https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+file
@ JLBorges: I don't know, does it? Seems to work fine. Maybe this would be an issue for C89 only?
> Seems to work fine.

Yes, in practise, it works on every implementation that I know of. So much so, that it is like assuming that a byte is an octet. Mentioned it because of the request for comments.

> Maybe this would be an issue for C89 only

If it is an issue with C89, it would also be an issue with C++11. C++ specifies the effect of seekoff() on a filebuf in terms of a call to std::fseek(), which is the C89 fseek() in the std namespace.
@JLBorges
That is an issue with the C99 standard, not the C90 or POSIX standards, which every extant implementation of seek() follows. C++, as you know, is C90 friendly, but C99 is antagonistic to it. I don't know why that language exists in the C99 standard, as it has been an industry standard practice to find a binary file's length that way, and it would impact existing code for a nominal worry over a mal-formed file.
> That is an issue with the C99 standard, not the C90 or POSIX standards

I see that now. Thanks.
http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2006-07/msg00175.html

So fseek() shouldn't normally be trusted when used with SEEK_END?
It seems this is just a special case, for certain files in certain filesystems.
> So fseek() shouldn't normally be trusted when used with SEEK_END?

In theory, fseek() used with SEEK_END should not be used on streams opened in binary mode.

In practise, haven't yet heard of an implementation where it does not work.

Note: fseek() used with SEEK_END is well defined for streams opened in text mode. Though a subsequent ftell() need not give the size of the stream. (Would not on Windows, Symbian, non-BSD versions of Mac OS etc.).
Topic archived. No new replies allowed.