Comment déterminez-vous la taille d'un fichier en C?

137

Comment puis-je déterminer la taille d'un fichier, en octets?

#include <stdio.h>

unsigned int fsize(char* file){
  //what goes here?
}
andrewrk
la source
Vous allez devoir utiliser une fonction de bibliothèque pour récupérer les détails d'un fichier. Comme C est complètement indépendant de la plate-forme, vous devrez nous indiquer pour quelle plate-forme / système d'exploitation vous développez!
Chris Roberts du
Pourquoi char* file, pourquoi pas FILE* file? -1
Mr Oscar le
-1 parce que les fonctions de fichiers doivent accepter les descripteurs de fichiers et non les chemins de fichiers
M. Oscar

Réponses:

144

Basé sur le code 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; 
}

Changements:

  • Faites de l'argument de nom de fichier un const char.
  • Correction de la struct statdéfinition, qui manquait le nom de la variable.
  • Renvoie -1sur erreur au lieu de 0, ce qui serait ambigu pour un fichier vide. off_test un type signé, c'est donc possible.

Si vous souhaitez fsize()imprimer un message d'erreur, vous pouvez utiliser ceci:

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

Sur les systèmes 32 bits, vous devez le compiler avec l'option -D_FILE_OFFSET_BITS=64, sinon off_tne contiendra que des valeurs jusqu'à 2 Go. Consultez la section «Utilisation de LFS» de la prise en charge des fichiers volumineux sous Linux pour plus de détails.

Ted Percival
la source
19
Ceci est spécifique à Linux / Unix - il vaut probablement la peine de le souligner puisque la question ne spécifiait pas de système d'exploitation.
Drew Hall
1
Vous pourriez probablement changer le type de retour en ssize_t et convertir la taille d'un off_t sans aucun problème. Il semble plus judicieux d'utiliser un ssize_t :-) (à ne pas confondre avec size_t qui n'est pas signé et ne peut pas être utilisé pour indiquer une erreur.)
Ted Percival
1
Pour un code plus portable, utilisez fseek+ ftellcomme proposé par Derek.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
9
Pour un code plus portable, utilisez fseek+ ftellcomme proposé par Derek. Non . La norme C précise que fseek()pour SEEK_ENDun fichier binaire est un comportement non défini. 7.19.9.2 La fseekfonction ... Un flux binaire n'a pas besoin de prendre en charge de manière significative les fseekappels avec une valeur SEEK_ENDwhence de , et comme indiqué ci-dessous, qui provient de la note 234 à la p. 267 de la norme lié par C, et qui en particulier des étiquettes fseekpour SEEK_ENDun flux binaire en tant que comportement non défini. .
Andrew Henle
74

N'utilisez pas int. Les fichiers de plus de 2 gigaoctets sont courants comme de la saleté de nos jours

N'utilisez pas unsigned int. Les fichiers de plus de 4 gigaoctets sont courants en tant que saleté légèrement moins courante

IIRC, la bibliothèque standard définit off_tcomme un entier 64 bits non signé, ce que tout le monde devrait utiliser. Nous pouvons redéfinir cela à 128 bits dans quelques années lorsque nous commencerons à avoir 16 fichiers exaoctets qui traînent.

Si vous êtes sous Windows, vous devez utiliser GetFileSizeEx - il utilise en fait un entier 64 bits signé, de sorte qu'ils commenceront à rencontrer des problèmes avec 8 fichiers exaoctets. Microsoft stupide! :-)

Orion Edwards
la source
1
J'ai utilisé des compilateurs où off_t vaut 32 bits. Certes, c'est sur les systèmes embarqués où les fichiers de 4 Go sont moins courants. Quoi qu'il en soit, POSIX définit également off64_t et les méthodes correspondantes pour ajouter à la confusion.
Aaron Campbell
J'aime toujours les réponses qui supposent Windows et ne font rien d'autre que critiquer la question. Pourriez-vous s'il vous plaît ajouter quelque chose qui est compatible POSIX?
SS Anne
1
@ JL2210 la réponse acceptée de Ted Percival montre une solution conforme posix, donc je ne vois aucun sens à répéter l'évidence. Je (et 70 autres) pensais que l'ajout de la note sur les fenêtres et de ne pas utiliser d'entiers 32 bits signés pour représenter la taille des fichiers était une valeur ajoutée en plus de cela. Cheers
Orion Edwards
30

La solution de Matt devrait fonctionner, sauf que c'est C ++ au lieu de C, et le tell initial ne devrait pas être nécessaire.

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

Correction de votre attelle pour vous aussi. ;)

Mise à jour: ce n'est pas vraiment la meilleure solution. Il est limité à 4 Go de fichiers sur Windows et il est probablement plus lent que d'utiliser simplement un appel spécifique à la plate-forme comme GetFileSizeExou stat64.

Parc Derek
la source
Oui tu devrais. Cependant, à moins qu'il n'y ait une raison vraiment convaincante de ne pas écrire spécifique à la plate-forme, vous devriez probablement utiliser un appel spécifique à la plate-forme plutôt que le modèle open / seek-end / tell / close.
Derek Park
1
Désolé pour la réponse tardive, mais j'ai un problème majeur ici. Cela provoque le blocage de l'application lors de l'accès à des fichiers restreints (comme des fichiers protégés par mot de passe ou système). Existe-t-il un moyen de demander à l'utilisateur un mot de passe en cas de besoin?
Justin
@Justin, vous devriez probablement ouvrir une nouvelle question spécifiquement sur le problème que vous rencontrez, et fournir des détails sur la plate-forme sur laquelle vous vous trouvez, comment vous accédez aux fichiers et quel est le comportement.
Derek Park
1
Les deux C99 et C11 reviennent long intde ftell(). (unsigned long)la coulée n'améliore pas la portée car déjà limitée par la fonction. ftell()retourne -1 en cas d'erreur et qui est obscurcie avec la distribution. Suggérer de fsize()renvoyer le même type que ftell().
chux
Je suis d'accord. Le casting devait correspondre au prototype original dans la question. Je ne me souviens pas pourquoi je l'ai transformé en unsigned long au lieu de unsigned int, cependant.
Derek Park le
15

** Ne faites pas ça ( pourquoi? ):

Citant le document standard C99 que j'ai trouvé en ligne: «La définition de l'indicateur de position du fichier sur la fin du fichier, comme avec fseek(file, 0, SEEK_END), a un comportement indéfini pour un flux binaire (en raison de possibles caractères nulles de fin) ou pour tout flux avec un codage dépendant de l'état qui ne se termine pas assurément dans l'état de décalage initial. **

Modifiez la définition en int afin que les messages d'erreur puissent être transmis, puis utilisez fseek()et ftell()pour déterminer la taille du fichier.

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
la source
5
@mezhaka: Ce rapport CERT est tout simplement faux. fseekoet ftello(ou fseeket ftellsi vous êtes bloqué sans l'ancien et satisfait des limites de taille de fichier avec lesquelles vous pouvez travailler) sont la bonne façon de déterminer la longueur d'un fichier. statLes solutions basées sur les fichiers ne fonctionnent pas sur de nombreux "fichiers" (tels que les périphériques de bloc) et ne sont pas portables sur des systèmes non POSIX.
R .. GitHub STOP AIDER ICE
1
C'est le seul moyen d'obtenir la taille du fichier sur de nombreux systèmes non compatibles posix (comme mon mbed très minimaliste)
Earlz
9

POSIX

Le standard POSIX a sa propre méthode pour obtenir la taille du fichier.
Incluez l'en- sys/stat.htête pour utiliser la fonction.

Synopsis

  • Obtenez des statistiques de fichiers à l'aide de stat(3).
  • Obtenez la st_sizepropriété.

Exemples

Remarque : il limite la taille à 4GB. Si ce n'est pas le Fat32système de fichiers, utilisez la version 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 (standard)

L' ANSI C ne fournit pas directement le moyen de déterminer la longueur du fichier.
Nous devrons utiliser notre esprit. Pour l'instant, nous allons utiliser l'approche de recherche!

Synopsis

  • Recherchez le fichier jusqu'à la fin en utilisant fseek(3).
  • Obtenez la position actuelle en utilisant ftell(3).

Exemple

#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 le fichier est stdinou un tube. POSIX, ANSI C ne fonctionnera pas.
Il retournera 0si le fichier est un tube ou stdin.

Opinion : Vous devriez utiliser le standard POSIX à la place. Parce qu'il prend en charge 64 bits.


la source
1
struct _stat64et __stat64()pour _Windows.
Bob Stein
5

Et si vous créez une application Windows, utilisez l' API GetFileSizeEx car les E / S de fichier CRT sont compliquées , en particulier pour déterminer la longueur de fichier, en raison des particularités des représentations de fichiers sur différents systèmes;)


la source
5

Si vous êtes d'accord avec l'utilisation de la bibliothèque 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
la source
24
Ce n'est pas le standard C. Cela fait partie du standard POSIX, mais pas du standard C.
Derek Park
3

Une recherche rapide dans Google a trouvé une méthode utilisant fseek et ftell et un fil avec cette question avec des réponses que cela ne peut pas être fait en C d'une autre manière.

Vous pouvez utiliser une bibliothèque de portabilité comme NSPR (la bibliothèque qui alimente Firefox) ou vérifier son implémentation (plutôt poilue).

Nickolay
la source
1

J'ai utilisé cet ensemble de code pour trouver la longueur du fichier.

//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
la source
1

Essaye ça --

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

Ce que cela fait, c'est d'abord chercher la fin du fichier; puis, indiquez où se trouve le pointeur de fichier. Enfin (c'est facultatif), il revient au début du fichier. Notez que cela fpdevrait être un flux binaire.

file_size contient le nombre d'octets que contient le fichier. Notez que puisque (selon climits.h) le type long non signé est limité à 4294967295 octets (4 gigaoctets), vous devrez trouver un type de variable différent si vous êtes susceptible de traiter des fichiers plus volumineux que cela.

Adrian
la source
3
En quoi est-ce différent de la réponse de Derek d'il y a 8 ans?
PP
C'est un comportement indéfini pour un flux binaire et pour un flux de texte, ftellil ne renvoie pas de valeur représentative du nombre d'octets pouvant être lus à partir du fichier.
Andrew Henle
0

J'ai une fonction qui fonctionne bien uniquement avec stdio.h. Je l'aime beaucoup et cela fonctionne très bien et est assez concis:

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

Voici une fonction simple et propre qui renvoie la taille du fichier.

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
la source
1
Vous n'avez pas besoin de fermer le fichier?
Jerry Jeremiah
Non, je n'aime pas les fonctions qui attendent un chemin. Au lieu de cela, veuillez faire en sorte que vous attendiez un pointeur de fichier
Mr Oscar
-3

Vous pouvez ouvrir le fichier, aller à 0 offset relatif à partir du bas du fichier avec

#define SEEKBOTTOM   2

fseek(handle, 0, SEEKBOTTOM)  

la valeur renvoyée par fseek est la taille du fichier.

Je n'ai pas codé en C pendant longtemps, mais je pense que cela devrait fonctionner.

PabloG
la source
12
Vous ne devriez pas avoir à définir quelque chose comme SEEKBOTTOM. #include <stdio.h> fseek (handle, 0, SEEK_END);
sigjuice
-4

En regardant la question, ftellpeut facilement obtenir le nombre d'octets.

  long size = ftell(FILENAME);
  printf("total size is %ld bytes",size);
Zishan
la source
ftellattend un descripteur de fichier, pas un nom de fichier, comme argument.
Barmar le
@Barmar, No ftelln'attend pas de descripteur de fichier, il en attend un à la FILE*place. Consultez d'abord la page de manuel!
L'approche est complètement fausse, c'est une constante qui ftellreviendrait à 0chaque fois!
Cette réponse est complètement fausse, car pour une, vous devez d' fseek()abord utiliser pour rechercher la fin du fichier, et aussi, ftell()attend une FILE *, pas une chaîne! Vous seriez bien servi pour étoffer votre réponse.
Mr Oscar