Como você determina o tamanho de um arquivo em C?

137

Como posso descobrir o tamanho de um arquivo, em bytes?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}
andrewrk
fonte
Você precisará usar uma função de biblioteca para recuperar os detalhes de um arquivo. Como o C é completamente independente da plataforma, você precisará nos informar para qual plataforma / sistema operacional você está desenvolvendo!
Chris Roberts
Por char* fileque não FILE* file? -1
Oscar Oscar
-1 porque as funções de arquivo devem aceitar descritores de arquivo e não caminhos de arquivo
Senhor Oscar

Respostas:

144

Com base no código do NilObject:

#include <sys/stat.h>
#include <sys/types.h>

off_t fsize(const char *filename) {
    struct stat st; 

    if (stat(filename, &st) == 0)
        return st.st_size;

    return -1; 
}

Alterar:

  • Tornou o argumento do nome do arquivo a const char.
  • Corrigida a struct statdefinição, na qual faltava o nome da variável.
  • Retorna -1com erro em vez de 0, o que seria ambíguo para um arquivo vazio. off_té um tipo assinado, portanto isso é possível.

Se você deseja fsize()imprimir uma mensagem com erro, pode usar o seguinte:

#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

off_t fsize(const char *filename) {
    struct stat st;

    if (stat(filename, &st) == 0)
        return st.st_size;

    fprintf(stderr, "Cannot determine size of %s: %s\n",
            filename, strerror(errno));

    return -1;
}

Em sistemas de 32 bits, você deve compilar isso com a opção -D_FILE_OFFSET_BITS=64, caso contrário off_t, manterá valores de até 2 GB. Consulte a seção "Usando o LFS" do Suporte a arquivos grandes no Linux para obter detalhes.

Ted Percival
fonte
19
Isso é específico para Linux / Unix - provavelmente vale a pena apontar isso, pois a pergunta não especificou um sistema operacional.
Drew Hall
1
Você provavelmente pode alterar o tipo de retorno para ssize_t e converter o tamanho de off_t sem nenhum problema. Parece fazer mais sentido usar um ssize_t :-) (Não deve ser confundida com size_t que é não assinado e não pode ser usada para indicar erro.)
Ted Percival
1
Para um código mais portátil, use fseek+ ftellconforme proposto por Derek.
Ciro Santilli #
9
Para um código mais portátil, use fseek+ ftellconforme proposto por Derek. Não. O Padrão C especifica especificamente que fseek()para SEEK_ENDum arquivo binário é um comportamento indefinido. 7.19.9.2 A fseekfunção ... Um fluxo binário não precisa oferecer suporte significativo a fseekchamadas com um valor deSEEK_END e, conforme observado abaixo, que é da nota de rodapé 234 na p. 267 do Padrão C vinculado e que rotula especificamente fseekpara SEEK_ENDum fluxo binário como comportamento indefinido. .
Andrew Henle
74

Não use int. Arquivos com mais de 2 gigabytes de tamanho são comuns hoje em dia

Não use unsigned int. Arquivos com mais de 4 gigabytes de tamanho são comuns, como uma sujeira um pouco menos comum

IIRC, a biblioteca padrão define off_tcomo um número inteiro de 64 bits não assinado, que é o que todos deveriam usar. Podemos redefinir isso para 128 bits em alguns anos, quando começamos a ter 16 arquivos de exabyte por aí.

Se você estiver no Windows, use GetFileSizeEx - ele realmente usa um número inteiro de 64 bits assinado, para que eles comecem a ter problemas com 8 arquivos de exabyte. Microsoft tolo! :-)

Orion Edwards
fonte
1
Eu usei compiladores onde off_t é de 32 bits. É verdade que isso ocorre em sistemas embarcados onde arquivos de 4 GB são menos comuns. De qualquer forma, o POSIX também define off64_t e métodos correspondentes para adicionar à confusão.
Aaron Campbell
Eu sempre amo respostas que assumem o Windows e não fazem mais nada além de criticar a pergunta. Você poderia adicionar algo compatível com POSIX?
SS Anne
1
@ JL2210, a resposta aceita de Ted Percival mostra uma solução compatível com posix, então não vejo sentido em repetir o óbvio. Eu (e 70 outros) pensamos que adicionar a observação sobre o Windows e não usar números inteiros assinados de 32 bits para representar o tamanho dos arquivos era um valor agregado. Cheers
Orion Edwards
30

A solução de Matt deve funcionar, exceto que é C ++ em vez de C, e a instrução inicial não deve ser necessária.

unsigned long fsize(char* file)
{
    FILE * f = fopen(file, "r");
    fseek(f, 0, SEEK_END);
    unsigned long len = (unsigned long)ftell(f);
    fclose(f);
    return len;
}

Corrigido sua chave para você também. ;)

Atualização: Esta não é realmente a melhor solução. É limitado a arquivos de 4 GB no Windows e provavelmente é mais lento do que usar uma chamada específica da plataforma, como GetFileSizeExou stat64.

Derek Park
fonte
Sim você deveria. No entanto, a menos que haja uma razão realmente convincente para não escrever específica da plataforma, você provavelmente deve usar apenas uma chamada específica da plataforma, em vez do padrão aberto / procurar-final / contar / fechar.
Derek Parque
1
Desculpe a resposta tardia, mas estou tendo um grande problema aqui. Isso faz o aplicativo travar ao acessar arquivos restritos (como protegido por senha ou arquivos do sistema). Existe uma maneira de solicitar uma senha ao usuário quando necessário?
23413 Justin
@ Justin, você provavelmente deve abrir uma nova pergunta especificamente sobre o problema que está enfrentando e fornecer detalhes sobre a plataforma em que está, como está acessando os arquivos e qual é o comportamento.
Derek Parque
1
C99 e C11 retornam long intde ftell(). (unsigned long)a transmissão não melhora o alcance, como já limitado pela função. ftell()retorne -1 com erro e que sejam ofuscados com o elenco. Sugerir fsize()retornar o mesmo tipo que ftell().
chux - Reinstala Monica
Concordo. O elenco deveria combinar com o protótipo original da questão. Não me lembro por que o transformei em sem assinatura por muito tempo, em vez de sem assinatura.
Derek Park
15

** Não faça isso ( por quê? ):

Citando o documento padrão C99 que eu encontrei on-line: "A configuração do indicador de posição do arquivo como final de arquivo, com fseek(file, 0, SEEK_END)comportamento indefinido para um fluxo binário (devido a possíveis caracteres nulos à direita) ou para qualquer fluxo com codificação dependente do estado que certamente não termina no estado de turno inicial. **

Altere a definição para int para que as mensagens de erro possam ser transmitidas e use fseek()e ftell()para determinar o tamanho do arquivo.

int fsize(char* file) {
  int size;
  FILE* fh;

  fh = fopen(file, "rb"); //binary mode
  if(fh != NULL){
    if( fseek(fh, 0, SEEK_END) ){
      fclose(fh);
      return -1;
    }

    size = ftell(fh);
    fclose(fh);
    return size;
  }

  return -1; //error
}
andrewrk
fonte
5
@mezhaka: Esse relatório do CERT está simplesmente errado. fseekoe ftello(ou fseeke ftellse você está preso sem o primeiro e feliz com limites sobre o tamanho dos arquivos que você pode trabalhar com) são a maneira correta para determinar o comprimento de um arquivo. statAs soluções baseadas em software não funcionam em muitos "arquivos" (como dispositivos de bloco) e não são portáveis ​​para sistemas não POSIX-ish.
R .. GitHub Pare de ajudar o gelo
1
Esta é a única maneira de obter o tamanho do arquivo em muitos sistemas não POSIX (como o meu mbed muito minimalista)
Earlz
9

POSIX

O padrão POSIX possui seu próprio método para obter o tamanho do arquivo.
Inclua o sys/stat.hcabeçalho para usar a função.

Sinopse

  • Obter estatísticas de arquivo usando stat(3).
  • Obter a st_sizepropriedade.

Exemplos

Nota : Limita o tamanho a 4GB. Caso contrário Fat32, use a versão de 64 bits!

#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    struct stat info;
    stat(argv[1], &info);

    // 'st' is an acronym of 'stat'
    printf("%s: size=%ld\n", argv[1], info.st_size);
}
#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    struct stat64 info;
    stat64(argv[1], &info);

    // 'st' is an acronym of 'stat'
    printf("%s: size=%ld\n", argv[1], info.st_size);
}

ANSI C (padrão)

O ANSI C não fornece diretamente a maneira de determinar o comprimento do arquivo.
Teremos que usar nossa mente. Por enquanto, usaremos a abordagem de busca!

Sinopse

  • Procure o arquivo até o fim usando fseek(3).
  • Obtenha a posição atual usando ftell(3).

Exemplo

#include <stdio.h>

int main(int argc, char** argv)
{
    FILE* fp = fopen(argv[1]);
    int f_size;

    fseek(fp, 0, SEEK_END);
    f_size = ftell(fp);
    rewind(fp); // to back to start again

    printf("%s: size=%ld", (unsigned long)f_size);
}

Se o arquivo é stdinou um tubo. POSIX, ANSI C não funcionará.
Ele retornará 0se o arquivo for um pipe ou stdin.

Opinião : você deve usar o padrão POSIX . Porque, ele tem suporte para 64 bits.


fonte
1
struct _stat64e __stat64()para _Windows.
21819 Bob Stein
5

E se você estiver criando um aplicativo do Windows, use a API GetFileSizeEx, pois a E / S do arquivo CRT é confusa, especialmente para determinar o tamanho do arquivo, devido a peculiaridades nas representações de arquivos em diferentes sistemas;)


fonte
5

Se você está bem com o uso da biblioteca std c:

#include <sys/stat.h>
off_t fsize(char *file) {
    struct stat filestat;
    if (stat(file, &filestat) == 0) {
        return filestat.st_size;
    }
    return 0;
}
NilObject
fonte
24
Esse não é o padrão C. Faz parte do padrão POSIX, mas não o padrão C.
Derek Parque
3

Uma pesquisa rápida no Google encontrou um método usando fseek e ftell e um tópico com esta pergunta com respostas que não podem ser feitas apenas em C de outra maneira.

Você pode usar uma biblioteca de portabilidade como o NSPR (a biblioteca que alimenta o Firefox) ou verificar sua implementação (um tanto complicada).

Nickolay
fonte
1

Eu usei esse conjunto de códigos para encontrar o tamanho do arquivo.

//opens a file with a file descriptor
FILE * i_file;
i_file = fopen(source, "r");

//gets a long from the file descriptor for fstat
long f_d = fileno(i_file);
struct stat buffer;
fstat(f_d, &buffer);

//stores file size
long file_length = buffer.st_size;
fclose(i_file);
rco16
fonte
1

Tente isto -

fseek(fp, 0, SEEK_END);
unsigned long int file_size = ftell(fp);
rewind(fp);

O que isso faz é primeiro, procure o final do arquivo; em seguida, relate onde está o ponteiro do arquivo. Por fim (isso é opcional), ele volta ao início do arquivo. Observe que fpdeve ser um fluxo binário.

file_size contém o número de bytes que o arquivo contém. Observe que, como (de acordo com climits.h), o tipo longo não assinado é limitado a 4294967295 bytes (4 gigabytes), você precisará encontrar um tipo de variável diferente se for possível lidar com arquivos maiores que isso.

adrian
fonte
3
Como isso é diferente da resposta de Derek de 8 anos atrás?
PP
Esse é um comportamento indefinido para um fluxo binário e para um fluxo de texto ftellnão retorna um valor representativo do número de bytes que podem ser lidos no arquivo.
Andrew Henle
0

Eu tenho uma função que funciona bem apenas stdio.h. Eu gosto muito e funciona muito bem e é bastante conciso:

size_t fsize(FILE *File) {
    size_t FSZ;
    fseek(File, 0, 2);
    FSZ = ftell(File);
    rewind(File);
    return FSZ;
}
Mr Oscar
fonte
0

Aqui está uma função simples e limpa que retorna o tamanho do arquivo.

long get_file_size(char *path)
{
    FILE *fp;
    long size = -1;
    /* Open file for reading */
    fp = fopen(path, "r");
    fseek(fp, 0, SEEK_END);
    size = ftell(fp); 
    fp.close();
    return 
}
Abdessamad Doughri
fonte
1
Você não precisa fechar o arquivo?
Jerry Jeremiah
Não, não gosto de funções que esperam um caminho. Em vez disso, por favor, faça ti exppect um ponteiro de arquivo
Sr. Oscar
-3

Você pode abrir o arquivo, vá para 0 deslocamento relativo na parte inferior do arquivo com

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

o valor retornado do fseek é o tamanho do arquivo.

Não codifiquei em C por um longo tempo, mas acho que deve funcionar.

PabloG
fonte
12
Você não precisa definir algo como SEEKBOTTOM. #include <stdio.h> fseek (identificador, 0, SEEK_END);
sigjuice
-4

Olhando para a pergunta, ftellé fácil obter o número de bytes.

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);
zishan
fonte
ftellespera um descritor de arquivo, não um nome de arquivo, como argumento.
Barmar 30/09/16
@ Barmar, No ftellnão espera um descritor de arquivo, ele espera um FILE*. Veja a página de manual primeiro!
A abordagem está completamente errada. É constante que ftellretornaria 0sempre!
Esta resposta está totalmente errada, pois, por exemplo, você precisa fseek()primeiro usar para procurar o final do arquivo e também ftell()espera uma FILE *, não uma string! Você estaria bem servido para elaborar sua resposta.
Oscar Oscar