Como obter o tamanho do arquivo em bytes com C ++ 17

97

Existem armadilhas para sistemas operacionais específicos, eu deveria saber?

Existem muitas duplicatas ( 1 , 2 , 3 , 4 , 5 ) desta pergunta, mas elas foram respondidas há décadas. As respostas com votos elevados em muitas dessas questões estão erradas hoje.

Métodos de outros (antigos QAs) em .sx

  • stat.h (wrapper sprintstatf ), usa syscall

  • tellg () , retorna por definição uma posição, mas não necessariamente bytes . O tipo de retorno não é int.

Jonas Stein
fonte
4
Iniciante para 10: en.cppreference.com/w/cpp/header/filesystem
Richard Critten
5
@LF: Bem, a primeira questão foi fechada como uma duplicata da segunda, o que explica por que a resposta aceita na primeira está errada . O terceiro pergunta sobre tellgproblemas semelhantes . O único com o qual vale a pena se preocupar é o quarto, e esse não é grande, já que fala demais ofstream, tanto na pergunta quanto nas respostas. Este é muito melhor em expressar a intenção do que os outros (exceto para o primeiro, que é estranhamente fechado).
Nicol Bolas
6
Pare de adicionar informações irrelevantes à sua pergunta e ao título da pergunta. O ano é irrelevante; as tecnologias são relevantes.
elixenida 01 de
2
O que há de errado com isso stat(2)? Ficou muito velho ou o quê?
Lorinczy Zsigmond
1
@LorinczyZsigmond O que há de errado comstat(2) Não faz parte do padrão de linguagem.
Andrew Henle

Respostas:

123

<filesystem>(adicionado em C ++ 17) torna isso muito simples .

#include <cstdint>
#include <filesystem>

// ...

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

Conforme observado nos comentários, se você planeja usar esta função para decidir quantos bytes ler do arquivo, lembre-se de que ...

... a menos que o arquivo seja aberto exclusivamente por você, seu tamanho pode ser alterado entre o momento em que você o solicita e o momento em que tenta ler os dados dele.
- Nicol Bolas

HolyBlackCat
fonte
11
Pequeno offtopic: existe um mundo onde std::uintmax_tserá capaz de manter valores maiores do que std::size_t? Se não, por que não usar std::size_t, que provavelmente é mais reconhecível? +1 na resposta,
aliás
13
@Fureeish eu usei apenas porque esse é o tipo que file_sizeretorna. Parece um pouco estranho para mim também.
HolyBlackCat
39
@Fureeish std::size_tsó é necessário para manter o tamanho máximo de objetos na memória. Os arquivos podem ser consideravelmente maiores,
Richard Critten
26
@Fureeish Bem, no Windows de 32 bits (e suponho que na maioria das plataformas modernas de 32 bits) size_té de 32 bits e uintmax_t64 bits.
HolyBlackCat
16
@HolyBlackCat: Seria bom dizer algo sobre o fato de que o sistema de arquivos é global e, portanto, a menos que o arquivo seja aberto exclusivamente por você, seu tamanho pode ser alterado entre o momento em que você pede e o momento em que tenta ler os dados a partir dele.
Nicol Bolas
28

C ++ 17 traz o std::filesystemque agiliza muitas tarefas em arquivos e diretórios. Não apenas você pode obter rapidamente o tamanho do arquivo e seus atributos, mas também criar novos diretórios, iterar por meio de arquivos e trabalhar com objetos de caminho.

A nova biblioteca nos oferece duas funções que podemos usar:

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

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

A primeira função é uma função livre em std::filesystem, a segunda é um método em directory_entry.

Cada método também tem uma sobrecarga, pois pode lançar uma exceção ou retornar um código de erro (por meio de um parâmetro de saída). Abaixo está o código detalhado explicando todos os casos possíveis.

#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
fonte
2
O que exatamente é "isso"? Você pode explicar para que serve todo esse código, especialmente quando a resposta aceita usa muito menos código?
Nico Haase