7

经过很长一段时间后,我正在用 C 语言做一个小项目。这些恰好包括一些文件处理。我在各种文档中注意到,有些函数返回FILE *句柄,有些函数返回(小整数)描述符。两组功能都提供了我需要的相同基本服务,所以我使用它真的没关系。

但我很好奇收藏智慧:是用fopen()和朋友更好,还是open()和朋友更好?

编辑由于有人提到缓冲与非缓冲和访问设备,我应该补充一点,这个小项目的一部分将在 FUSE 下编写用户空间文件系统驱动程序。因此,文件级别的访问可以像在“文件”(即图像)上一样容易地在设备(例如 CDROM 或 SCSI 驱动器)上进行。

4

7 回答 7

15

如果您坚持使用类似 unix 的系统,最好使用 open() 并且您可能希望:

  • 对文件创建时的 unix 权限位进行更细粒度的控制。
  • 使用较低级别的函数,例如 read/write/mmap,而不是 C 缓冲流 I/O 函数。
  • 使用基于文件描述符 (fd) 的 IO 调度(轮询、选择等) 您当然可以使用 fileno() 从 FILE * 获取 fd,但必须注意不要将基于 FILE * 的流函数与基于 fd 的函数混合.
  • 打开任何特殊设备(不是常规文件)

最好使用 fopen/fread/fwrite 以获得最大的可移植性,因为这些是标准 C 函数,我上面提到的函数不是。

于 2009-02-13T04:15:49.777 回答
7

“fopen”是可移植的而“open”不是可移植的反对意见是虚假的。

fopen 是 libc 的一部分,open 是一个 POSIX 系统调用。

每个都像他们来自的地方一样便携。

fopen'ed 文件的 i/o 是(您必须假设它可能是,并且出于实际目的,它是)由 libc 缓冲,文件描述符 open()'ed 不由 libc 缓冲(它们很可能并且通常是缓冲在文件系统中——但不是你 open() 的所有内容都是文件系统上的文件。

fopen'ing 有什么意义,例如,像 /dev/sg0 或 /dev/tty0 这样的设备节点……你打算做什么?您要对 FILE * 执行 ioctl?祝你好运。

也许你想用 O_DIRECT 之类的标志打开——用 fopen() 没有意义。

于 2009-02-13T04:27:53.337 回答
4

fopen 在比 open 更高的级别上工作.... fopen 返回一个指向 FILE 流的指针,它类似于您在 C++ 中读取的流抽象

open 为您返回打开文件的文件描述符...它不为您提供流抽象,您自己负责处理位和字节...与 fopen 相比,它处于较低级别

Stdio 流被缓冲,而 open() 文件描述符则没有。取决于你需要什么。您也可以从另一个创建一个:

int fileno (FILE * stream) 返回 FILE * 的文件描述符,FILE * fdopen(int fildes, const char * mode) 从文件描述符创建 FILE *。

混合缓冲和非缓冲 IO 时要小心,因为如果不使用 fflush() 刷新缓冲区,则会丢失缓冲区中的内容。

于 2009-02-13T04:18:39.020 回答
3

是的。当您需要一个低级句柄时。

在 UNIX 操作系统上,您通常可以交换文件句柄和套接字。

此外,与 FILE 指针相比,低级句柄具有更好的 ABI 兼容性。

于 2009-02-13T04:08:31.650 回答
2

通常,您应该倾向于使用标准库 (fopen)。但是,有时您需要直接使用 open。

想到的一个例子是解决旧版本的 solaris 中的一个错误,该错误导致 fopen 在打开 256 个文件后失败。这是因为他们在 struct FILE 实现中错误地将 unsigned char 用于 fd 字段,而不是 int。但这是一个非常具体的案例。

于 2009-02-13T04:07:30.263 回答
2

read() & write() 使用无缓冲 I/O。( fd : 整数文件描述符)

fread()fwrite() 使用缓冲 I/O。(文件*结构指针)

由于字节对齐、可变大小等原因,使用write() 写入 管道的二进制数据可能无法使用fread()读取二进制数据。这是一个废话。

大多数低级设备驱动程序代码使用无缓冲的 I/O 调用。

大多数应用程序级 I/O 使用缓冲。

在逐个机器的基础上使用FILE * 及其相关函数是可以的:但是在其他体系结构上在读写二进制数据时会丢失可移植性。fwrite() 是缓冲 I/O,如果为 64 位架构编写并在 32 位上运行,可能会导致不可靠的结果;或(Windows/Linux)。大多数操作系统在自己的代码中都有兼容性宏来防止这种情况。

对于低级二进制 I/O 可移植性, read()write()保证在不同架构上编译时相同的二进制读取和写入。基本的事情是在整个二进制套件中选择一种方式并保持一致。

<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

因此,对于基本用途,我会亲自使用上述内容,而不会过多地混合成语。

相比之下,

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

这些提供了对读取和写入字节的细粒度控制(推荐用于特殊设备和 fifos(管道))。

再说一次,使用你需要的东西,但在你的习惯用法和界面上保持一致。如果您的大部分代码库都使用一种模式,那么也使用它,除非有真正的理由不这样做。两组 I/O 库函数都非常可靠,每天使用数百万次。

注意——如果您正在将 CI/O 与另一种语言(perl、python、java、c#、lua ...)进行交互,请在编写 C 代码之前查看这些语言的开发人员推荐的内容,这样可以省去一些麻烦。

于 2012-06-03T21:40:36.580 回答
0

fopen 和它的表兄弟是缓冲的。打开、读取和写入不会被缓冲。您的应用程序可能会或可能不会在意。

fprintf 和 scanf 具有更丰富的 API,允许您读取和写入格式化的文本文件。读写使用基本的字节数组。转换和格式必须是手工制作的。

文件描述符和 (FILE *) 之间的区别实际上是无关紧要的。

兰迪

于 2009-02-13T04:58:03.433 回答