Existe-t-il une raison ordinaire d’utiliser open() au lieu de fopen() ?
Question
Je fais un petit projet en C après une longue période d'absence.Ceux-ci incluent une certaine gestion de fichiers.J'ai remarqué dans diverses documentations qu'il existe des fonctions qui renvoient FILE *
handles et autres qui renvoient des descripteurs (petits entiers).Les deux ensembles de fonctions offrent les mêmes services de base dont j’ai besoin, donc peu importe que j’utilise.
Mais je suis curieux de connaître la sagesse de la collection :est-il préférable d'utiliser fopen()
et amis, ou open()
et amis?
Modifier Puisque quelqu'un a mentionné les périphériques tamponnés ou non tamponnés et l'accès, je dois ajouter qu'une partie de ce petit projet consistera à écrire un pilote de système de fichiers en espace utilisateur sous FUSE.Ainsi, l'accès au niveau des fichiers pourrait tout aussi bien se faire sur un appareil (par ex.un CDROM ou un lecteur SCSI) comme sur un "fichier" (c'est-à-direune image).
La solution
L'objection selon laquelle "fopen" est portable et "ouvert" n'est pas est faux.
fopen fait partie de libc, ouvert est un appel système POSIX.
Chacun d'eux est aussi portable que l'endroit où ils viennent.
i / o à fopen'ed fichiers est (vous devez supposer qu'il peut être, et pour des raisons pratiques, il est) amorti par libc, descripteurs de fichiers () 'ed ne sont pas stockées par libc (ils peuvent bien être, et généralement sont mises en tampon dans le système de fichiers -. mais pas tout ce que vous ouvrez () est un fichier sur un système de fichiers
Quel est le point de fopen'ing, par exemple, un nœud de périphérique / dev / sg0, par exemple, ou / dev / tty0 ... Qu'est-ce que tu vas faire? Vous allez faire un ioctl sur un FILE *? Bonne chance.
Peut-être que vous voulez ouvrir avec des drapeaux comme O_DIRECT - n'a pas de sens avec fopen ()
.Autres conseils
Il est préférable d'utiliser open () si vous en tenir à des systèmes Unix et vous pourriez aimer:
- Avoir un contrôle plus fin sur les bits d'autorisation unix sur la création de fichiers.
- Utilisez les fonctions de bas niveau, tels que lecture / écriture / mmap par opposition aux fonctions C flux mis en mémoire tampon E / S.
- descripteur de fichier Utilisation (fd) sur la base de planification IO (sondage, sélectionnez, etc.) Vous pouvez bien sûr obtenir un fd d'un fichier * en utilisant fileno (), mais il faut veiller à ne pas mélanger les fonctions de flux à base de fichiers * avec fd fonctions basées.
- Ouvrez un dispositif spécial (pas un fichier régulier)
Il est préférable d'utiliser fopen / fread / fwrite pour une portabilité maximale, car ceux-ci sont des fonctions standard C, les fonctions que je l'ai mentionné ci-dessus ne sont pas.
œuvres fopen à un niveau supérieur ouvert .... fopen vous renvoie un pointeur sur FILE flux qui est similaire à l'abstraction de flux que vous lisez en C ++
open retourne vous un descripteur de fichier pour le fichier ouvert ... Il ne vous fournit pas une abstraction de flux et vous êtes responsable de la gestion des bits et octets vous-même ... C'est à un niveau inférieur par rapport à fopen
flux STDIO sont tamponnées, alors que les descripteurs de fichiers () ne sont pas. Dépend de ce dont vous avez besoin. Vous pouvez également créer un de l'autre:
int fileno (FILE * stream) renvoie le descripteur de fichier pour un FILE *, FILE * fdopen (int fd, le mode const char *) crée un fichier * à partir d'un descripteur de fichier.
Soyez prudent lorsque vous entremêlement tamponnés et non tamponnés IO, puisque vous perdrez ce qui est dans votre mémoire tampon lorsque vous ne vident pas avec fflush ().
Oui. Lorsque vous avez besoin d'une poignée de bas niveau.
Sur les systèmes d'exploitation UNIX, vous pouvez échanger en général poignées de fichiers et prises.
En outre, les poignées de bas niveau pour faire une meilleure compatibilité ABI que les pointeurs de fichiers.
En général, vous devriez favoriser l'utilisation de la bibliothèque standard (fopen). Cependant, il y a des occasions où vous aurez besoin d'utiliser directement ouvert.
Un exemple qui vient à l'esprit est de travailler autour d'un bogue dans une ancienne version de solaris qui ont fait échouer après fopen 256 dossiers ont été ouverts. En effet, ils ont utilisé un erroniously unsigned char pour le champ fd dans leur mise en œuvre struct FILE au lieu d'un int. Mais ce fut un cas très spécifique.
lire() & écrire() utilisez des E/S sans tampon.(fd:descripteur de fichier entier)
fread() & fécrire() utilisez des E/S tamponnées.(DÉPOSER* pointeur de structure)
Données binaires écrites dans un tube avec écrire() Peut-être pas être capable de lire des données binaires avec fread(), en raison des alignements d'octets, des tailles variables, etc.C'est une connerie.
La plupart des codes de pilotes de périphériques de bas niveau utilisent des appels d'E/S sans tampon.
La plupart des E/S au niveau de l'application utilisent une mémoire tampon.
Utilisation du DÉPOSER* Et ses fonctions associées sont ok sur une base de machine par machine:Mais la portabilité est perdue dans d'autres architectures dans la lecture et l'écriture de données binaires.FWrite () est des E / S tamponnées et peut conduire à des résultats peu fiables s'ils sont écrits pour une architecture 64 bits et exécuter sur un 32 bits;ou (Windows/Linux).La plupart des systèmes d'exploitation ont des macros de compatibilité dans leur propre code pour éviter cela.
Pour la portabilité des E/S binaires de bas niveau lire() et écrire() garantie Le même binaire lit et écrit lorsqu’il est compilé sur des architectures différentes.L’essentiel est de choisir une façon ou une autre et d’être cohérent à ce sujet. dans l’ensemble de la suite binaire.
<stdio.h> // mostly FILE* some fd input/output parameters for compatibility
// gives you a lot of helper functions -->
List of Functions
Function Description
───────────────────────────────────────────────────────────────────
clearerr check and reset stream status
fclose close a stream
fdopen stream open functions //( fd argument, returns FILE*) feof check and reset stream status
ferror check and reset stream status
fflush flush a stream
fgetc get next character or word from input stream
fgetpos reposition a stream
fgets get a line from a stream
fileno get file descriptor // (FILE* argument, returns fd)
fopen stream open functions
fprintf formatted output conversion
fpurge flush a stream
fputc output a character or word to a stream
fputs output a line to a stream
fread binary stream input/output
freopen stream open functions
fscanf input format conversion
fseek reposition a stream
fsetpos reposition a stream
ftell reposition a stream
fwrite binary stream input/output
getc get next character or word from input stream
getchar get next character or word from input stream
gets get a line from a stream
getw get next character or word from input stream
mktemp make temporary filename (unique)
perror system error messages
printf formatted output conversion
putc output a character or word to a stream
putchar output a character or word to a stream
puts output a line to a stream
putw output a character or word to a stream
remove remove directory entry
rewind reposition a stream
scanf input format conversion
setbuf stream buffering operations
setbuffer stream buffering operations
setlinebuf stream buffering operations
setvbuf stream buffering operations
sprintf formatted output conversion
sscanf input format conversion
strerror system error messages
sys_errlist system error messages
sys_nerr system error messages
tempnam temporary file routines
tmpfile temporary file routines
tmpnam temporary file routines
ungetc un-get character from input stream
vfprintf formatted output conversion
vfscanf input format conversion
vprintf formatted output conversion
vscanf input format conversion
vsprintf formatted output conversion
vsscanf input format conversion
Donc, pour une utilisation de base, j'utiliserais personnellement ce qui précède sans trop mélanger les idiomes.
Par contre,
<unistd.h> write()
lseek()
close()
pipe()
<sys/types.h>
<sys/stat.h>
<fcntl.h> open()
creat()
fcntl()
all use file descriptors.
Ceux-ci fournissent un contrôle précis sur la lecture et l’écriture des octets (recommandé pour les appareils spéciaux et les FIFO (tuyaux) ).
Encore une fois, utilisez ce dont vous avez besoin, mais restez cohérent dans vos idiomes et vos interfaces.Si la majeure partie de votre base de code utilise un mode, utilisez-le aussi, à moins qu’il n’y en ait une vraie raison de ne pas le faire.Les deux ensembles de fonctions de la bibliothèque d’E/S sont extrêmement fiables et utilisé des millions de fois par jour.
note-- Si vous interfaces des E/S C avec un autre langage, (Perl, Python, Java, C#, Lua...) vérifier ce que les développeurs de ces langages recommander avant d’écrire votre code C et vous épargner des ennuis.
fopen et ses cousines sont mises en mémoire tampon. ouvrir, lire et écrire ne sont pas en mémoire tampon. Votre application peut ou ne peut pas prendre soin.
fprintf et scanf ont une API plus riche qui vous permet de lire et d'écrire des fichiers texte au format. lire et écrire utiliser des tableaux d'octets fondamentaux. Les conversions et la mise en forme doivent être fabriqués à la main.
La différence entre les descripteurs de fichiers et (FILE *) est vraiment sans conséquence.
Randy