Subscribe to Anarion's Blog        RSS Feed
-----

Determining a file's size in C++

Icon 11 Comments
Recently I got curious to explore the methods that are available to find out a file's size in C++. The first (easiest?) method that I had learned a long time ago is using tellg(). Let's see an example:
#include <iostream>
#include <fstream>

using namespace std;

int main() {

    ifstream file("test", ios::ate);
    streampos size = file.tellg();
    cout<<"File size for \"test\" is: "<<size<<" bytes."<<endl;

    return 0;
}

Simple. First, a file stream is opened with ios::ate flag which sets the stream's position indicator at the end of the file. Then, tellg() is used to retrieve this position index.
Back then, this was the only method I used to find a file's size. However, in some cases it would be handy to be able to retrieve a file's size without having to open the file. Other than that, some say that if the file has been opened in binary mode, sometimes the returned size could be incorrect(?).
Whether the latter fact is true or not, opening a file just to determine it's size doesn't seem to be the best approach. What other method is available, then?

An alternative approach is to use the stat struct. Let's see it in action:
#include <iostream>
#include <sys/stat.h>

using namespace std;

int main() {

    struct stat st;
    stat("test", &st);
    cout<<"File size for \"test\" is: "<<st.st_size<<" bytes."<<endl;

    return 0;
}

I know! struct stat st; looks like C! In C++ you don't need to write struct every time you want to declare a structure instance. However, notice that in <sys/stat.h>, there is also a function with the same name. Without the extra struct in front of it, the compiler comes up with an error because it mistakenly tries to consider the function.
The stat method works without the need to open the file. However, your code becomes platform-dependent to some degree. The stat function and struct are available in POSIX-based systems. In Windows, however, the equivalent is _stat. Given that, it's not difficult to write a function that works on both platforms alike, with a little bit of macro help:
#include <iostream>
#include <string>
#include <stdexcept>
#include <sys/stat.h>

long int file_size(const std::string& file_name) {
    #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
        #define STAT_IDENTIFIER stat //for MacOS X and other Unix-like systems
    #elif defined(_WIN32)
        #define STAT_IDENTIFIER _stat //for Windows systems, both 32bit and 64bit?
    #endif
    struct STAT_IDENTIFIER st;
    if(STAT_IDENTIFIER(file_name.c_str(), &st) == -1) {
        throw std::runtime_error("stat error!");
    }
    return st.st_size;
}

using namespace std;

int main() {

    try {
        cout<<"File size for \"test\" is: "<<file_size("test")<<" bytes."<<endl;
    } catch(const exception& e) {
        cerr<<e.what()<<endl;
    }

    return 0;
}

If you check Boost's filesystem library, you will find it's file_size function is using the stat approach. Until a filesystem library is added to the C++ standard (hopefully!), the better approach to determine a file's size seems to be the stat method, whether you use Boost or a function like above.

It would be interesting for me to see if the above code works in Visual Studio and Mac OS X as well, as the only platform I currently have access to is GNU/Linux. Please share your result if you test it.

Reference on macros used: here

11 Comments On This Entry

Page 1 of 1

Martyr2 Icon

16 August 2015 - 03:00 PM
I can confirm that this function works on Windows 7 with Visual Studio 2013.
1

CTphpnwb Icon

16 August 2015 - 06:19 PM
This works sort of in OS X using Xcode. If I use the code as is, and there is a file named test then I get the run time error stat error!. If I rename the file test.txt and use that in the code, I get the correct file size.
1

CTphpnwb Icon

16 August 2015 - 07:57 PM
By the way, it doesn't seem to matter what extension I use, as long as I use one!
0

Anarion Icon

17 August 2015 - 04:38 PM

Martyr2, on 17 August 2015 - 02:30 AM, said:

I can confirm that this function works on Windows 7 with Visual Studio 2013.

Thanks for your feedback!
Windows' box is now checked!
0

Anarion Icon

17 August 2015 - 06:32 PM

CTphpnwb, on 17 August 2015 - 05:49 AM, said:

This works sort of in OS X using Xcode. If I use the code as is, and there is a file named test then I get the run time error stat error!. If I rename the file test.txt and use that in the code, I get the correct file size.

Could you please test it with this modified version as well?
long int file_size(const std::string& file_name) {
    #if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
        #define STAT_IDENTIFIER stat //for MacOS X and other Unix-like systems
    #elif defined(_WIN32)
        #define STAT_IDENTIFIER _stat //for Windows systems, both 32bit and 64bit?
    #endif
    struct STAT_IDENTIFIER st;
    if(STAT_IDENTIFIER(file_name.c_str(), &st) == -1) {
        perror(nullptr);
        throw std::runtime_error("stat error!");
    }
    return st.st_size;
}

I'm guessing the error would be "No such file or directory"?
Regarding your second comment:

Quote

By the way, it doesn't seem to matter what extension I use, as long as I use one!

Just to make sure I understand correctly, you are saying that if there is a file named "test.txt" in the directory, whether the code tries to get the size for "test.txt" or even "test.any", it succeeds? In which case, it discards the extension :-\ That's weird...
0

CTphpnwb Icon

17 August 2015 - 07:13 PM
Same issue with that version. It succeeds as long as an extension is used and fails if one isn't. The extension used in the code has to match the one on the file.
0

CTphpnwb Icon

18 August 2015 - 05:51 AM
Oh, and yes, I get No such file or directory.
0

Anarion Icon

18 August 2015 - 04:53 PM

CTphpnwb, on 18 August 2015 - 06:43 AM, said:

Same issue with that version. It succeeds as long as an extension is used and fails if one isn't. The extension used in the code has to match the one on the file.

This is odd, as Mac OS X should allow files without extensions just line Linux does. Have you had such issue with file streams too? Like the first example in the blog post which uses tellg? It would be interesting to see if that method has the same issue or not?
0

CTphpnwb Icon

20 August 2015 - 08:25 AM
OS X does allow files with no extension. It's the code that's having trouble seeing it.

#include <iostream>
#include <string>
#include <stdexcept>
#include <sys/stat.h>
#include <fstream>

long int file_size(const std::string& file_name) {
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#define STAT_IDENTIFIER stat //for MacOS X and other Unix-like systems
#elif defined(_WIN32)
#define STAT_IDENTIFIER _stat //for Windows systems, both 32bit and 64bit?
#endif
	struct STAT_IDENTIFIER st;
	if(STAT_IDENTIFIER(file_name.c_str(), &st) == -1) {
		perror(nullptr);
		throw std::runtime_error("stat error!");
	}
	return st.st_size;
}

using namespace std;

int main() {
	try {
		cout<<"File size for \"test\" is: "<<file_size("test")<<" bytes."<<endl;
	} catch(const exception& e) {
		cerr<<e.what()<<endl;
	}

	ifstream file("test", ios::ate);
	streampos size = file.tellg();
	cout<<"File size for \"test\" is: "<<size<<" bytes."<<endl;


	return 0;
}

Quote

No such file or directory
File size for "test" is: stat error!
File size for "test" is: -1 bytes.
0

CTphpnwb Icon

20 August 2015 - 10:27 AM
Oh crap! I was using the Finder to "rename" the file, but it was just hiding the extension, not actually removing it. When I removed the extension it worked.
:stupid:
0

Anarion Icon

22 August 2015 - 03:57 PM

CTphpnwb, on 20 August 2015 - 09:57 PM, said:

Oh crap! I was using the Finder to "rename" the file, but it was just hiding the extension, not actually removing it. When I removed the extension it worked.
:stupid:/>

Now I can rest in peace! :bananaman: Thank you for testing the code!
0
Page 1 of 1

October 2017

S M T W T F S
1234567
891011121314
1516 17 18192021
22232425262728
293031    

Tags

    Recent Entries

    Recent Comments

    Search My Blog

    0 user(s) viewing

    0 Guests
    0 member(s)
    0 anonymous member(s)