I'm doing a small project in C after quite a long time away from it. These happen to include some file handling. I noticed in various documentation that there are functions which return FILE *
handles and others which return (small integer) descriptors. Both sets of functions offer the same basic services I need so it really does not matter I use.
经过相当长的一段时间后,我正在C做一个小项目。这些碰巧包括一些文件处理。我在各种文档中注意到有返回FILE *句柄的函数和返回(小整数)描述符的其他函数。这两组功能都提供了我需要的基本服务,所以我使用它并不重要。
But I'm curious about the collection wisdom: is it better to use fopen()
and friends, or open()
and friends?
但我对收藏智慧感到好奇:使用fopen()和朋友,或者打开()和朋友更好吗?
Edit Since someone mentioned buffered vs unbuffered and accessing devices, I should add that one part of this small project will be writing a userspace filesystem driver under FUSE. So the file level access could as easily be on a device (e.g. a CDROM or a SCSI drive) as on a "file" (i.e. an image).
编辑因为有人提到缓冲与未缓冲和访问设备,我应该补充说,这个小项目的一部分将在FUSE下编写用户空间文件系统驱动程序。因此,文件级访问可以像在“文件”(即图像)上那样容易地在设备(例如CDROM或SCSI驱动器)上。
7 个解决方案
#1
The objection that "fopen" is portable and "open" isn't is bogus.
“fopen”便携和“开放”的反对意见并非虚假。
fopen is part of libc, open is a POSIX system call.
fopen是libc的一部分,open是POSIX系统调用。
Each is as portable as the place they come from.
每个都像他们来自的地方一样便携。
i/o to fopen'ed files is (you must assume it may be, and for practical purposes, it is) buffered by libc, file descriptors open()'ed are not buffered by libc (they may well be, and usually are buffered in the filesystem -- but not everything you open() is a file on a filesystem.
对于fopen'ed文件的i / o是(你必须假设它可能是,并且出于实际目的,它是)由libc缓冲,文件描述符open()'ed不被libc缓冲(它们可能是,通常是缓存在文件系统中 - 但不是你打开的所有东西()都是文件系统上的文件。
What's the point of fopen'ing, for example, a device node like /dev/sg0, say, or /dev/tty0... What are you going to do? You're going to do an ioctl on a FILE *? Good luck with that.
例如,像/ dev / sg0这样的设备节点,或者/ dev / tty0,有什么意义呢?你打算做什么?你要在FILE上做一个ioctl *?祝你好运。
Maybe you want to open with some flags like O_DIRECT -- makes no sense with fopen().
也许你想打开像O_DIRECT这样的标志 - 用fopen()没有意义。
#2
It is better to use open() if you are sticking to unix-like systems and you might like to:
如果您坚持使用类似unix的系统,最好使用open(),您可能希望:
- Have more fine-grained control over unix permission bits on file creation.
- Use the lower-level functions such as read/write/mmap as opposed to the C buffered stream I/O functions.
- Use file descriptor (fd) based IO scheduling (poll, select, etc.) You can of course obtain an fd from a FILE * using fileno(), but care must be taken not to mix FILE * based stream functions with fd based functions.
- Open any special device (not a regular file)
在文件创建时对unix权限位进行更细粒度的控制。
使用较低级别的函数(如read / write / mmap)而不是C缓冲流I / O函数。
使用基于文件描述符(fd)的IO调度(轮询,选择等)您当然可以使用fileno()从FILE *获取fd,但必须注意不要将基于FILE *的流函数与基于fd的函数混合使用。
打开任何特殊设备(不是常规文件)
It is better to use fopen/fread/fwrite for maximum portability, as these are standard C functions, the functions I've mentioned above aren't.
最好使用fopen / fread / fwrite来获得最大的可移植性,因为这些是标准的C函数,我上面提到的函数不是。
#3
fopen works at a higher level than open .... fopen returns you a pointer to FILE stream which is similar to the stream abstraction that you read in C++
fopen工作在比open更高的级别.... fopen返回一个指向FILE流的指针,类似于你在C ++中读取的流抽象
open returns you a file descriptor for the file opened ... It does not provide you a stream abstraction and you are responsible for handling the bits and bytes yourself ... This is at a lower level as compared to fopen
open返回一个打开的文件的文件描述符...它不提供流抽象,你负责自己处理位和字节...这与fopen相比处于较低的水平
Stdio streams are buffered, while open() file descriptors are not. Depends on what you need. You can also create one from the other:
Stdio流是缓冲的,而open()文件描述符则不是。取决于你需要什么。您也可以从另一个创建一个:
int fileno (FILE * stream) returns the file descriptor for a FILE *, FILE * fdopen(int fildes, const char * mode) creates a FILE * from a file descriptor.
int fileno(FILE * stream)返回FILE *的文件描述符,FILE * fdopen(int fildes,const char * mode)从文件描述符创建FILE *。
Be careful when intermixing buffered and non-buffered IO, since you'll lose what's in your buffer when you don't flush it with fflush().
在混合缓冲和非缓冲IO时要小心,因为当你不用fflush()冲洗它时,你将丢失缓冲区中的内容。
#4
Yes. When you need a low-level handle.
是。当你需要一个低级句柄。
On UNIX operating systems, you can generally exchange file handles and sockets.
在UNIX操作系统上,通常可以交换文件句柄和套接字。
Also, low-level handles make for better ABI compatibility than FILE pointers.
此外,低级句柄比FILE指针具有更好的ABI兼容性。
#5
usually, you should favor using the standard library (fopen). However, there are occasions where you will need to use open directly.
通常,你应该赞成使用标准库(fopen)。但是,有时您需要直接使用open。
One example that comes to mind is to work around a bug in an older version of solaris which made fopen fail after 256 files were open. This was because they erroniously used an unsigned char for the fd field in their struct FILE implementation instead of an int. But this was a very specific case.
想到的一个例子是解决旧版solaris中的一个错误,这个错误导致256个文件打开后fopen失败。这是因为他们在struct FILE实现中使用unsigned char作为fd字段而不是int。但这是一个非常具体的案例。
#6
read() & write() use unbuffered I/O. (fd: integer file descriptor)
read()&write()使用无缓冲的I / O. (fd:整数文件描述符)
fread() & fwrite() use buffered I/O. (FILE* structure pointer)
fread()和fwrite()使用缓冲的I / O. (文件*结构指针)
Binary data written to a pipe with write() may not be able to read binary data with fread(), because of byte alignments, variable sizes, etc. Its a crap-shoot.
使用write()写入管道的二进制数据可能无法使用fread()读取二进制数据,因为字节对齐,可变大小等等。它是一个废话。
Most low-level device driver code uses unbuffered I/O calls.
大多数低级设备驱动程序代码使用无缓冲的I / O调用。
Most application level I/O uses buffered.
大多数应用程序级I / O使用缓冲。
Use of the FILE* and its associated functions is OK on a machine-by-machine basis: but portability is lost on other architectures in the reading and writing of binary data. fwrite() is buffered I/O and can lead to unreliable results if written for a 64 bit architecture and run on a 32bit; or (Windows/Linux). Most OSs have compatibility macros within their own code to prevent this.
在逐个机器的基础上使用FILE *及其相关功能是正常的:但在读取和写入二进制数据时,其他架构的可移植性会丢失。 fwrite()是缓冲I / O,如果为64位架构编写并运行在32位上,可能导致不可靠的结果;或(Windows / Linux)。大多数操作系统在自己的代码中都有兼容性宏来防止这种情况。
For low-level binary I/O portability read() and write() guarantee the same binary reads and writes when compiled on differing architectures. The basic thing is to pick one way or the other and be consistent about it, throughout the binary suite.
对于低级二进制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
So for basic use I would personally use the above without mixing idioms too much.
因此,对于基本用途,我个人会使用上面的内容而不会混淆过多的习语。
By contrast,
<unistd.h> write()
lseek()
close()
pipe()
<sys/types.h>
<sys/stat.h>
<fcntl.h> open()
creat()
fcntl()
all use file descriptors.
These provide fine-grained control over reading and writing bytes (recommended for special devices and fifos (pipes) ).
它们提供了对读写字节的细粒度控制(推荐用于特殊设备和fifos(管道))。
So again, use what you need, but keep consistent in your idioms and interfaces. If most of your code base uses one mode , use that too, unless there is a real reason not to. Both sets of I/O library functions are extremely reliable and used millions of times a day.
再次,使用您需要的东西,但在您的习语和界面中保持一致。如果你的大部分代码库都使用一种模式,那么也要使用它,除非有真正的理由不这样做。两组I / O库函数都非常可靠,每天使用数百万次。
note-- If you are interfacing C I/O with another language, (perl, python, java, c#, lua ...) check out what the developers of those languages recommend before you write your C code and save yourself some trouble.
注意 - 如果您正在使用其他语言连接C I / O,(perl,python,java,c#,lua ...)在编写C代码之前,请查看这些语言的开发人员所建议的内容,并为自己省去一些麻烦。
#7
fopen and its cousins are buffered. open, read, and write are not buffered. Your application may or may not care.
fopen和它的表兄弟都是缓冲的。打开,读取和写入都不是缓冲的。您的申请可能会或可能不会关心。
fprintf and scanf have a richer API that allows you to read and write formatted text files. read and write use fundamental arrays of bytes. Conversions and formatting must be hand crafted.
fprintf和scanf有一个更丰富的API,允许您读取和写入格式化的文本文件。读写使用基本的字节数组。转换和格式化必须手工制作。
The difference between file descriptors and (FILE *) is really inconsequential.
文件描述符和(FILE *)之间的区别实际上是无关紧要的。
Randy
#1
The objection that "fopen" is portable and "open" isn't is bogus.
“fopen”便携和“开放”的反对意见并非虚假。
fopen is part of libc, open is a POSIX system call.
fopen是libc的一部分,open是POSIX系统调用。
Each is as portable as the place they come from.
每个都像他们来自的地方一样便携。
i/o to fopen'ed files is (you must assume it may be, and for practical purposes, it is) buffered by libc, file descriptors open()'ed are not buffered by libc (they may well be, and usually are buffered in the filesystem -- but not everything you open() is a file on a filesystem.
对于fopen'ed文件的i / o是(你必须假设它可能是,并且出于实际目的,它是)由libc缓冲,文件描述符open()'ed不被libc缓冲(它们可能是,通常是缓存在文件系统中 - 但不是你打开的所有东西()都是文件系统上的文件。
What's the point of fopen'ing, for example, a device node like /dev/sg0, say, or /dev/tty0... What are you going to do? You're going to do an ioctl on a FILE *? Good luck with that.
例如,像/ dev / sg0这样的设备节点,或者/ dev / tty0,有什么意义呢?你打算做什么?你要在FILE上做一个ioctl *?祝你好运。
Maybe you want to open with some flags like O_DIRECT -- makes no sense with fopen().
也许你想打开像O_DIRECT这样的标志 - 用fopen()没有意义。
#2
It is better to use open() if you are sticking to unix-like systems and you might like to:
如果您坚持使用类似unix的系统,最好使用open(),您可能希望:
- Have more fine-grained control over unix permission bits on file creation.
- Use the lower-level functions such as read/write/mmap as opposed to the C buffered stream I/O functions.
- Use file descriptor (fd) based IO scheduling (poll, select, etc.) You can of course obtain an fd from a FILE * using fileno(), but care must be taken not to mix FILE * based stream functions with fd based functions.
- Open any special device (not a regular file)
在文件创建时对unix权限位进行更细粒度的控制。
使用较低级别的函数(如read / write / mmap)而不是C缓冲流I / O函数。
使用基于文件描述符(fd)的IO调度(轮询,选择等)您当然可以使用fileno()从FILE *获取fd,但必须注意不要将基于FILE *的流函数与基于fd的函数混合使用。
打开任何特殊设备(不是常规文件)
It is better to use fopen/fread/fwrite for maximum portability, as these are standard C functions, the functions I've mentioned above aren't.
最好使用fopen / fread / fwrite来获得最大的可移植性,因为这些是标准的C函数,我上面提到的函数不是。
#3
fopen works at a higher level than open .... fopen returns you a pointer to FILE stream which is similar to the stream abstraction that you read in C++
fopen工作在比open更高的级别.... fopen返回一个指向FILE流的指针,类似于你在C ++中读取的流抽象
open returns you a file descriptor for the file opened ... It does not provide you a stream abstraction and you are responsible for handling the bits and bytes yourself ... This is at a lower level as compared to fopen
open返回一个打开的文件的文件描述符...它不提供流抽象,你负责自己处理位和字节...这与fopen相比处于较低的水平
Stdio streams are buffered, while open() file descriptors are not. Depends on what you need. You can also create one from the other:
Stdio流是缓冲的,而open()文件描述符则不是。取决于你需要什么。您也可以从另一个创建一个:
int fileno (FILE * stream) returns the file descriptor for a FILE *, FILE * fdopen(int fildes, const char * mode) creates a FILE * from a file descriptor.
int fileno(FILE * stream)返回FILE *的文件描述符,FILE * fdopen(int fildes,const char * mode)从文件描述符创建FILE *。
Be careful when intermixing buffered and non-buffered IO, since you'll lose what's in your buffer when you don't flush it with fflush().
在混合缓冲和非缓冲IO时要小心,因为当你不用fflush()冲洗它时,你将丢失缓冲区中的内容。
#4
Yes. When you need a low-level handle.
是。当你需要一个低级句柄。
On UNIX operating systems, you can generally exchange file handles and sockets.
在UNIX操作系统上,通常可以交换文件句柄和套接字。
Also, low-level handles make for better ABI compatibility than FILE pointers.
此外,低级句柄比FILE指针具有更好的ABI兼容性。
#5
usually, you should favor using the standard library (fopen). However, there are occasions where you will need to use open directly.
通常,你应该赞成使用标准库(fopen)。但是,有时您需要直接使用open。
One example that comes to mind is to work around a bug in an older version of solaris which made fopen fail after 256 files were open. This was because they erroniously used an unsigned char for the fd field in their struct FILE implementation instead of an int. But this was a very specific case.
想到的一个例子是解决旧版solaris中的一个错误,这个错误导致256个文件打开后fopen失败。这是因为他们在struct FILE实现中使用unsigned char作为fd字段而不是int。但这是一个非常具体的案例。
#6
read() & write() use unbuffered I/O. (fd: integer file descriptor)
read()&write()使用无缓冲的I / O. (fd:整数文件描述符)
fread() & fwrite() use buffered I/O. (FILE* structure pointer)
fread()和fwrite()使用缓冲的I / O. (文件*结构指针)
Binary data written to a pipe with write() may not be able to read binary data with fread(), because of byte alignments, variable sizes, etc. Its a crap-shoot.
使用write()写入管道的二进制数据可能无法使用fread()读取二进制数据,因为字节对齐,可变大小等等。它是一个废话。
Most low-level device driver code uses unbuffered I/O calls.
大多数低级设备驱动程序代码使用无缓冲的I / O调用。
Most application level I/O uses buffered.
大多数应用程序级I / O使用缓冲。
Use of the FILE* and its associated functions is OK on a machine-by-machine basis: but portability is lost on other architectures in the reading and writing of binary data. fwrite() is buffered I/O and can lead to unreliable results if written for a 64 bit architecture and run on a 32bit; or (Windows/Linux). Most OSs have compatibility macros within their own code to prevent this.
在逐个机器的基础上使用FILE *及其相关功能是正常的:但在读取和写入二进制数据时,其他架构的可移植性会丢失。 fwrite()是缓冲I / O,如果为64位架构编写并运行在32位上,可能导致不可靠的结果;或(Windows / Linux)。大多数操作系统在自己的代码中都有兼容性宏来防止这种情况。
For low-level binary I/O portability read() and write() guarantee the same binary reads and writes when compiled on differing architectures. The basic thing is to pick one way or the other and be consistent about it, throughout the binary suite.
对于低级二进制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
So for basic use I would personally use the above without mixing idioms too much.
因此,对于基本用途,我个人会使用上面的内容而不会混淆过多的习语。
By contrast,
<unistd.h> write()
lseek()
close()
pipe()
<sys/types.h>
<sys/stat.h>
<fcntl.h> open()
creat()
fcntl()
all use file descriptors.
These provide fine-grained control over reading and writing bytes (recommended for special devices and fifos (pipes) ).
它们提供了对读写字节的细粒度控制(推荐用于特殊设备和fifos(管道))。
So again, use what you need, but keep consistent in your idioms and interfaces. If most of your code base uses one mode , use that too, unless there is a real reason not to. Both sets of I/O library functions are extremely reliable and used millions of times a day.
再次,使用您需要的东西,但在您的习语和界面中保持一致。如果你的大部分代码库都使用一种模式,那么也要使用它,除非有真正的理由不这样做。两组I / O库函数都非常可靠,每天使用数百万次。
note-- If you are interfacing C I/O with another language, (perl, python, java, c#, lua ...) check out what the developers of those languages recommend before you write your C code and save yourself some trouble.
注意 - 如果您正在使用其他语言连接C I / O,(perl,python,java,c#,lua ...)在编写C代码之前,请查看这些语言的开发人员所建议的内容,并为自己省去一些麻烦。
#7
fopen and its cousins are buffered. open, read, and write are not buffered. Your application may or may not care.
fopen和它的表兄弟都是缓冲的。打开,读取和写入都不是缓冲的。您的申请可能会或可能不会关心。
fprintf and scanf have a richer API that allows you to read and write formatted text files. read and write use fundamental arrays of bytes. Conversions and formatting must be hand crafted.
fprintf和scanf有一个更丰富的API,允许您读取和写入格式化的文本文件。读写使用基本的字节数组。转换和格式化必须手工制作。
The difference between file descriptors and (FILE *) is really inconsequential.
文件描述符和(FILE *)之间的区别实际上是无关紧要的。
Randy