文件I/O操作代码分析
一、基础文件操作
1. 打开文件 - 01open.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (-1 == fd) {
fprintf(stderr, "open error\n");
return 1;
}
return 0;
}
知识点:
-
open():系统调用打开文件 -
flags参数:
-
O_WRONLY:只写模式 -
O_CREAT:文件不存在时创建 -
O_TRUNC:清空文件内容
-
-
mode参数 :
0666表示文件权限(用户、组、其他都可读写) -
文件描述符:成功返回非负整数,失败返回-1
2. 写入文件 - 04write.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
char str[100] = "hello";
ssize_t ret = write(fd, str, strlen(str));
printf("写入了%ld字节到文件", ret);
close(fd);
return 0;
}
知识点:
-
write(fd, buf, count):写入数据 -
strlen(str)vssizeof(str):-
strlen(str):实际字符串长度(不包括'\0') -
sizeof(str):数组总大小(100字节)
-
-
ssize_t:有符号整数类型,表示实际写入字节数 -
必须调用
close(fd)释放资源
3. 读取文件 - 05read.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd = open("/etc/passwd", O_RDONLY);
char buf[50] = {0};
while (1) {
bzero(buf, sizeof(buf)); // 清空缓冲区
int ret = read(fd, buf, sizeof(buf)-1); // 留一个位置给'\0'
if (ret <= 0) break;
printf("[%d]:{%s}", ret, buf);
}
close(fd);
return 0;
}
知识点:
-
read(fd, buf, count):读取数据 -
缓冲区清空:
-
bzero(buf, sizeof(buf)):将内存清零 -
memset(buf, 0, sizeof(buf)):功能相同
-
-
读取策略 :
sizeof(buf)-1保留一个字节给字符串结束符 -
返回值:
-
>0:实际读取字节数 -
=0:文件结束 -
<0:错误
-
4. 文件复制 - 06read_cp.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (argc < 3) {
printf("usage: ./a.out srcfile dstfile\n");
}
int fd_src = open(argv[1], O_RDONLY);
int fd_dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);
while (1) {
char buffer[1024] = {0};
int ret = read(fd_src, buffer, sizeof(buffer));
if (ret <= 0) break;
write(fd_dst, buffer, ret);
}
close(fd_dst);
close(fd_src);
return 0;
}
知识点:
-
命令行参数 :
argv[1]源文件,argv[2]目标文件 -
分块读取:循环读取直到文件结束
-
精确写入 :
write(fd_dst, buffer, ret)只写入实际读取的字节 -
资源管理:最后关闭两个文件描述符
二、高级文件操作
1. 文件插入 - 02insert_file.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
FILE* fp = fopen("1.txt", "r+");
// 获取文件大小
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
int pos = 15;
char insert_str[100] = "hello";
// 保存后半部分内容
fseek(fp, pos, SEEK_SET);
char *data = (char*)malloc(size);
fread(data, size-pos, 1, fp);
// 插入内容
fseek(fp, pos, SEEK_SET);
fwrite(insert_str, strlen(insert_str), 1, fp);
fwrite(data, size-pos, 1, fp);
fclose(fp);
free(data);
return 0;
}
知识点:
-
标准I/O :使用
fopen()、fread()、fwrite() -
文件定位:
-
fseek(fp, 0, SEEK_END):移动到文件末尾 -
ftell(fp):获取当前位置(即文件大小)
-
-
插入策略:
-
读取插入点之后的内容到内存
-
在插入点写入新内容
-
追加原来的后半部分内容
-
-
内存管理 :使用
malloc()动态分配,最后free()
2. 文件定位 - 08lseek.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd = open("1.txt", O_RDWR);
// 获取文件大小
long size = lseek(fd, 0, SEEK_END);
printf("size %ld\n", size);
// 移动到指定位置并写入
lseek(fd, 1024*1024, SEEK_SET);
char str[] = "老孙到此一游";
write(fd, str, strlen(str));
close(fd);
return 0;
}
知识点:
-
lseek(fd, offset, whence):移动文件指针-
SEEK_SET:从文件开头 -
SEEK_CUR:从当前位置 -
SEEK_END:从文件末尾
-
-
空洞文件:跳过大量字节后写入,中间部分会被填'\0'
-
文件大小 :使用
lseek(fd, 0, SEEK_END)获取文件大小
三、标准输入输出
07stdin.c
cpp
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char buf[10] = {0};
printf("pls input num:");
fflush(stdout); // 强制刷新缓冲区
read(0, buf, sizeof(buf)); // 从标准输入读取
int num = atoi(buf);
write(2, &num, 4); // 写入标准错误
return 0;
}
知识点:
-
标准文件描述符:
-
0:标准输入(stdin) -
1:标准输出(stdout) -
2:标准错误(stderr)
-
-
缓冲机制:
-
printf()输出到缓冲区,需要刷新才能显示 -
fflush(stdout)强制刷新输出缓冲区
-
-
字符串转换 :
atoi()将字符串转换为整数 -
系统调用读写 :
read()/write()也可以操作标准输入输出
四、应用实例
字典程序 - 03dict.c
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
typedef struct {
char word[50];
char mean[512];
struct list_head node;
} DATATYPE;
int main(int argc, char** argv)
{
// 1. 打开字典文件
FILE* fp = fopen("/home/linux/dict.txt", "r");
// 2. 初始化链表
struct list_head dict_head;
INIT_LIST_HEAD(&dict_head);
// 3. 读取并存储字典数据
while (1) {
char str[1024] = {0};
if (NULL == fgets(str, sizeof(str), fp)) break;
char* word = strtok(str, " ");
char* mean = strtok(NULL, "\r");
add_word(&dict_head, word, mean);
}
// 4. 用户查询交互
while (1) {
char want_word[50] = {0};
printf("pls input want_word:");
fgets(want_word, sizeof(want_word), stdin);
want_word[strlen(want_word) - 1] = '\0'; // 去掉换行符
if (0 == strcmp(want_word, "#quit")) break;
DATATYPE* tmp = find_word(&dict_head, want_word);
if (NULL == tmp) {
printf("cant find word:%s\n", want_word);
} else {
printf("word:%s mean:%s\n", tmp->word, tmp->mean);
}
}
return 0;
}
核心知识点:
-
文件读取:
-
fgets(str, sizeof(str), fp):安全读取一行 -
检查
NULL判断文件结束// 原始格式:单词 解释\r\n
-
char* word = strtok(str, " "); // 第一个空格前是单词
char* mean = strtok(NULL, "\r"); // 到\r前是解释
fgets(want_word, sizeof(want_word), stdin);
want_word[strlen(want_word) - 1] = '\0'; // 去掉末尾的换行符
-
链表操作:
-
list_add(&p->node, head):添加到链表 -
list_for_each_entry_safe():安全遍历链表
-
五、重要总结
1. 系统调用 vs 标准I/O
| 操作 | 系统调用 | 标准I/O |
|---|---|---|
| 打开 | open() |
fopen() |
| 读取 | read() |
fread() |
| 写入 | write() |
fwrite() |
| 关闭 | close() |
fclose() |
| 定位 | lseek() |
fseek() |
2. 常用文件打开模式
| 模式 | 说明 |
|---|---|
O_RDONLY |
只读 |
O_WRONLY |
只写 |
O_RDWR |
读写 |
O_CREAT |
不存在时创建 |
O_TRUNC |
清空文件 |
O_APPEND |
追加模式 |
3. 错误处理模式
int fd = open("file.txt", O_RDONLY);
if (-1 == fd) {
perror("open failed"); // 自动添加错误信息
// 或
fprintf(stderr, "open error: %s\n", strerror(errno));
return 1;
}
4. 内存与文件操作注意事项
-
缓冲区管理:读取前清空,写入时注意长度
-
资源释放:打开后必须关闭,分配后必须释放
-
错误检查:每个系统调用都可能失败
-
边界检查:防止缓冲区溢出
-
文件权限:创建文件时指定合适的权限