【问题标题】:Is there any ordinary reason to use open() instead of fopen()?有什么普通的理由使用 open() 而不是 fopen()?
【发布时间】:2010-10-07 09:03:04
【问题描述】:

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

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

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

【问题讨论】:

    标签: c file posix libc


    【解决方案1】:

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

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

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

    【讨论】:

    • 我很高兴有人偶尔说明使用 open() 的好处。
    【解决方案2】:

    “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() 没有意义。

    【讨论】:

      【解决方案3】:

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

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

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

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

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

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

        • int fd = fileno(file); /* 从 FILE 中获取低级句柄 * */
        • 注意:fileno 是 posix 而不是 c89/c99。
        • @Joshua:您是指在进程之间传递文件描述符的能力(深奥,但我知道在哪里完成它的软件),或者在文件指针和文件描述符之间进行更改,还是其他什么?请记住,fdopen() 可以将文件描述符转换为文件指针。
        • 另一方面,如果你需要一个文件描述符,你已经超出了标准 C 的范围,所以需要 fileno 没有问题。
        • 乔纳森,没有。有时不同的编译器对 FILE 和 FILE * 有不同的想法,但低级句柄由 OS ABI 定义,因此您的 .o 文件更有可能与其他编译器的 .o 文件一起使用。
        【解决方案5】:

        通常,您应该倾向于使用标准库 (fopen)。但是,在某些情况下您需要直接使用 open。

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

        【讨论】:

        • “旧版本的 Solaris”包括 Solaris 10(当前)——至少对于 32 位代码。
        【解决方案6】:

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

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

        使用 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 库函数都非常可靠 并且每天使用数百万次。

        注意-- 如果您将 C I/O 与另一种语言交互, (perl、python、java、c#、lua ...)查看这些语言的开发人员是什么 建议在编写 C 代码之前为自己省点麻烦。

        【讨论】:

        • "但是在二进制数据的读写中,其他架构失去了可移植性。fwrite() 是缓冲 I/O,如果为 64 位架构编写并运行在32 位;或 (Windows/Linux)。” - 这完全不真实。您需要做的就是确保以 BINARY 模式打开文件
        【解决方案7】:

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

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

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

        兰迪

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-10-04
          • 2010-10-10
          • 2011-12-26
          • 1970-01-01
          • 1970-01-01
          • 2021-12-26
          • 2015-04-25
          相关资源
          最近更新 更多