116 votes

Quelle est la raison pour laquelle fread/fwrite prennent la taille et le nombre comme arguments ?

Nous avons eu une discussion ici au travail concernant le fait que fread() et fwrite() prennent une taille par élément et un compteur et renvoient le nombre d'éléments lus/écrits plutôt que de simplement prendre un tampon et une taille. La seule utilisation que nous avons pu trouver est si vous voulez lire/écrire un tableau de structures qui ne sont pas divisibles de manière égale par l'alignement de la plate-forme et qui ont donc été rembourrées, mais cela ne peut pas être si courant pour justifier ce choix dans la conception.

Depuis fread(3):

La fonction fread() lit nmemb éléments de données, chacun de taille bytes , du flux pointé par stream, les stockant à l'emplacement donné par ptr.

La fonction fwrite() écrit nmemb éléments de données, chacun de taille bytes du flux pointé par stream, les obtenant à partir de l'emplacement donné par ptr.

fread() et fwrite() renvoient le nombre d'éléments lus ou écrits avec succès (c'est-à-dire pas le nombre de caractères ). En cas d'erreur, ou si la fin de fichier est atteinte, la valeur de retour est un court compte d'éléments (ou zéro).

14 votes

Hey c'est une bonne question. j'ai toujours été curieux à ce sujet

2 votes

88voto

Peter Miehle Points 3753

La différence entre fread(buf, 1000, 1, stream) et fread(buf, 1, 1000, stream) est que, dans le premier cas, vous obtenez seulement un morceau de 1000 octets ou rien, si le fichier est plus petit et dans le deuxième cas, vous obtenez tout ce qui se trouve dans le fichier jusqu'à 1000 octets.

4 votes

Bien que cela soit vrai, cela ne raconte qu'une petite partie de l'histoire. Il serait préférable de contraster quelque chose en lisant, disons, un tableau de valeurs int, ou un tableau de structures.

4 votes

Cela ferait une excellente réponse si la justification était complétée.

26voto

Powerlord Points 43989

Cela est basé sur la manière dont fread est implémenté.

La Spécification unique UNIX indique

Pour chaque objet, des appels de taille doivent être faits à la fonction fgetc() et les résultats stockés, dans l'ordre de lecture, dans un tableau de unsigned char exactement superposé à l'objet.

fgetc a également cette note :

Étant donné que fgetc() fonctionne sur des octets, la lecture d'un caractère composé de plusieurs octets (ou "un caractère multi-octet") peut nécessiter plusieurs appels à fgetc().

De toute évidence, cela précède les encodages de caractères variables sophistiqués comme l'UTF-8.

Le SUS note que cela est en fait tiré des documents ISO C.

19voto

nos Points 102226

Ce ne sont que des spéculations, mais à l'époque (certains existent toujours), de nombreux systèmes de fichiers n'étaient pas de simples flux d'octets sur un disque dur.

De nombreux systèmes de fichiers étaient basés sur des enregistrements, donc pour satisfaire de tels systèmes de fichiers de manière efficace, vous devrez spécifier le nombre d'éléments ("enregistrements"), permettant à fwrite/fread d'opérer sur le stockage comme des enregistrements et non seulement comme des flux d'octets.

2 votes

Je suis content que quelqu'un ait soulevé cette question. J'ai beaucoup travaillé avec les spécifications du système de fichiers et du FTP et les enregistrements/pages et autres concepts de blocage sont très fermement pris en charge, même si personne n'utilise plus ces parties des spécifications.

10voto

Michael Burr Points 181287

Ici, laissez-moi réparer ces fonctions :

size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
    return fread( ptr, 1, size, stream);
}

size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
    return fwrite( ptr, 1, size, stream);
}

Quant à une justification des paramètres de fread()/fwrite(), j'ai perdu ma copie de K&R il y a longtemps donc je peux seulement supposer. Je pense qu'une réponse plausible est que Kernighan et Ritchie ont peut-être simplement pensé que la réalisation d'E/S binaire se ferait le plus naturellement sur des tableaux d'objets. De plus, ils ont peut-être pensé que l'E/S par bloc serait plus rapide/plus facile à mettre en œuvre ou autre sur certaines architectures.

Même si la norme C spécifie que fread() et fwrite() doivent être implémentés en termes de fgetc() et fputc(), souvenez-vous que la norme est apparue longtemps après que C ait été défini par K&R et que les choses spécifiées dans la norme pourraient ne pas avoir été dans les idées initiales des concepteurs. Il est même possible que les choses dites dans "The C Programming Language" de K&R ne soient pas les mêmes que lorsque le langage était en cours de conception.

Enfin, voici ce que P.J. Plauger a à dire sur fread() dans "The Standard C Library" :

Si le deuxième argument size est supérieur à un, vous ne pouvez pas déterminer si la fonction a également lu jusqu'à size - 1 caractères supplémentaires au-delà de ce qu'elle rapporte. En règle générale, il est préférable d'appeler la fonction comme fread(buf, 1, size * n, stream); au lieu de fread(buf, size, n, stream);

Fondamentalement, il dit que l'interface de fread() est cassée. Pour fwrite(), il note que "Les erreurs d'écriture sont généralement rares, donc ce n'est pas un inconvénient majeur" - une déclaration avec laquelle je ne serais pas d'accord.

23 votes

En fait, j'aime souvent faire de l'autre manière: fread(buf, size*n, 1, stream); Si les lectures incomplètes sont une condition d'erreur, il est plus simple de faire en sorte que fread retourne simplement 0 ou 1 plutôt que le nombre d'octets lus. Ensuite, vous pouvez faire des choses comme if (!fread(...)) au lieu de devoir comparer le résultat au nombre d'octets demandés (ce qui nécessite du code C supplémentaire et du code machine supplémentaire).

1 votes

@R.. Assurez-vous simplement de vérifier que size * count! = 0 en plus de !fread (...). Si size * count == 0, vous obtiendrez une valeur de retour nulle lors d'une lecture réussie (de zéro octet), feof() et ferror() ne seront pas définis, et errno sera quelque chose d'absurde comme ENOENT, ou pire, quelque chose de trompeur (et éventuellement critique) comme EAGAIN - très déroutant, notamment puisque pratiquement aucune documentation ne met en garde contre ce piège.

3voto

dolch Points 294

Il est probable que cela remonte à la manière dont l'E/S de fichiers était implémentée. (à l'époque) Il était peut-être plus rapide d'écrire / lire dans des fichiers par blocs que d'écrire tout en une seule fois.

0 votes

Pas vraiment. La spécification C pour fwrite note qu'elle effectue des appels répétés à fputc: opengroup.org/onlinepubs/009695399/functions/fwrite.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X