标准 I/O 流(Standard I/O Stream)和 系统 I/O 流(System I/O Stream)是两种文件和流操作方式,主要区别在于它们的设计层次、缓冲机制以及易用性。以下是详细的比较:
1. 层次和定位
标准 I/O 流 | 系统 I/O 流 |
---|---|
是 C 标准库(<stdio.h> )的一部分,提供了一组高层抽象的文件操作接口。 | 是 POSIX 系统调用的一部分,直接与操作系统交互,用于底层文件操作。 |
封装了系统 I/O 流,提供了易用的、高效的接口。 | 更接近内核,通常提供更直接的文件描述符操作。 |
2. 缓冲机制
标准 I/O 流 | 系统 I/O 流 |
---|---|
使用 缓冲机制,会在内存中维护一个缓冲区以提高性能。 | 无缓冲机制,每次 I/O 操作都会直接触发系统调用,效率可能较低。 |
通过 setvbuf 或 setbuf 设置缓冲类型和大小。 | 不提供缓冲功能,操作结果直接反映在文件系统上。 |
示例:
- 标准 I/O 流缓冲机制:
- 数据被暂存在用户空间的缓冲区中,只有在缓冲区满或者调用
fflush
时才写入文件。
- 数据被暂存在用户空间的缓冲区中,只有在缓冲区满或者调用
- 系统 I/O 流无缓冲:
- 调用
write
立即触发系统调用,将数据写入文件。
- 调用
3. 文件表示方式
标准 I/O 流 | 系统 I/O 流 |
---|---|
使用 FILE* 类型作为文件流对象的表示。 | 使用文件描述符(int 类型)表示打开的文件。 |
FILE* 是高级抽象,提供了额外的功能如格式化 I/O(printf 、scanf )。 | 文件描述符是一个简单的整数,仅表示打开文件的内核索引。 |
示例:
// 标准 I/O 流
FILE *fp = fopen("example.txt", "r");
// 系统 I/O 流
int fd = open("example.txt", O_RDONLY);
4. 函数接口
标准 I/O 流 | 系统 I/O 流 |
---|---|
提供了高级接口,如 fopen 、fprintf 、fgets 、fread 等,适合格式化、缓冲和字符串操作。 | 提供了底层接口,如 open 、write 、read 等,适合直接文件和设备操作。 |
标准 I/O 流:
FILE *fp = fopen("file.txt", "w");
fprintf(fp, "Hello, World!\n");
fclose(fp);
系统 I/O 流:
#include <fcntl.h>
#include <unistd.h>
int fd = open("file.txt", O_WRONLY | O_CREAT, 0644);
write(fd, "Hello, World!\n", 14);
close(fd);
5. 错误处理
标准 I/O 流 | 系统 I/O 流 |
---|---|
使用 errno 结合函数如 perror 或 ferror 检查错误状态。 | 直接通过返回值判断,-1 表示错误,同时 errno 设置为具体错误代码。 |
示例:
总结
对比点 | 标准 I/O 流 | 系统 I/O 流 |
---|---|---|
表示方式 | FILE* | 文件描述符(整数) |
缓冲机制 | 有缓冲(性能较高) | 无缓冲(精确控制 I/O) |
函数接口 | 高级接口,如 fopen 、fprintf | 底层接口,如 open 、write |
错误处理 | 错误标志 + errno | 返回值 + errno |
适用场景 | 常规文件操作、文本处理 | 设备文件操作、随机访问、大量数据传输 |
- 标准 I/O 流错误处理:
FILE *fp = fopen("nonexistent.txt", "r"); if (fp == NULL) { perror("Error opening file"); }
- 系统 I/O 流错误处理:
int fd = open("nonexistent.txt", O_RDONLY); if (fd == -1) { perror("Error opening file"); }
1. fopen
和 fclose
用于打开和关闭文件。
FILE *fopen(const char *filename, const char *mode);
int fclose(FILE *stream);
示例:
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "w"); // 打开文件用于写入
if (file == NULL) {
perror("Error opening file");
return -1;
}
fprintf(file, "Hello, World!\n");
fclose(file); // 关闭文件
return 0;
}
2. fprintf
和 fscanf
用于格式化写入和读取文件。
int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
示例:
#include <stdio.h>
int main() {
FILE *file = fopen("data.txt", "w+");
if (file == NULL) {
perror("Error opening file");
return -1;
}
// 写入数据
fprintf(file, "Name: %s, Age: %d\n", "Alice", 25);
rewind(file); // 回到文件开头
// 读取数据
char name[50];
int age;
fscanf(file, "Name: %[^,], Age: %d", name, &age);
printf("Read from file: Name=%s, Age=%d\n", name, age);
fclose(file);
return 0;
}
3. fgets
和 fputs
用于读取和写入字符串。
char *fgets(char *str, int n, FILE *stream);
int fputs(const char *str, FILE *stream);
示例:
#include <stdio.h>
int main() {
FILE *file = fopen("text.txt", "w+");
if (file == NULL) {
perror("Error opening file");
return -1;
}
// 写入字符串
fputs("This is a test.\n", file);
rewind(file); // 回到文件开头
// 读取字符串
char buffer[100];
if (fgets(buffer, sizeof(buffer), file)) {
printf("Read: %s", buffer);
}
fclose(file);
return 0;
}
4. fputc
和 fgetc
用于写入和读取单个字符。
int fputc(int char, FILE *stream);
int fgetc(FILE *stream);
#include <stdio.h>
int main() {
FILE *file = fopen("chars.txt", "w+");
if (file == NULL) {
perror("Error opening file");
return -1;
}
// 写入字符
fputc('A', file);
fputc('B', file);
rewind(file);
// 读取字符
char ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch); // 输出到屏幕
}
fclose(file);
return 0;
}
5. fread
和 fwrite
用于读写二进制数据。
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
#include <stdio.h>
#include <string.h>
int main() {
FILE *file = fopen("binary.dat", "wb+");
if (file == NULL) {
perror("Error opening file");
return -1;
}
// 写入二进制数据
int numbers[] = {1, 2, 3, 4, 5};
fwrite(numbers, sizeof(int), 5, file);
rewind(file);
// 读取二进制数据
int buffer[5];
fread(buffer, sizeof(int), 5, file);
for (int i = 0; i < 5; i++) {
printf("%d ", buffer[i]);
}
printf("\n");
fclose(file);
return 0;
}
6. fflush
刷新缓冲区,将缓冲区内容写入文件。
int fflush(FILE *stream);
#include <stdio.h>
int main() {
FILE *file = fopen("flush_example.txt", "w");
if (file == NULL) {
perror("Error opening file");
return -1;
}
fprintf(file, "Buffered text");
fflush(file); // 立即写入文件
fclose(file);
return 0;
}
7. remove
和 rename
用于删除文件或重命名文件。
int remove(const char *filename);
int rename(const char *oldname, const char *newname);
#include <stdio.h>
int main() {
FILE *file = fopen("temp.txt", "w");
if (file == NULL) {
perror("Error creating file");
return -1;
}
fclose(file);
// 重命名文件
if (rename("temp.txt", "renamed.txt") == 0) {
printf("File renamed successfully.\n");
} else {
perror("Error renaming file");
}
// 删除文件
if (remove("renamed.txt") == 0) {
printf("File deleted successfully.\n");
} else {
perror("Error deleting file");
}
return 0;
}
8. feof
和 ferror
用于检查文件流的状态。
int feof(FILE *stream); // 检查文件结束标志
int ferror(FILE *stream); // 检查文件错误标志
#include <stdio.h>
int main() {
FILE *file = fopen("nonexistent.txt", "r");
if (file == NULL) {
perror("Error opening file");
return -1;
}
char ch;
while ((ch = fgetc(file)) != EOF) {
putchar(ch);
}
if (feof(file)) {
printf("End of file reached.\n");
}
if (ferror(file)) {
perror("File error occurred");
}
fclose(file);
return 0;
}
9. setbuf
和 setvbuf
用于设置文件流的缓冲区。
void setbuf(FILE *stream, char *buffer);
int setvbuf(FILE *stream, char *buffer, int mode, size_t size);
#include <stdio.h>
int main() {
FILE *file = fopen("buffer_example.txt", "w");
if (file == NULL) {
perror("Error opening file");
return -1;
}
char buffer[1024];
setbuf(file, buffer); // 设置缓冲区
fprintf(file, "Buffered data\n");
fflush(file); // 强制写入
fclose(file);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
char buff[1024];
memset( buff, '\0', sizeof( buff ));
fprintf(stdout, "启用全缓冲\n");
setvbuf(stdout, buff, _IOFBF, 1024);
fprintf(stdout, "这里是 runoob.com\n");
fprintf(stdout, "该输出将保存到 buff\n");
fflush( stdout );
fprintf(stdout, "这将在编程时出现\n");
fprintf(stdout, "最后休眠五秒钟\n");
sleep(5);
return(0);
}