¿Cómo se determina el tamaño de un archivo en C?

137

¿Cómo puedo calcular el tamaño de un archivo, en bytes?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}
andrewrk
fuente
Necesitará usar una función de biblioteca para recuperar los detalles de un archivo. Como C es completamente independiente de la plataforma, ¡deberá informarnos para qué plataforma / sistema operativo está desarrollando!
Chris Roberts
¿Por qué char* file, por qué no FILE* file? -1
Sr. Oscar el
-1 porque las funciones de archivo deben aceptar descriptores de archivo, no rutas de archivo
Sr. Oscar

Respuestas:

144

Basado en el código de 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; 
}

Cambios:

  • Hizo el argumento del nombre de archivo a const char.
  • Se corrigió la struct statdefinición, a la que le faltaba el nombre de la variable.
  • Devuelve -1por error en lugar de 0, lo que sería ambiguo para un archivo vacío. off_tes un tipo con signo, por lo que esto es posible.

Si desea fsize()imprimir un mensaje de error, puede usar esto:

#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;
}

En sistemas de 32 bits, debe compilar esto con la opción -D_FILE_OFFSET_BITS=64; de lo contrario off_t, solo tendrá valores de hasta 2 GB. Consulte la sección "Uso de LFS" de Soporte de archivos grandes en Linux para más detalles.

Ted Percival
fuente
19
Esto es específico de Linux / Unix, probablemente valga la pena señalarlo ya que la pregunta no especificaba un sistema operativo.
Drew Hall el
1
Probablemente podría cambiar el tipo de retorno a ssize_t y emitir el tamaño desde un off_t sin ningún problema. Parecería tener más sentido usar un ssize_t :-) (No debe confundirse con size_t que no está firmado y no se puede usar para indicar un error).
Ted Percival
1
Para un código más portátil, use fseek+ ftellsegún lo propuesto por Derek.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
9
Para un código más portátil, use fseek+ ftellsegún lo propuesto por Derek. No. El C estándar establece específicamente que fseek()a SEEK_ENDen un archivo binario es un comportamiento indefinido. 7.19.9.2 La fseekfunción ... Una secuencia binaria no necesita admitir significativamente fseekllamadas con un valor de origen deSEEK_END , y como se indica a continuación, que es de la nota 234 en la p. 267 de la C Standard vinculado, y que expresamente etiquetas fseeka SEEK_ENDen una corriente binaria como un comportamiento indefinido. .
Andrew Henle
74

No utilice int. Los archivos de más de 2 gigabytes de tamaño son comunes en la actualidad.

No utilice unsigned int. Los archivos de más de 4 gigabytes de tamaño son comunes como algo de tierra un poco menos común

IIRC, la biblioteca estándar, se define off_tcomo un entero de 64 bits sin signo, que es lo que todos deberían usar. Podemos redefinir que sean 128 bits en unos pocos años cuando comencemos a tener 16 archivos exabyte dando vueltas.

Si está en Windows, debe usar GetFileSizeEx ; en realidad usa un entero de 64 bits con signo, por lo que comenzarán a resolver problemas con 8 archivos exabyte. Tonto de Microsoft! :-)

Orion Edwards
fuente
1
He usado compiladores donde off_t es de 32 bits. De acuerdo, esto es en sistemas integrados donde los archivos de 4GB son menos comunes. De todos modos, POSIX también define off64_t y los métodos correspondientes para agregar a la confusión.
Aaron Campbell
Siempre me encantan las respuestas que asumen Windows y no hacen nada más que criticar la pregunta. ¿Podría agregar algo que cumpla con POSIX?
SS Anne
1
@ JL2210 la respuesta aceptada de Ted Percival muestra una solución compatible con posix, por lo que no tengo sentido repetir lo obvio. Yo (y otros 70) pensé que agregar la nota sobre Windows y no usar enteros de 32 bits con signo para representar tamaños de archivo era un valor agregado. Saludos
Orion Edwards
30

La solución de Matt debería funcionar, excepto que es C ++ en lugar de C, y la información inicial no debería ser necesaria.

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;
}

También arregló su abrazadera para usted. ;)

Actualización: esta no es realmente la mejor solución. Está limitado a archivos de 4GB en Windows y es probable que sea más lento que usar una llamada específica de la plataforma como GetFileSizeExo stat64.

Derek Park
fuente
Si deberías. Sin embargo, a menos que haya una razón realmente convincente para no escribir una plataforma específica, probablemente debería usar una llamada específica de la plataforma en lugar del patrón abierto / buscar-fin / decir / cerrar.
Derek Park
1
Lamento la respuesta tardía, pero tengo un problema importante aquí. Hace que la aplicación se bloquee al acceder a archivos restringidos (como protegidos por contraseña o archivos del sistema). ¿Hay alguna manera de pedirle al usuario una contraseña cuando sea necesario?
Justin
@Justin, probablemente deberías abrir una nueva pregunta específicamente sobre el problema con el que te encuentras y proporcionar detalles sobre la plataforma en la que te encuentras, cómo estás accediendo a los archivos y cuál es el comportamiento.
Derek Park el
1
Ambos C99 y C11 regresan long intde ftell(). (unsigned long)el lanzamiento no mejora el rango ya que está limitado por la función. ftell()devuelve -1 por error y eso se ofusca con el elenco. Sugerir fsize()devolver el mismo tipo que ftell().
chux - Restablecer Monica
Estoy de acuerdo. El elenco debía coincidir con el prototipo original en la pregunta. Sin embargo, no recuerdo por qué lo convertí en unsigned long en lugar de unsigned int.
Derek Park el
15

** No hagas esto ( ¿por qué? ):

Citando el documento estándar C99 que encontré en línea: "Establecer el indicador de posición del archivo al final del archivo, como con fseek(file, 0, SEEK_END), tiene un comportamiento indefinido para una secuencia binaria (debido a posibles caracteres nulos finales) o para cualquier secuencia con codificación dependiente del estado que seguramente no termina en el estado de cambio inicial. **

Cambie la definición a int para que se puedan transmitir mensajes de error, y luego use fseek()y ftell()para determinar el tamaño del archivo.

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
fuente
55
@mezhaka: Ese informe del CERT es simplemente incorrecto. fseekoy ftello(o fseeky ftellsi le pegan sin la primera y feliz con límites en el tamaño de los archivos se puede trabajar con) es la forma correcta para determinar la longitud de un archivo. statLas soluciones basadas en sistemas no funcionan en muchos "archivos" (como dispositivos de bloque) y no son portátiles para sistemas que no son POSIX-ish.
R .. GitHub DEJA DE AYUDAR AL HIELO
1
Esta es la única forma de obtener el tamaño del archivo en muchos sistemas que no son compatibles con posix (como mi mbed muy minimalista)
Earlz
9

POSIX

El estándar POSIX tiene su propio método para obtener el tamaño del archivo.
Incluya el sys/stat.hencabezado para usar la función.

Sinopsis

  • Obtenga estadísticas de archivos usando stat(3).
  • Obtenga la st_sizepropiedad.

Ejemplos

Nota : limita el tamaño a 4GB. Si no es un Fat32sistema de archivos, ¡use la versión 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 (estándar)

El ANSI C no proporciona directamente la forma de determinar la longitud del archivo.
Tendremos que usar nuestra mente. Por ahora, utilizaremos el enfoque de búsqueda.

Sinopsis

  • Busque el archivo hasta el final usando fseek(3).
  • Obtenga la posición actual usando ftell(3).

Ejemplo

#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);
}

Si el archivo es stdino una tubería. POSIX, ANSI C no funcionará.
Volverá 0si el archivo es una tubería o stdin.

Opinión : en su lugar, debe usar el estándar POSIX . Porque tiene soporte de 64 bits.


fuente
1
struct _stat64y __stat64()para _Windows.
Bob Stein
5

Y si está creando una aplicación de Windows, use la API GetFileSizeEx ya que la E / S del archivo CRT es desordenada, especialmente para determinar la longitud del archivo, debido a peculiaridades en las representaciones de archivos en diferentes sistemas;)


fuente
5

Si está bien con el uso de la 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
fuente
24
Ese no es el estándar C. Es parte del estándar POSIX, pero no es el estándar C.
Derek Park el
3

Una búsqueda rápida en Google encontró un método usando fseek y ftell y un hilo con esta pregunta con respuestas que no se puede hacer solo en C de otra manera.

Podría usar una biblioteca de portabilidad como NSPR (la biblioteca que alimenta Firefox) o verificar su implementación (bastante complicada).

Nickolay
fuente
1

Usé este conjunto de código para encontrar la longitud del archivo.

//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
fuente
1

Prueba esto --

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

Lo que esto hace es primero, buscar hasta el final del archivo; luego, informe dónde está el puntero del archivo. Por último (esto es opcional), se rebobina al principio del archivo. Tenga en cuenta quefp debería ser una secuencia binaria.

file_size contiene el número de bytes que contiene el archivo. Tenga en cuenta que dado que (según climits.h) el tipo largo sin signo está limitado a 4294967295 bytes (4 gigabytes) necesitará encontrar un tipo de variable diferente si es probable que maneje archivos más grandes que eso.

adrian
fuente
3
¿Cómo es esto diferente de la respuesta de Derek de hace 8 años?
PP
Ese es un comportamiento indefinido para una secuencia binaria, y para una secuencia de texto ftellno devuelve un valor representativo del número de bytes que se pueden leer del archivo.
Andrew Henle
0

Tengo una función que funciona bien solo con stdio.h. Me gusta mucho y funciona muy bien y es bastante conciso:

size_t fsize(FILE *File) {
    size_t FSZ;
    fseek(File, 0, 2);
    FSZ = ftell(File);
    rewind(File);
    return FSZ;
}
Señor oscar
fuente
0

Aquí hay una función simple y limpia que devuelve el tamaño del archivo.

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
fuente
1
¿No necesitas cerrar el archivo?
Jerry Jeremiah
No, no me gustan las funciones que esperan un camino. En cambio, haga que espere un puntero de archivo
Sr. Oscar el
-3

Puede abrir el archivo, ir a 0 desplazamiento relativo desde la parte inferior del archivo con

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

El valor devuelto por fseek es el tamaño del archivo.

No codifiqué en C durante mucho tiempo, pero creo que debería funcionar.

PabloG
fuente
12
No debería tener que definir algo como SEEKBOTTOM. #include <stdio.h> fseek (handle, 0, SEEK_END);
sigjuice
-4

Mirando la pregunta, ftellpuede obtener fácilmente el número de bytes.

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);
zishan
fuente
ftellespera un descriptor de archivo, no un nombre de archivo, como argumento.
Barmar
@Barmar, No ftellno espera un descriptor de archivo, espera un FILE*en su lugar. ¡Vea la página del manual primero!
El enfoque es completamente incorrecto, ¡es constante que ftellvolvería siempre 0!
Esta respuesta es completamente incorrecta, ya que para una, debe usarla fseek()primero para buscar el final del archivo y, además, ftell()espera una FILE *, ¡no una cadena! Estarías bien servido para desarrollar tu respuesta.
Sr. Oscar