Jak uzyskać rozmiar pliku w bajtach za pomocą C ++ 17

98

Czy istnieją pułapki związane z określonymi systemami operacyjnymi, o których powinienem wiedzieć?

Istnieje wiele duplikatów ( 1 , 2 , 3 , 4 , 5 ) tego pytania, ale odpowiedź na nie została udzielona dziesiątki lat temu. Bardzo wysoko ocenione odpowiedzi na wiele z tych pytań są dziś błędne.

Metody z innych (starych QA) na .sx

  • stat.h (wrapper sprintstatf ), używa syscall

  • tellg () , zwraca zgodnie z definicją pozycję, ale niekoniecznie bajty . Zwracany typ nie jest int.

Jonas Stein
źródło
4
Starter for 10: en.cppreference.com/w/cpp/header/filesystem
Richard Critten
5
@LF: Cóż, pierwsze pytanie zostało zamknięte jako duplikat drugiego, co wyjaśnia, dlaczego przyjęta odpowiedź w pierwszym jest błędna . Trzeci pyta o podobne tellgproblemy. Jedynym, którym warto się przejmować, jest czwarty, a ten nie jest wielki, bo o nim za dużo mówi ofstream, zarówno w pytaniu, jak i odpowiedziach. Ten jest o wiele lepszy w wyrażaniu intencji niż inne (z wyjątkiem pierwszego, który jest dziwnie zamknięty).
Nicol Bolas
6
Przestań dodawać nieistotne informacje do pytania i jego tytułu. Rok nie ma znaczenia; technologie są odpowiednie.
elixenide
2
Co w stat(2)ogóle jest nie tak? Za stary czy co?
Lorinczy Zsigmond
1
@LorinczyZsigmond Co jest nie takstat(2) To nie jest częścią standardu językowego.
Andrew Henle,

Odpowiedzi:

124

<filesystem>(dodane w C ++ 17) czyni to bardzo prostym .

#include <cstdint>
#include <filesystem>

// ...

std::uintmax_t size = std::filesystem::file_size("c:\\foo\\bar.txt");

Jak zauważono w komentarzach, jeśli planujesz użyć tej funkcji, aby zdecydować, ile bajtów odczytać z pliku, pamiętaj, że ...

... o ile plik nie jest otwierany na wyłączność przez Ciebie, jego rozmiar można zmienić między chwilą, gdy o to poprosisz, a próbą odczytania z niego danych.
- Nicol Bolas

HolyBlackCat
źródło
11
Trochę offtopic: czy istnieje świat, w którym std::uintmax_tbędzie można zachować większe wartości niż std::size_t? Jeśli nie, dlaczego nie użyć std::size_t, co prawdopodobnie jest bardziej rozpoznawalne? +1 w odpowiedzi, przy okazji
Fureeish
13
@Fureeish użyłem tylko dlatego, że ten typ file_sizezwraca. Dla mnie też wygląda trochę dziwnie.
HolyBlackCat
39
@Fureeish std::size_tjest wymagane tylko do przechowywania maksymalnego rozmiaru w obiektach pamięci. Pliki mogą być znacznie większe,
Richard Critten
27
@Fureeish Cóż, na 32-bitowym systemie Windows (i zakładam, że na większości nowoczesnych platform 32-bitowych) size_tma 32 bity i uintmax_t64 bity.
HolyBlackCat
16
@HolyBlackCat: Dobrze byłoby powiedzieć coś o tym, że system plików jest globalny, a więc jeśli plik nie jest otwierany na wyłączność przez Ciebie, jego rozmiar można zmienić między momentem, gdy o to poprosisz, a próbą odczytania danych z tego.
Nicol Bolas
28

C ++ 17 zapewnia, std::filesystemco usprawnia wiele zadań na plikach i katalogach. Nie tylko możesz szybko uzyskać rozmiar pliku, jego atrybuty, ale także tworzyć nowe katalogi, iterować pliki, pracować z obiektami ścieżek.

Nowa biblioteka daje nam dwie funkcje, z których możemy skorzystać:

std::uintmax_t std::filesystem::file_size( const std::filesystem::path& p );

std::uintmax_t std::filesystem::directory_entry::file_size() const;

Pierwsza funkcja to funkcja wolna w programie std::filesystem, druga to metoda w directory_entry.

Każda metoda ma również przeciążenie, ponieważ może zgłosić wyjątek lub zwrócić kod błędu (za pośrednictwem parametru wyjściowego). Poniżej znajduje się szczegółowy kod wyjaśniający wszystkie możliwe przypadki.

#include <chrono>
#include <filesystem>  
#include <iostream>

namespace fs = std::filesystem;

int main(int argc, char* argv[])
{
    try
    {
        const auto fsize = fs::file_size("a.out");
        std::cout << fsize << '\n';
    }
    catch (const fs::filesystem_error& err)
    {
        std::cerr << "filesystem error! " << err.what() << '\n';
        if (!err.path1().empty())
            std::cerr << "path1: " << err.path1().string() << '\n';
        if (!err.path2().empty())
            std::cerr << "path2: " << err.path2().string() << '\n';
    }
    catch (const std::exception& ex)
    {
        std::cerr << "general exception: " << ex.what() << '\n';
    }

    // using error_code
    std::error_code ec{};
    auto size = std::filesystem::file_size("a.out", ec);
    if (ec == std::error_code{})
        std::cout << "size: " << size << '\n';
    else
        std::cout << "error when accessing test file, size is: " 
              << size << " message: " << ec.message() << '\n';
}
GOVIND DIXIT
źródło
2
Czym dokładnie jest „to”? Czy możesz wyjaśnić, do czego służy ten cały kod, zwłaszcza gdy zaakceptowana odpowiedź używa znacznie mniej kodu?
Nico Haase