Gibt es einen ordentlichen Grund offen zu verwenden () anstelle von fopen ()?
Frage
Ich mache ein kleines Projekt in C nach einer recht lange Zeit weg von ihr. Dies geschieht, um eine Datei-Handling enthalten. Ich habe bemerkt, in verschiedener Dokumentation, dass es Funktionen, die FILE *
Griffe und andere zurück, die (kleine ganze Zahl) Deskriptoren zurück. Beide Sätze von Funktionen die gleichen grundlegenden Dienste anbieten muss ich es so wirklich keine Rolle ich.
Aber ich bin neugierig auf die Sammlung Weisheit: ist es besser, fopen()
und Freunde zu verwenden, oder open()
und Freunde
Bearbeiten Da jemand vs ungepufferte und den Zugriff auf Geräte gepuffert erwähnt, sollte ich hinzufügen, dass ein Teil dieses kleine Projekt wird ein Userspace-Dateisystem-Treiber unter FUSE schreiben. So ist der Zugriff auf Dateiebene so einfach auf einem Gerät (zum Beispiel eines CD-ROM oder ein SCSI-Laufwerk), wie auf einer „Datei“ sein könnte (das heißt ein Bild).
Lösung
Der Einwand, dass „fopen“ ist tragbar und „offen“ ist nicht gefälscht ist.
fopen Teil libc ist, offen ist ein POSIX-Systemaufruf.
Jeder ist so portabel wie der Ort, sie kommen.
i / o-Dateien fopen'ed wird (Sie davon ausgehen, muss es auch sein mag, und für praktische Zwecke wird) gepuffert durch libc, Filedeskriptoren open () "ed werden nicht von libc gepuffert (sie kann gut sein, und in der Regel werden im Dateisystem gepuffert -. aber nicht alles, was Sie öffnen () ist eine Datei auf einem Dateisystem
Was ist der Sinn von fopen'ing, beispielsweise ein Geräteknoten wie / dev / sg0, zu sagen oder / dev / tty0 ... Was werden Sie tun? Du wirst einen ioctl auf einem FILE * tun? Viel Glück damit.
Vielleicht möchten Sie mit einigen Fahnen wie O_DIRECT öffnen - macht keinen Sinn, mit fopen ()
.Andere Tipps
Es ist besser, offen () zu verwenden, wenn Sie in der Unix-ähnlichen Systeme kleben und Sie vielleicht mögen:
- Haben Sie mehr feinkörnige Kontrolle über Unix-Berechtigungs-Bits auf Dateierstellung.
- Mit den Funktionen auf tieferer Ebene, wie beispielsweise Lesen / Schreiben / Mmap in Bezug auf die C Gegensatz gepufferten Strom I / O-Funktionen.
- Verwenden Sie Dateideskriptor (fd) basierend IO Scheduling (Umfrage, wählen, etc.) Sie können natürlich einen fd aus einer Datei erhalten * mit fileno (), aber darauf, nicht FILE zu mischen * basierten Stromfunktionen getroffen werden müssen, mit fd basierte Funktionen.
- Öffnen Sie ein beliebiges spezielles Gerät (keine reguläre Datei)
Es ist besser, verwenden fopen / fread / fwrite für maximale Portabilität, da diese Standard-C-Funktionen sind die Funktionen, die ich oben erwähnt habe, sind es nicht.
fopen Arbeiten auf einem höheren Niveau als offen .... fopen gibt Ihnen einen Zeiger Stream-Datei, die in die Stream Abstraktion ähnlich ist, die Sie in C ++ lesen
open kehren Sie eine Dateibeschreibung für die Datei geöffnet ... Es macht Sie keinen Strom Abstraktion liefern und Sie sind verantwortlich für die Bits und Bytes Umgang mit sich selbst ... Das auf einem niedrigeren Niveau ist im Vergleich zu fopen
Stdio Ströme werden gepuffert, während open () Filedeskriptoren nicht. Hängt davon ab, was Sie brauchen. Sie können auch eine vom anderen erstellen:
int fileno (FILE * stream) gibt den Dateideskriptor für einen FILE *, FILE * fdopen (int fildes, const char * Modus) erstellt eine Datei * von einem Dateideskriptor.
Seien Sie vorsichtig, wenn gepufferte Mischung und IO ungepufferten, da Sie verlieren, was in Ihrem Puffer ist, wenn Sie spülen Sie es nicht mit fflush ().
Ja. Wenn Sie einen Low-Level-Griff.
Unter UNIX-Betriebssystemen können Sie in der Regel tauschen Datei-Handles und Steckdosen.
Auch Low-Level-Griffe für eine bessere ABI-Kompatibilität als FILE-Pointer machen.
in der Regel, sollten Sie mit der Standardbibliothek (fopen) begünstigen. Es gibt jedoch Fälle, in denen Sie benötigen, um direkt öffnen können.
Ein Beispiel, das in den Sinn kommt, ist um einen Fehler in einer älteren Version von solaris zu arbeiten, die fopen gemacht fehlschlagen, nachdem 256 Dateien geöffnet waren. Das war, weil sie erroniously ein unsigned char für das fd Feld verwendet in ihrer Struktur FILE Implementierung anstelle eines int. Aber das war ein sehr spezieller Fall.
read () & write () verwenden ungepufferte E / A. ( fd : integer Dateideskriptor)
fread () & fwrite () Verwendung gepufferte I / O. ( Datei * Strukturzeiger)
Binärdaten an ein Rohr geschrieben mit write () kann nicht der Lage sein, mit binären Daten lesen fread () , wegen Byteausrichtungen, variable Größe, etc. Es ist ein Mist-shoot.
Die meisten Low-Level-Gerätetreiber-Code verwendet ungepufferte E / A-Anrufe.
Die meisten Anwendungsebene I / O-gepufferte verwendet.
Mit den Datei * und den dazugehörigen Funktionen ist auf einer Maschine-by-Maschine Basis OK aber Portabilität verloren auf anderen Architekturen in der Lesen und Schreiben von Binärdaten. fwrite () ist I / O gepuffert und zu unzuverlässigen Ergebnissen führen kann, wenn geschrieben für eine 64-Bit-Architektur und auf eine 32-Bit; oder (Windows / Linux). Die meisten Betriebssysteme haben Kompatibilität Makros in ihrem eigenen Code, um dies zu verhindern.
Für Low-Level-Binär-E / A-Portabilität lesen () und write () Garantie die gleiche binäre liest und schreibt, wenn sie auf unterschiedlichen Architekturen kompiliert werden. Die grundlegende Sache ist die eine oder die andere zu wählen und darüber übereinstimmen, während der gesamten binären Suite.
<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
So für die grundlegende Nutzung würde ich persönlich die oben verwenden, ohne Idiome zu viel.
MischIm Gegensatz dazu
<unistd.h> write()
lseek()
close()
pipe()
<sys/types.h>
<sys/stat.h>
<fcntl.h> open()
creat()
fcntl()
all use file descriptors.
Diese bieten eine fein abgestimmte Kontrolle über das Lesen und Schreiben Bytes (Empfohlen für spezielle Geräte und fifos (Rohre)).
Also noch einmal, verwenden, was Sie brauchen, aber in Ihrem Idiom und Schnittstellen konsistent halten. Wenn die meisten Ihrer Code-Basis einen Modus verwendet, verwenden Sie das auch, es sei denn, es gibt ein echter Grund, nicht zu. Beide Sätze von I / O-Bibliotheksfunktionen sind extrem zuverlässig und verwendet Millionen Mal pro Tag.
note - Wenn Sie mit einer anderen Sprache C I / O sind Schnittstellen, (Perl, Python, Java, C #, lua ...) Besuche , was die Entwickler dieser Sprachen empfehlen, bevor Sie Ihren C-Code schreiben und sich etwas Mühe sparen.
fopen und seine Vettern gepuffert. Öffnen, Lesen und Schreiben nicht gepuffert werden. Ihre Anwendung kann oder auch nicht.
fprintf und scanf haben eine reichere API, die Sie lesen und schreiben formatierte Textdateien erlaubt. Lesen und Schreiben von grundlegenden Arrays von Bytes verwenden. Umwandlungen und Formatierungen müssen Hand gefertigt werden.
Der Unterschied zwischen Datei-Deskriptoren und (FILE *) ist wirklich belanglos.
Randy