7

I have a class that has a filestream of type ofstream. The constructor opens the file in append mode and all the messages always get written at the end of the file.

I need to write into outputFile up to some fixed size say 1Mb, then I need to close, rename, and compress it, and then open a new file of the same name.

This needs to be done when a certain size of file is reached.

I tried using tellg() but after reading stuffs (and this) on internet, I understood that this is not the right approach.

As I'm new to C++, I'm trying to find out the most optimized and correct way to get the accurate current size of file opened by ofstream?

class Logger {
    std::ofstream outputFile;
    int curr_size;
    Logger (const std::string logfile) : outputFile(FILENAME,
                                                     std::ios::app)
    {
        curr_size = 0;
    }
};

Somewhere in the program, I'm writing data into it:

    // ??? Determine the size of current file ???

    if (curr_size >= MAX_FILE_SIZE) {
        outputFile.close();
        //Code to rename and compress file
        // ...
        outputFile.open(FILENAME, std::ios::app);
        curr_size = 0;
    }

    outputFile << message << std::endl;
    outputFile.flush();
void
  • 338
  • 5
  • 19

2 Answers2

2

fstreams can be both input and output streams. tellg() will return the input position and tellp() will tell you of the output position. tellp() will after appending to a file tell you its size.

Consider initializing your Logger like this (edit: added example for output stream operator):

#include <iostream>
#include <fstream>

class Logger {
    std::string m_filename;
    std::ofstream m_os;
    std::ofstream::pos_type m_curr_size;
    std::ofstream::pos_type m_max_size;
public:
    Logger(const std::string& logfile, std::ofstream::pos_type max_size) :
        m_filename(logfile),
        m_os(m_filename, std::ios::app),
        m_curr_size(m_os.tellp()),
        m_max_size(max_size)
    {}

    template<typename T>
    friend Logger& operator<<(Logger&, const T&);
};

template<typename T>
Logger& operator<<(Logger& log, const T& msg) {
    log.m_curr_size = (log.m_os << msg << std::flush).tellp();

    if(log.m_curr_size>log.m_max_size) {
        log.m_os.close();
        //rename & compress
        log.m_os = std::ofstream(log.m_filename, std::ios::app);
        log.m_curr_size = log.m_os.tellp();
    }
    return log;
}

int main()
{
    Logger test("log", 4LL*1024*1024*1024*1024);
    test << "hello " << 10 << "\n";
    return 0;
}

If you use C++17 or have an experimental version of <filesystem> available, you could also use that to get the absolute file size, like this:

#include <iostream>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;

class Logger {
    fs::directory_entry m_logfile;
    std::ofstream m_os;
    std::uintmax_t m_max_size;

    void rotate_if_needed() {
        if(max_size_reached()) {
            m_os.close();
            //rename & compress
            m_os = std::ofstream(m_logfile.path(), std::ios::app);
        }
    }
public:
    Logger(const std::string& logfile, std::uintmax_t max_size) :
        m_logfile(logfile),
        m_os(m_logfile.path(), std::ios::app),
        m_max_size(max_size)
    {
        // make sure the path is absolute in case the process
        // have changed current directory when we need to rotate the log
        if(m_logfile.path().is_relative())
            m_logfile = fs::directory_entry(fs::absolute(m_logfile.path()));
    }

    std::uintmax_t size() const { return m_logfile.file_size(); }
    bool max_size_reached() const { return size()>m_max_size; }

    template<typename T>
    friend Logger& operator<<(Logger&, const T&);
};

template<typename T>
Logger& operator<<(Logger& log, const T& msg) {
    log.m_os << msg << std::flush;
    log.rotate_if_needed();
    return log;
}

int main()
{
    Logger test("log", 4LL*1024*1024*1024*1024);
    std::cout << test.size() << "\n";
    test << "hello " << 10 << "\n";
    std::cout << test.size() << "\n";
    test << "some more " << 3.14159 << "\n";
    std::cout << test.size() << "\n";
    return 0;
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 1
    Doesn't [this](https://stackoverflow.com/questions/22984956/tellg-function-give-wrong-size-of-file) apply to `tellp` as well? – GPhilo Jan 17 '19 at 13:59
  • 1
    Perhaps if you move the output pointer after you've appended to the file or if you have multiple streams to the file but no, not as used in OP:s question. [std::basic_ostream::tellp](https://en.cppreference.com/w/cpp/io/basic_ostream/tellp) – Ted Lyngmo Jan 17 '19 at 14:03
  • @TedLyngmo, I tried initializing my Logger as you suggested, but the value of curr_size always remains 0 throughout the execution of the program. After writing the message to the stream, I print the curr_size but it shows 0. `outputFile << message << std::endl; std::cout << "Bytes: " << curr_size << std::endl; //prints zero` – void Jan 17 '19 at 14:58
  • 1
    You need to do `outputFile << logmsg; curr_size = outputFile.tellp();`. Added example. – Ted Lyngmo Jan 17 '19 at 15:13
  • 1
    @C_user5 Added example providing a max size and a logging output stream operator accepting the same types as `std::ofstream`. Also added a `` example. – Ted Lyngmo Jan 17 '19 at 16:32
  • 1
    "tellg() will return the input position and tellp() will tell you of the output position" <- Most intuitive API of the year award, nineteen-ninety-something :-( – einpoklum Jul 05 '20 at 08:03
  • @einpoklum Very intuitive indeed :-) They didn't like long names, like `input_position()` and `output_position()`, back then I guess. I assume the `g` is for _get_ and the `p` is for _put_. – Ted Lyngmo Jul 05 '20 at 08:44
  • 1
    @TedLyngmo: I'm guessing they based themselves on Unix library conventions; like how C's `stdio.h` has `ftell()`. – einpoklum Jul 05 '20 at 08:53
0

I gave it a try with tellp() and it works fine for me:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
  ofstream myFile("data.txt", ios_base::app);
  myFile << "Hello World!" << endl;

  cout << myFile.tellp() << endl;
  return 0;
}

This is the output, when calling this program:

$ ./program
13
$ ./program
26
$ ./program
39
Tom Mekken
  • 1,019
  • 1
  • 12
  • 27