2009-02-13 6 views
5

Ich mache ein kleines Projekt in C nach ziemlich langer Zeit weg davon. Dazu gehören einige Dateihandling. Ich habe in verschiedenen Dokumentationen bemerkt, dass es Funktionen gibt, die FILE * Handles und andere zurückgeben, die (kleine ganze Zahl) Deskriptoren zurückgeben. Beide Funktionen bieten die gleichen grundlegenden Dienste, die ich brauche, also ist es wirklich egal, was ich benutze.Gibt es einen gewöhnlichen Grund, open() anstelle von fopen() zu verwenden?

Aber ich bin neugierig auf die Sammlung Weisheit: ist es besser zu verwenden und Freunde, oder open() und Freunde?

Edit Da jemand gepuffert vs ungepuffered und zugreifende Geräte erwähnt, sollte ich hinzufügen, dass ein Teil dieses kleinen Projekts einen Userspace-Dateisystemtreiber unter FUSE schreiben wird. So könnte der Zugriff auf Dateiebene genauso einfach auf einem Gerät (z. B. einem CD-ROM- oder einem SCSI-Laufwerk) wie auf einer "Datei" (d. H. Einem Bild) erfolgen.

Antwort

5

Der Einwand, dass "fopen" portabel und "offen" ist, ist nicht falsch.

fopen ist Teil von libc, open ist ein POSIX-Systemaufruf.

Jeder ist so tragbar wie der Ort, aus dem sie kommen.

I/O zu fopen'ed Dateien ist (Sie müssen annehmen, dass es sein kann, und für praktische Zwecke ist es) von libc gepuffert, Dateideskriptoren open() 'ed sind nicht von libc gepuffert (sie können auch sein Normalerweise sind sie im Dateisystem gepuffert - aber nicht alles, was Sie öffnen() ist eine Datei in einem Dateisystem.

Was ist der Sinn von fopen'ing, zum Beispiel, ein Geräteknoten wie/dev/sg0, sagen wir, oder/dev/tty0 ... Was wirst du tun? Du wirst ein ioctl auf einer Datei * machen? Viel Glück damit.

Vielleicht möchten Sie mit einigen Flags wie O_DIRECT öffnen - macht kein Sinn mit fopen().

2

Normalerweise sollten Sie die Verwendung der Standardbibliothek (fopen) bevorzugen. Es gibt jedoch Gelegenheiten, bei denen Sie direkt öffnen müssen.

Ein Beispiel, das in den Sinn kommt, ist, einen Fehler in einer älteren Version von Solaris zu umgehen, der fopen fehlgeschlagen hat, nachdem 256 Dateien geöffnet wurden. Dies lag daran, dass sie in der FILE-Implementierung ihrer Struktur fälschlicherweise ein Zeichen ohne Vorzeichen für das Feld fd anstelle eines int verwendeten. Aber das war ein sehr spezifischer Fall.

+0

Diese "ältere Version von Solaris" enthält Solaris 10 (aktuell) - zumindest für 32-Bit-Code. –

3

Ja. Wenn Sie einen Low-Level-Griff benötigen.

Auf UNIX-Betriebssystemen können Sie im Allgemeinen Dateihandles und Sockets austauschen.

Auch Low-Level-Handles sorgen für bessere ABI-Kompatibilität als FILE-Pointer.

+0

int fd = Dateino (Datei);/* erhält einen Low-Level-Handle von einer Datei * */ –

+0

HINWEIS: fileno ist posix und nicht c89/c99 obwohl. –

+0

@Joshua: Beziehen Sie sich auf die Fähigkeit, Dateideskriptoren zwischen Prozessen (esoterisch, aber ich kenne Software, wo es gemacht wird) zu übergeben, oder zwischen Dateizeiger und Dateideskriptoren oder etwas anderes zu wechseln? Denken Sie daran, dass fdopen() einen Dateideskriptor in einen Dateizeiger konvertieren kann. –

14

Es ist besser, offen() zu verwenden, wenn Sie in der Unix-ähnlichen Systeme kleben und Sie könnten mögen:

  • Haben Sie mehr feinkörnige Kontrolle über Unix-Berechtigungs-Bits auf Dateierstellung.
  • Verwenden Sie die untergeordneten Funktionen wie Lesen/Schreiben/mmap im Gegensatz zu den C-gepufferten Stream-E/A-Funktionen.
  • Verwenden Sie Datei-Deskriptor (fd) basierte IO-Planung (Poll, select, etc.) Sie können natürlich eine FD aus einer Datei * mit fileno() erhalten, aber es muss darauf geachtet werden, nicht mit FILE * basierten Stream-Funktionen zu mischen fd basierte Funktionen.
  • Öffnen Sie eine spezielle Vorrichtung (keine reguläre Datei)

Es ist besser fopen verwenden/fread/fwrite für maximale Portabilität, da diese Standard-C-Funktionen sind die Funktionen, die ich oben erwähnt habe, sind nicht .

+0

Ich bin froh, dass wir jemanden haben, der die Vorteile der Verwendung von open() hin und wieder ausschreibt. –

4

fopen Arbeiten auf einem höheren Niveau als offen .... fopen kehrt man einen Zeiger Strom-Datei, die in die Stream Abstraktion ähnlich ist, die Sie lesen in C++

offen kehren Ihnen ein Dateideskriptor für die Datei geöffnet ... Es bietet Ihnen keine Stream-Abstraktion und Sie sind verantwortlich für die Handhabung der Bits und Bytes selbst ... Dies ist auf einer niedrigeren Ebene als fopen

Stdio Streams sind gepuffert, während open() -Datei Deskriptoren sind nicht. Hängt davon ab, was Sie brauchen.Sie können auch eine von der anderen erstellen:

int fileno (FILE * stream) gibt den Dateideskriptor für eine FILE * zurück, FILE * fdopen (int fildes, const char * mode) erstellt eine FILE * aus einem Dateideskriptor.

Seien Sie vorsichtig beim Mischen gepufferter und nicht gepufferter E/A, da Sie verlieren, was in Ihrem Puffer ist, wenn Sie es nicht mit fflush() leeren.

0

fopen und seine Cousins ​​sind gepuffert. Öffnen, Lesen und Schreiben sind nicht gepuffert. Ihre Bewerbung mag es interessieren oder auch nicht.

fprintf und scanf haben eine reichhaltigere API, mit der Sie formatierte Textdateien lesen und schreiben können. Lese- und Schreibvorgänge verwenden grundlegende Bytearrays. Konvertierungen und Formatierungen müssen von Hand ausgeführt werden.

Der Unterschied zwischen Dateideskriptoren und (FILE *) ist wirklich inkonsequent.

Randy

+0

bis es nicht ist. –

2

read() & write() Verwendung ungepufferte E/A. (fd: integer Dateideskriptor)

fread() & fwrite() Verwendung gepufferte I/O. (FILE * Struktur pointer)

Binary an einem Rohr geschriebenen Daten mit write()nicht können binäre Daten mit fread lesen() wegen Byte Ausrichtungen, variable Größen, etc. Es ist ein Mist-Shooting.

Die meisten Low-Level-Gerätetreibercodes verwenden ungepufferte E/A-Aufrufe.

Die meisten E/A auf Anwendungsebene werden gepuffert verwendet.

Verwendung des FILE * und die dazugehörigen Funktionen ist OK auf einer Maschine-zu-Maschine Basis: aber Portabilität verloren auf anderen Architekturen in der Lesen und Schreiben von Binärdaten. fwrite() ist gepufferter E/A und kann zu unzuverlässigen Ergebnissen führen, wenn für eine 64-Bit-Architektur geschrieben und auf einem 32-Bit-System ausgeführt wird; oder (Windows/Linux). Die meisten Betriebssysteme verfügen über Kompatibilitätsmakros in ihrem eigenen Code, um dies zu verhindern.

Für Low-Level-Binär-E/A-Portabilität read() und write() Garantie die gleiche binäre liest und schreibt, wenn sie auf unterschiedlichen Architekturen kompiliert werden. Die grundlegende Sache ist, wählen Sie die eine oder andere und konsistent darüber, in der 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 

Also für die grundlegende Nutzung würde ich persönlich die oben verwenden, ohne Idiome zu viel zu mischen.

Dagegen

<unistd.h> write() 
      lseek() 
      close() 
      pipe() 
<sys/types.h> 
<sys/stat.h> 
<fcntl.h> open() 
      creat() 
      fcntl() 
all use file descriptors. 

Diese stellen feinkörnige Kontrolle über das Lesen und Schreiben von Bytes (empfohlen für spezielle Geräte und FIFOs (Rohre)).

Also wieder, verwenden Sie, was Sie brauchen, aber halten Sie konsequent in Ihren Idiomen und Schnittstellen. Wenn der Großteil Ihrer Codebasis einen Modus verwendet, verwenden Sie diesen auch, es sei denn, es gibt einen wirklichen Grund nicht zu. Beide Sätze von E/A-Bibliotheksfunktionen sind äußerst zuverlässig und werden millionenfach am Tag verwendet.

HINWEIS-- Wenn Sie CI/O mit einer anderen Sprache sind Schnittstellen, (Perl, Python, Java, C#, lua ...) Check-out, was die Entwickler dieser Sprachen bevor Sie empfehlen schreiben dein C-Code und ersparen Sie sich Ärger.

+0

"aber die Portabilität geht bei anderen Architekturen beim Lesen und Schreiben von Binärdaten verloren. Fwrite() ist gepufferte E/A und kann zu unzuverlässigen Ergebnissen führen, wenn sie für eine 64 - Bit - Architektur geschrieben und auf einem 32bit ausgeführt wird; oder (Windows/Linux). " - Das ist völlig falsch. Alles, was Sie tun müssen, ist, dass Sie die Datei im BINARY-Modus öffnen – minexew