是否有任何普通的理由使用open()而不是fopen()?

经过相当长的一段时间后,我正在C做一个小项目。 这些碰巧包括一些文件处理。 我在各种文档中注意到有返回FILE *句柄的函数和返回(小整数)描述符的其他函数。 这两组function都提供了我需要的基本服务,所以我使用它并不重要。

但我对collections智慧感到好奇:使用fopen()和朋友,还是open()和朋友更好?

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

“fopen”便携和“开放”的反对意见并非虚假。

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

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

对于fopen’ed文件的i / o是(你必须假设它可能是,并且出于实际目的,它是)由libc缓冲,文件描述符open()’ed不被libc缓冲(它们可能是,通常是缓存在文件系统中 – 但不是你打开的所有东西()都是文件系统上的文件。

例如,像/ dev / sg0这样的设备节点,或者/ dev / tty0,有什么意义呢?你打算做什么? 你要在FILE上做一个ioctl *? 祝你好运。

也许你想打开像O_DIRECT这样的标志 – 用fopen()没有意义。

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

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

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

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

open返回一个打开的文件的文件描述符…它不提供流抽象,你负责自己处理位和字节…这与fopen相比处于较低的水平

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

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

在混合缓冲和非缓冲IO时要小心,因为当你不用fflush()冲洗它时,你将丢失缓冲区中的内容。

是。 当你需要一个低级句柄。

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

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

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

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

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

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

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

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

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

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

对于低级二进制I / O可移植性, read()write()在不同体系结构上编译时保证相同的二进制读写操作。 基本的是在整个二进制套件中选择一种方式或一种方式保持一致。

  // 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 

因此,对于基本用途,我个人会使用上面的内容而不会混淆过多的习语。

相比之下,

  write() lseek() close() pipe()    open() creat() fcntl() all use file descriptors. 

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

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

注意 –如果你正在使用另一种语言连接CI / O,(perl,python,java,c#,lua ……)在编写C代码之前,请查看这些语言的开发人员所推荐的内容并节省一些麻烦。

fopen和它的表兄弟都是缓冲的。 打开,读取和写入都不是缓冲的。 您的申请可能会或可能不会关心。

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

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

兰迪