目录
一、C 标准库文件操作
1. 文件的写入
以下是一个使用 C 标准库函数 fwrite
写入文件的示例:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("myfile", "w"); // 以写模式打开文件
if (!fp)
{
printf("fopen error!\n");
return 1;
}
const char *msg = "hello bit!\n";
int count = 5;
while (count--)
{
fwrite(msg, strlen(msg), 1, fp); // 将字符串写入文件
}
fclose(fp); // 关闭文件
return 0;
}
-
fopen
函数用于打开或创建文件。"w"
模式表示以写模式打开文件,如果文件不存在则创建一个新文件,如果文件存在则清空其内容。 -
fwrite
函数将指定内存区域的内容写入文件。第一个参数是内存区域的指针,第二个参数是每个元素的大小,第三个参数是元素的数量,第四个参数是文件指针。 -
fclose
函数用于关闭文件,释放与文件相关的资源。
2. 文件的读取
以下是一个使用 C 标准库函数 fread
读取文件的示例:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("myfile", "r"); // 以读模式打开文件
if (!fp)
{
printf("fopen error!\n");
return 1;
}
char buf[1024];
const char *msg = "hello bit!\n";
while (1)
{
ssize_t s = fread(buf, 1, strlen(msg), fp); // 从文件中读取数据到缓冲区
if (s > 0)
{
buf[s] = 0; // 将缓冲区内容转换为字符串
printf("%s", buf);
}
if (feof(fp))
{
// 检查是否到达文件末尾
break;
}
}
fclose(fp);
return 0;
}
-
fopen
函数以读模式打开文件。 -
fread
函数从文件中读取数据到指定的缓冲区。第一个参数是缓冲区的指针,第二个参数是每个元素的大小,第三个参数是元素的数量,第四个参数是文件指针。 -
feof
函数用于检查文件是否已到达末尾。
3. 数据输出到显示器
以下是一个使用多种方法将数据输出到显示器的示例:
#include <stdio.h>
#include <string.h>
int main()
{
const char *msg = "hello fwrite\n";
fwrite(msg, strlen(msg), 1, stdout); // 使用 fwrite 输出到标准输出
printf("hello printf\n"); // 使用 printf 输出
fprintf(stdout, "hello fprintf\n"); // 使用 fprintf 输出
return 0;
}
-
stdout
是标准输出流,指向显示器。 -
fwrite
、printf
和fprintf
都可以将数据输出到标准输出流。
4. 标准输入输出流
C 语言默认提供三个标准输入输出流:
-
stdin
:标准输入流,默认为键盘输入。 -
stdout
:标准输出流,默认为显示器输出。 -
stderr
:标准错误流,默认为显示器输出。
以下是一个示例:
#include <stdio.h>
#include <string.h>
int main()
{
char buf[1024];
ssize_t s = read(0, buf, sizeof(buf)); // 从标准输入(文件描述符0)读取
if (s > 0)
{
buf[s] = 0;
write(1, buf, strlen(buf)); // 写入到标准输出(文件描述符1)
write(2, buf, strlen(buf)); // 写入到标准错误(文件描述符2)
}
return 0;
}
-
文件描述符
0
对应stdin
。 -
文件描述符
1
对应stdout
。 -
文件描述符
2
对应stderr
。
二、C 标准库文件操作模式
C 标准库的 fopen
函数支持多种文件操作模式,以下是一些常见的模式:
模式 | 描述 |
---|---|
r | 打开纯文本文件以只读方式,文件不能不存在。 |
r+ | 打开纯文本文件用于读/写,文件不能不存在。 |
w | 打开纯文本文件用于写覆盖,若文件存在则清空,不存在则创建。 |
w+ | 打开纯文本文件用于读/写覆盖,若文件存在则清空,不存在则创建。 |
a | 打开纯文本文件用于在文件尾部追加写入。 |
a+ | 打开纯文本文件用于读/写,并在尾部追加新数据。 |
以下是一个使用 a+
模式的示例:
#include <stdio.h>
int main()
{
FILE *fp = fopen("myfile", "a+"); // 追加写入模式
if (!fp)
{
printf("fopen error!\n");
return 1;
}
const char *msg = "add new line\n";
fwrite(msg, strlen(msg), 1, fp);
fclose(fp);
return 0;
}
-
a+
模式表示以读写方式打开文件,并在文件尾部追加新数据。
三、系统调用文件操作
1. 文件的打开与描述符
以下是一个使用系统调用 open
打开文件的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
umask(0); // 设置文件权限掩码
int fd = open("myfile", O_WRONLY | O_CREAT, 0644); // 打开或创建文件
if (fd < 0)
{
perror("open");
return 1;
}
const char *msg = "hello bit!\n";
int len = strlen(msg);
write(fd, msg, len); // 写入数据到文件
close(fd); // 关闭文件
return 0;
}
-
open
函数用于打开或创建文件。第二个参数是标志位,第三个参数是文件权限。 -
O_WRONLY | O_CREAT
表示以只写方式打开文件,如果文件不存在则创建。 -
0644
是文件权限,表示文件所有者具有读写权限,组用户和其他用户具有只读权限。 -
write
函数将数据写入文件。第一个参数是文件描述符,第二个参数是数据的指针,第三个参数是数据的长度。 -
close
函数用于关闭文件描述符。
2. 文件的读取
以下是一个使用系统调用 read
读取文件的示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd = open("myfile", O_RDONLY); // 以只读模式打开文件
if (fd < 0) {
perror("open");
return 1;
}
const char *msg = "hello bit!\n";
char buf[1024];
ssize_t s = read(fd, buf, strlen(msg)); // 读取文件数据
if (s > 0) {
printf("%s", buf);
}
close(fd);
return 0;
}
-
read
函数从文件描述符中读取数据。第一个参数是文件描述符,第二个参数是数据的缓冲区,第三个参数是最大读取长度。 -
返回值
s
表示实际读取的字节数。
3. 文件操作标志
系统调用 open
的第二个参数是标志位,以下是一些常见的标志位:
标志位 | 描述 |
---|---|
O_RDONLY | 以只读方式打开文件。 |
O_WRONLY | 以只写方式打开文件。 |
O_RDWR | 以读写方式打开文件。 |
O_CREAT | 如果文件不存在则创建。 |
O_APPEND | 以追加模式写入。 |
O_TRUNC | 如果文件存在,则清空文件内容。 |
O_EXCL | 如果使用 O_CREAT 标志时文件已存在,则失败。 |
4. 文件权限
系统调用 open
的第三个参数是文件权限,使用八进制表示。以下是一些常见的权限位:
权限位 | 描述 |
---|---|
0400 | 所有者具有读权限。 |
0200 | 所有者具有写权限。 |
0100 | 所有者具有执行权限。 |
0040 | 组用户具有读权限。 |
0020 | 组用户具有写权限。 |
0010 | 组用户具有执行权限。 |
0004 | 其他用户具有读权限。 |
0002 | 其他用户具有写权限。 |
0001 | 其他用户具有执行权限。 |
5. 文件描述符
-
文件描述符是一个小整数,由操作系统分配。
-
程序默认拥有三个文件描述符:
-
0
:标准输入(stdin
)。 -
1
:标准输出(stdout
)。 -
2
:标准错误(stderr
)。
-
以下是一个使用文件描述符输出数据的示例:
#include <unistd.h>
#include <string.h>
int main()
{
char buf[] = "Hello World!\n";
write(1, buf, strlen(buf)); // 使用文件描述符1(stdout)输出
return 0;
}
-
write(1, buf, strlen(buf))
表示将数据写入到标准输出流。
文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针。本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件 。
四、C 标准库与系统调用的比较
1. 功能对比
功能 | C 标准库 | 系统调用 |
---|---|---|
文件打开 | fopen | open |
文件读取 | fread | read |
文件写入 | fwrite | write |
文件关闭 | fclose | close |
文件定位 | fseek | lseek |
标准输入输出 | stdin, stdout, stderr | 文件描述符0、1、2 |
2. 底层交互
-
C 标准库函数是封装好的函数,内部调用了系统调用。
-
例如,
fopen
是基于open
实现的。 -
fwrite
是基于write
实现的。
-
-
系统调用直接与操作系统内核交互,执行效率更高,但使用更复杂。
3. 使用场景
-
C 标准库:
-
适合快速开发和处理简单的文件操作。
-
提供了缓冲机制,数据处理更高效。
-
-
系统调用:
-
适合对底层控制要求较高的场景,如设备驱动程序、高性能文件处理等。
-
对文件描述符的直接操作,灵活性更高。
-