1
votes

Puis-je écrire dans des fichiers sans affecter de tampon

Existe-t-il un moyen d'utiliser fwrite ou une autre fonction d'écriture de fichier sans affecter le tampon à une variable?

Étant donné que mes données sont de simples constantes uint8_t code > y a-t-il un meilleur moyen que

if (cond1)      { fputint(CONST_1, out_file); }
else if (cond2) { fputint(CONST_2, out_file); }
// etc

EDIT: Pour les caractères uniques, il existe fputc (n'est pas venu dans mes recherches parce que je cherchais pour écrire). Existe-t-il un moyen de le faire pour les données multi-octets comme les ints?

Je cherche des moyens d'éviter d'avoir ce type de code

const uint16_t val1 = CONST_1;
const uint16_t val2 = CONST_2;
const uint16_t val3 = CONST_3;
const uint16_t val4 = CONST_4;
const uint16_t val5 = CONST_5;

if (cond1)      { fwrite(&val1, sizeof(val1), 1, out_file); }
else if (cond2) { fwrite(&val1, sizeof(val1), 1, out_file); }
// etc

Au lieu de simplement

const uint8_t a = 'A';
const uint8_t b = 'B';

if (cond)
   fwrite(&a, 1, 1, out_file);
else
   fwrite(&b, 1, 1, out_file);


15 commentaires

fwrite () est le mécanisme principal si vous utilisez les E / S de flux de fichiers ( FILE * ). Si vous descendez dans les E / S du descripteur de fichier Unix ( int ), il y a plus de choix quant à la façon d'écrire des données, mais dans le contexte, vous finiriez par faire quelque chose comme write ( fd, & a, 1) (où fd est le descripteur de fichier). Donc, il n'y a pas beaucoup de différence pratique.


@JonathanLeffler Je pense que nous pouvons dire que write [pour un seul octet] fait un appel système [qui introduit une surcharge importante] pour chaque octet écrit. En faisant fwrite , les données sont mises en mémoire tampon en interne par le flux, et les E / S de flux feront beaucoup moins d'appels système (c'est-à-dire d'appels write ). Ainsi, fwrite sera plus efficace. Si nous avons un tampon plus grand (par exemple uint8_t buf [1024]; ) et faisons (par exemple) write (fd, buf, sizeof (buf)) la surcharge est réduite et peut être plus rapide


Il n'y a donc aucun moyen d'écrire dans un fichier sans avoir une sorte de variable intermédiaire? quelque chose comme fputint (int, FILE *) analogue à fputc (char, FILE *)


@CraigEstey: mais la question demande "sans tampon" dans le titre…: D… Je suis d'accord, mais alors vous allez à l'encontre du titre de la question.


Quelles que soient les primitives de flux utilisées (par exemple fputc ) ou toute autre méthode, elles utiliseront toutes write (2) éventuellement [en utilisant un tampon , sous la capuche]. Quant à votre fputint , vous pouvez en coder un vous-même, ou utiliser int foo = 23; fwrite (& foo, sizeof (foo), 1, out_file); Le fputint fera à peu près ce que l'exemple que je viens de donner.


@JonathanLeffler Accordé. Mais, la comparaison de vitesse vaut [encore plus] pour l'exemple à un octet.


@CraigEstey: la vitesse n'est pas un critère mentionné dans la question. Les appels write () à un seul caractère ne sont pas particulièrement efficaces (mais ils ne sont pas non plus si inefficaces qu'ils sont inutilisables - généralement, sous réserve d'aucune option idiote lors de l'appel open () , etc), mais écrire A ou B dans un fichier n'est probablement pas vraiment représentatif de la charge de travail. Le fichier ne sera pas très excitant à lire - une sorte de mouton: BAA BAA BAABAABAAAABBBBAAAA.


Similaire à fputc , il existe également des fputs si vous avez déjà une chaîne à écrire.


@Craig Je pense qu'écrire ma propre fonction est probablement la meilleure alternative, je veux juste éviter d'avoir une liste de variables en haut comme const int a = CONST_1; const int b = CONST_2; const int c = CONST_3; etc.


Oui, écrire le vôtre serait [probablement] le meilleur choix - personnalisé adapté à vos besoins exacts. En partie parce qu'il n'y a pas quelque chose de standard qui correspond exactement à fputint . Vous êtes libre de créer autant de fonctions wrapper que vous le souhaitez. Pour maintenir l'abstrait, avec typedef int age_t; putage (age_t age, FILE * fi) {fputint (age); } Si vous changez [jamais] age_t en (par exemple) short , vous pouvez faire: putage (age_t age, FILE * fi) {putint16 (âge, fi); } en un seul endroit et tout le reste du code reste inchangé.


Je pense que vous posez la mauvaise question. Qu'entendez-vous par «sans tampon»? Vous pouvez écrire n'importe quel objet dans un flux avec fwrite (& object, sizeof object, 1, out_file); . Vous n'avez pas besoin de mettre l'objet dans un tampon avant de faire cela, et le passer à une routine fput par valeur n'accomplit rien de différent. Et qu'est-ce que if (cond) a à voir avec l'utilisation ou non d'un tampon? Quel est le problème réel que vous devez résoudre?


@Eric voyez mon commentaire précédent, ce que je voulais dire était de savoir si je pouvais écrire dans un fichier sans attribuer de tampon. J'ai un enum et la valeur à écrire dépend de la condition.


@Blake: Les petits objets que vous écrivez sont triviaux. Faites simplement const uint8_t x = cond? un B; fwrite (& x, sizeof x, 1, out_file); et en finir avec. Vous pouvez même écrire ceci dans une expression comme fwrite ((uint8_t []) {cond? A: b}, sizeof (uint8_t), 1, out_file); .


Les conditions que j'ai sont beaucoup moins triviales, je ne veux vraiment pas la réécrire avec l'opérateur conditionnel


@Blake: pensez à modifier votre question à nouveau pour en expliquer davantage, donner plus de contexte et de motivation. Évitez de commenter votre propre question


4 Réponses :


1
votes

Puis-je écrire dans des fichiers sans tampon (?)

Étant donné que mes données sont de simples constantes uint8_t , y a-t-il un meilleur moyen que ...?

Considérez les 2 écritures ci-dessous. Dans les deux cas, a est le tampon. @Eric Postpischil

XXX

Il est vraiment étonnant de voir à quel point les compilateurs peuvent être bons de nos jours. Il est tout à fait possible que les deux lignes de code émettent le même code. Codez pour plus de clarté et laissez le compilateur gérer ce qui est vraiment un problème de micro-optimisation.


Y a-t-il un moyen de le faire pour les données multi-octets comme les int s?

int i = rand();
fwrite(&i, sizeof i, 1, out_file);

0 commentaires

1
votes

Vous devez faire la différence entre les valeurs qui existent pendant l'exécution et celles qui n'existent que pendant la compilation.

Toute valeur compilée dans la section de données du programme existe à une adresse pendant l'exécution, et pour les valeurs multi-octets passant par la référence est la norme (et le seul choix pratique pour les valeurs de 64 bits et plus). Cela signifie également que les valeurs qui n'existent que pendant la compilation ne peuvent pas être adressées directement, il s'agit principalement de macros (comme les énumérations). Les énumérations sont généralement utilisées de manière statique pendant l'exécution, ce qui signifie qu'elles pourraient ne jamais quitter la section du programme et devenir adressables.

Cependant, vous êtes autorisé à réutiliser des variables pour vos constantes, voici deux exemples utilisant raw posix / linux write:

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>

enum {
    FIRST = 10000,
    SECOND,
    THIRD
};

//This can (and probably will automatically) be inlined
void write_constant(uint16_t val, int fd)
{
    write(fd, &val, sizeof(val));
}

int main (void)
{
    int fd;
    uint16_t out;
    fd = open("/tmp/testfile", O_CREAT | O_WRONLY, 0640);

    //First example, reusing variable and assigning compile time constants:
    out = FIRST;  write(fd, &out, sizeof(out));
    out = SECOND; write(fd, &out, sizeof(out));
    out = THIRD;  write(fd, &out, sizeof(out));

    //Second example, wrapping the write in a function
    //This wraps the value inside an uint16_t value on either stack or register:
    write_constant(FIRST,fd);
    write_constant(SECOND,fd);
    write_constant(THIRD,fd);

    close(fd);
    return 0;
}

Une chose à noter lors de l'écriture de plusieurs entiers directement sans formateur, est que l'endianness du programme est préservée dans le fichier, et ils ne sont généralement pas humains lisible.


0 commentaires

1
votes

Nous pouvons utiliser des littéraux composés .

if (cond)
   fwrite((uint8_t[]){'A'}, 1, 1, out_file);
else
   fwrite((uint8_t[]){'B'}, 1, 1, out_file);


0 commentaires

0
votes

Existe-t-il un moyen d'utiliser fwrite ou une autre fonction d'écriture de fichier sans affecter le tampon à une variable?

Bien sûr, vous pouvez écrire une chaîne littérale, par exemple

fwrite("\2\3\0\17", 4, 1, file);

ou

fwrite("\1", 1, 1, file);

Dans l'exemple suivant, la chaîne contient un octet NUL en troisième position.

Rappelez-vous cependant que les formats binaires sont en pratique liés à une architecture (il ne sera pas facile de lire sur certains ARM un binaire fichier écrit sur x86, par exemple parce que ces processeurs ont différentes endianess ).

Bien sûr, vous devriez vous soucier de endianess si cette chaîne littérale est censée encoder un entier en plusieurs octets. Ensuite, utiliser des littéraux composés, comme répondu par Kamil Cuk , devrait être plus facile.

En pratique, vous peut préférer les techniques de sérialisation (car les formats binaires tels que XDR sont indépendants de l'architecture) et dans certains cas, vous voulez textuels tels que JSON , YAML , etc .... (car ils sont plus faciles à déboguer). Vous trouverez des bibliothèques pour vous aider.


0 commentaires