文件I/O操作代码分析
一、基础文件操作
1. 打开文件 -01open.c
#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
#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
#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
#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
#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
#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
#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
#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. 内存与文件操作注意事项
缓冲区管理:读取前清空,写入时注意长度
资源释放:打开后必须关闭,分配后必须释放
错误检查:每个系统调用都可能失败
边界检查:防止缓冲区溢出
文件权限:创建文件时指定合适的权限