应用——Linux 标准IO编程

Linux 标准IO编程

一、基础文件操作

1. 文件打开/关闭 (01fopen.c)

复制代码
// 文件打开模式: "r"(读), "w"(写), "a"(追加)
// "r+", "w+", "a+" (读写模式)
FILE* fp = fopen("1.txt", "w");
if(NULL == fp) {  // 使用 NULL == fp 防止误写为 fp = NULL
    printf("fopen error\n");
    return 1;
}

// 单个字符写入
fputc('h', fp);  // 写入字符'h'

// 关闭文件
fclose(fp);  // 必须关闭,释放资源

2. 字符读写 (02fgetc.c, 03fgetc_cp.c)

复制代码
// 读取字符(返回int,因为EOF=-1)
int c = fgetc(fp);
while(1) {
    c = fgetc(fp);
    if(EOF == c) {  // 到达文件结尾
        break;
    }
    printf("%c", c);  // 输出字符
    // fputc(c, fp_dst);  // 复制到目标文件
}

二、字符串读写

3. 字符串写入 (05fputs.c)

复制代码
char str[100] = "hello";
fputs(str, fp);     // 写入字符串(不自动加\n)
fputs("world!\n", fp);  // 可以写入字面量

4. 字符串读取 (06fgets.c, 13stdin.c)

复制代码
// fgets特点:读取一行,包含换行符,自动加'\0'
char buf[100] = {0};
while(1) {
    if(NULL != fgets(buf, sizeof(buf), fp)) {
        printf("%s", buf);  // buf中包含\n
    } else {
        break;  // 读取失败或到达文件尾
    }
}

// 处理输入中的换行符
char name[50] = {0};
fgets(name, sizeof(name), stdin);  // 从标准输入读取
name[strlen(name)-1] = '\0';  // 去掉换行符

三、命令行参数处理

5. 参数基础 (04argc.c)

复制代码
// 示例: ./a.out 1 2 3 4 5 6
// argc = 7 (程序名 + 6个参数)
// argv[0] = "./a.out"
// argv[1] = "1"
printf("argc is %d\n", argc);
for(int i = 0; i < argc; i++) {
    printf("%d %s\n", i, argv[i]);
}

6. 参数验证 (07zuoye1.c)

复制代码
if(argc < 2) {  // 至少需要一个文件名参数
    printf("usage: ./a.out filename\n");
    return 1;
}

四、文件复制方法对比

7. 逐字符复制 (03fgetc_cp.c)

复制代码
// 最慢但最简单
while(1) {
    int c = fgetc(fp_src);
    if(EOF == c) break;
    fputc(c, fp_dst);
}

8. 逐行复制 (08fget_cp.c)

复制代码
// 适合文本文件
while(1) {
    char str[1024] = {0};
    if(NULL == fgets(str, sizeof(str), fp_src)) {
        break;
    }
    fputs(str, fp_dst);
}

9. 块复制 (12fread_cp.c)

复制代码
// 最快,适合二进制文件
char str[1024] = {0};
while(1) {
    size_t ret = fread(str, 1, sizeof(str), fp_src);
    if(0 == ret) {  // fread返回实际读取的字节数
        break;
    }
    fwrite(str, ret, 1, fp_dst);  // 写入实际读取的字节数
}

五、结构体读写

10. 结构体定义

复制代码
typedef struct {
    char name[50];
    int age;
    char address[30];
} PER;

11. 写入结构体 (10fwrite.c)

复制代码
PER per = {"zhangsan", 20, "科技二路"};
// 方式1:按结构体数量写入
fwrite(&per, sizeof(per), 1, fp);  // 写入1个结构体

// 方式2:按字节写入
fwrite(&per, 1, sizeof(per), fp);  // 写入sizeof(per)个字节

12. 读取结构体 (11fread.c)

复制代码
PER per = {0};

// 方式1:按结构体数量读取
size_t ret = fread(&per, sizeof(per), 1, fp);
// ret = 成功读取的结构体数量(0或1)

// 方式2:按字节读取
size_t ret = fread(&per, 1, sizeof(per), fp);
// ret = 成功读取的字节数
printf("ret is %lu, name:%s age:%d addr:%s\n", 
       ret, per.name, per.age, per.address);

六、文件指针操作

13. 文件定位 (17fseek.c, 18ftell.c)

复制代码
// 三个定位宏:
// SEEK_SET - 文件开头
// SEEK_CUR - 当前位置
// SEEK_END - 文件末尾

// 移动到文件开头后19字节处
int ret = fseek(fp, 19, SEEK_SET);
if(ret != 0) {  // fseek成功返回0
    fprintf(stderr, "fseek error\n");
}

// 获取文件大小
fseek(fp, 0, SEEK_END);  // 移动到文件末尾
long size = ftell(fp);    // 返回当前位置(即文件大小)
printf("size is %ld\n", size);

// 重置到文件开头
rewind(fp);  // 相当于 fseek(fp, 0, SEEK_SET)

七、缓冲区类型

14. 行缓冲 (14linebuff.c)

复制代码
// stdout默认行缓冲(大小1024字节)
// 刷新条件:
// 1. 遇到'\n'
printf("hello\n");  // 立即输出

// 2. 缓冲区满(1024字节)
for(int i = 0; i < 1024; i++) {
    fputc('a', stdout);  // 缓冲区满时输出
}

// 3. 程序结束
printf("hello");  // 程序结束时输出

// 4. 手动刷新
printf("hello");
fflush(stdout);  // 立即输出

15. 全缓冲 (15fullbuf.c)

复制代码
// 文件操作默认全缓冲(大小4096字节)
FILE* fp = fopen("1.txt", "w");

// 刷新条件:
// 1. 缓冲区满(4096字节)
for(int i = 0; i < 4096; i++) {
    fputc('a', fp);  // 缓冲区满时写入文件
}

// 2. 程序结束
fputs("hello", fp);  // 程序结束时写入

// 3. 手动刷新
fputs("hello", fp);
fflush(fp);  // 立即写入文件

// 4. 关闭文件
fclose(fp);  // 会刷新缓冲区

16. 无缓冲 (16stderr.c)

复制代码
// stderr默认无缓冲(立即输出)
fprintf(stderr, "fopen error");  // 立即输出错误信息
// 适合输出错误信息,确保及时看到

八、实用工具函数

17. 统计小写字母 (09count_little.c)

复制代码
int a[26] = {0};  // 26个小写字母的计数数组
while(1) {
    int c = fgetc(fp);
    if(EOF == c) break;
    
    if(c >= 'a' && c <= 'z') {
        a[c - 'a']++;  // 对应字母计数加1
    }
}

// 输出结果
for(int i = 0; i < 26; i++) {
    printf("%c:%d\n", i + 'a', a[i]);
}

18. 统计文件行数 (07count_lin.c)

复制代码
int count = 0;
while(1) {
    char str[100] = {0};
    if(NULL == fgets(str, sizeof(str), fp)) {
        break;
    }
    if('\n' == str[strlen(str)-1]) {  // 判断是否有换行符
        count++;
    }
}
printf("line num:%d\n", count);

九、三个标准流

19. 标准流指针

复制代码
stdin  - FILE*,标准输入(键盘),文件描述符 0
stdout - FILE*,标准输出(屏幕),文件描述符 1
stderr - FILE*,标准错误(屏幕),文件描述符 2

// 示例:从键盘读取,输出到屏幕
fgets(name, sizeof(name), stdin);
fputs(name, stdout);
fputs(name, stderr);  // 立即输出错误信息

十、最佳实践总结

20. 编程模式

复制代码
// 1. 打开文件并检查
FILE* fp = fopen(filename, mode);
if(NULL == fp) {
    perror("fopen error");  // 使用perror显示系统错误
    return 1;
}

// 2. 错误处理使用fprintf(stderr)
fprintf(stderr, "Error: %s\n", error_msg);

// 3. 文件指针操作后检查返回值
if(ret != 0) {
    fprintf(stderr, "operation failed\n");
}

// 4. 确保关闭所有打开的文件
fclose(fp);

// 5. 命令行参数验证
if(argc < expected_args) {
    printf("Usage: %s <args>\n", argv[0]);
    return 1;
}

21. 性能建议

  1. 小文件:可以使用fgetc/fputc逐字符处理

  2. 文本文件:使用fgets/fputs逐行处理

  3. 二进制文件/大文件:使用fread/fwrite块处理

  4. 结构体数据:使用fwrite/fread整体读写

22. 常见错误

  1. 忘记检查fopen返回值

  2. 忘记关闭文件(fclose)

  3. fgets后不处理换行符

  4. 缓冲区大小不足导致溢出

  5. 混淆"r"和"w"模式

相关推荐
一颗青果10 分钟前
HTTP协议详解
linux·网络·网络协议·http
安当加密3 小时前
MySQL 数据库如何加密脱敏?TDE透明加密 + DBG数据库网关 双引擎加固实战
数据库·mysql·adb
IT技术分享社区3 小时前
MySQL统计查询优化:内存临时表的正确打开方式
数据库·mysql·程序员
短剑重铸之日3 小时前
7天读懂MySQL|Day 5:执行引擎与SQL优化
java·数据库·sql·mysql·架构
Light603 小时前
CSS逻辑革命:原生if()函数如何重塑我们的样式编写思维
前端·css·响应式设计·组件化开发·css if函数·声明式ui·现代css
广州灵眸科技有限公司3 小时前
瑞芯微(EASY EAI)RV1126B CAN使用
linux·网络·单片机·嵌入式硬件
蜡笔小嘟3 小时前
宝塔安装dify,更新最新版本--代码版
前端·ai编程·dify
长安er3 小时前
LeetCode215/347/295 堆相关理论与题目
java·数据结构·算法·leetcode·
元亓亓亓3 小时前
LeetCode热题100--62. 不同路径--中等
算法·leetcode·职场和发展
好记忆不如烂笔头abc4 小时前
RECOVER STANDBY DATABASE FROM SERVICE xxx,ORA-19909
数据库