IO进程day01
- 人工智能
- 2025-08-27 23:42:01

IO进程 1. 课程介绍2. IO3. 标准IO和文件IO3.1. 标准IO3.2. 文件IO3.3. 标准IO的调用逻辑3.4. 缓存机制 5. 标准IO5.1. 标准IO的特点5.2. 流5.2.1. 定义5.2.2. 分类5.2.3. 流指针 FILE* 5.3. 缓存区的分类5.4. 计算缓存区大小5.4.1. 计算5.4.2. fflush 5.5. 标准IO的函数接口(上)5.5.1. fopen/fclose5.5.2. fgetc/fputc5.5.3.fgets/fputs5.5.4. perror5.5.5. fprintf #练习一#练习二timelocaltime 1. 课程介绍 IO 文件IO 标准IO 目录IO 库:静态库,动态库进程 进程:进程,进程通信 线程:线程,线程通信 2. IO
对文件进行输入输出操作,打开文件——读文件——写文件 I:input——输入——写文件 O: output——输出——读文件 文件放在磁盘空间,断电不丢失
bcd-lsp文件类型b块设备文件c字符设备文件d目录文件–普通文件L链接文件s套接字文件P管道文件 3. 标准IO和文件IO 3.1. 标准IO 由C库定义的标准输入输出函数,不同的操作系统只要有C库,就可以直接用,可移植性强C库定义时调用文件IO,在C库调用标准IO之前,由C库判断操作系统,从而调用相应的文件IO 3.2. 文件IO 内核层向应用层提供的输入输出函数接口,由内核提供。内核不同文件IO不同,不同内核不可通用,可移植性差。 3.3. 标准IO的调用逻辑标准IO由C库提供,C库封装相应函数时,通过封装内核层的文件IO来进行调用。调用标准IO时,C库中有判断操作系统的代码块,以此来保证高可移植性的特点
if(Linux操作系统) { 调用Linux的系统函数(文件IO) } if(Windows操作系统) { 调用Windows的系统函数(文件IO) } if(macos操作系统) { 调用macos的系统函数(文件IO) } 3.4. 缓存机制缓存区只有在使用时才会被开辟
没有缓存机制的系统调用: 应用层读写文件——调用内核层函数接口——内核层与硬件层交互——拿到数据返回给应用层——每次读写重复操作
有缓存机制的系统调用: 应用层读写文件——调用内核层函数接口——内核层与硬件层交互——拿到数据返回应用层的缓存区——每次读写从缓存区拿数据
5. 标准IO 5.1. 标准IO的特点1. 标准 IO 是 C 库中提供的一组专门用于输入输出的函数接口 2. 标准 IO 由ANSI C 标准定义,不仅在Unix系统上,在很多操作系统上都实现了标准IO——可移植性高 3. 标准 IO 通过缓存机制减少系统调用次数,提高效率 4. 标准 IO 默认打开三个流,标准输入(stdin),标准输出(stdout),标准出错(stderr) 5. 标准 IO 库的所有操作都是围绕流(stream)来进行的,在标准IO中,流用FILE *来描述
5.2. 流 5.2.1. 定义所有的IO操作仅是简单的从程序移进或者移出,这种字节流,就称为流。
5.2.2. 分类文本流/二进制流
5.2.3. 流指针 FILE*FILE* 类型是FILE结构体的结构体指针,FILE结构体由标准库定义,用于表示一个打开的文件或输入输出流
查看结构体:vi -t FILE 查找的是FILE是结构体名,所以输入1,回车 追踪:ctrl + ] 返回追踪:ctrl + [ FILE是_IO_FILE结构体重定义的名,所以追踪_IO_FILE,然后找定义,所以输入1,回车
定义注释_IO_read_ptr读取地址_IO_read_end读取结束后地址_IO_read_base读取起始地址_IO_write_base写入起始地址_IO_write_ptr写入地址_IO_write_end写入结束后地址_IO_buf_base缓存起始地址_IO_buf_end缓存结束后地址 5.3. 缓存区的分类1. 全缓存:基于文件 刷新缓存: 1. 程序正常退出: return,exit,fclose(关闭流指针) 2. fflush:强制刷新缓存区 3. 缓存区满 2. 行缓存:基于终端:stdin/stdout 刷新缓存: 1. 程序正常退出: return,exit,fclose(关闭流指针) 2. fflush:强制刷新缓存区 3. 缓存区满 4. 换行 3. 不缓存:stderr
5.4. 计算缓存区大小 5.4.1. 计算 stdout->_IO_buf_end - stdout->_IO_buf_base; // 高地址 - 低地址 5.4.2. fflush #include <stdio.h> int fflush(FILE* stream);功能:强制刷新缓存区 参数:流指针,NULL刷新所有流 返回值:成功返回0 不成功返回-1(EOF),更新errno
5.5. 标准IO的函数接口(上) 函数函数功能fopen/fclose打开/关闭文件fgetc/fputc读/写一个字符fgets/fputs读/写一个字符串fread/fwrite读/写一个二进制文件fseek移动文件指针 5.5.1. fopen/fclose #include <stdio.h> FILE *fopen(const char *path, const char *mode);功能:打开文件 参数: path:打开文件的路径 mode:打开方式
打开方式权限功能r只读文件指针定位到文件开头(有文件)r+可读可写文件指针定位到文件开头(有文件)w只写文件不存在则创建,文件存在则清空,文件指针定位到文件开头w+可读可写文件不存在则创建,文件存在则清空,文件指针定位到文件开头a只写文件不存在则创建,文件存在则追加,文件指针定位到文件末尾a+可读可写文件不存在则创建,文件存在则追加,文件指针定位到文件末尾返回值: 成功:返回文件流指针 失败:返回NULL,更新errno 一个任务中最多打开1024个文件,一个文件可以被重复打开,打开文件是有限资源,使用结束之后及时关闭
#include <stdio.h> int fclose(FILE *stream);功能:关闭文件 参数:流指针,指向打开的文件 返回值:成功0,失败EOF且更新errno 示例:
#include <stdio.h> int main () { FILE* fp = fopen("./test.c", "w"); if(fp == NULL) { perror("open err"); return -1; } printf("open success\n"); fclose(fp); if(fp != NULL) { perror("close err"); return -1; } printf("close succes\n"); return 0; } 5.5.2. fgetc/fputcfgetc
#include <stdio.h> int fgetc(FILE *stream);功能:从文件中读取一个字符 参数:流指针,指向目标文件 返回值: 成功:返回读取到的字符的ASCII值 失败:读取到文件末尾或读取失败返回-1 fputc
#include <stdio.h> int fputc(int c, FILE *stream);功能:向文件中写入一个字符 参数: c:写入的字符的ASCII值 stream:流指针,指向要写入的文件 返回值: 成功:返回写入的ASCII值 失败:返回EOF,更新errno
练习 使用fgetc和fputc实现将文件1(file1)中的内容复制到文件2(file2)里面
#include <stdio.h> int main () { // 打开两个文件 // 只读打开文件1,因为要读取文件1的内容 FILE* fp1 = fopen("./file1.c", "r"); if(fp1 == NULL) { perror("open file1 err"); return -1; } // 只写打开文件2,因为只需要向文件2中写入内容 FILE* fp2 = fopen("./file2.c", "w"); if(fp2 == NULL) { perror("open file2 err"); return -1; } // 循环读取文件1 的字符,写入文件2中 int ch = 0; while(ch = fgetc(fp1) != -1) fputc(ch,fp2); // 关闭文件 fclose(fp1); fclose(fp2); } 5.5.3.fgets/fputsfgets
#include <stdio.h> char *fgets(char *s, int size, FILE *stream);功能:从文件中获取指定长度的字符串 参数: s:字符串存放首地址 size:期望获取的字符个数 stream:流指针,指向目标文件 返回值: 成功:返回获取的字符串首地址 失败或读到文件结尾返回NULL 注意事项
fgets获取到字符串之后会自动补’\0’,所以实际获取的字符个数为size-1文件中不满size-1个字符,有多少字符读多少字符,最后自动补’\0’当读取到’\n’时,停止读取内容,再次调用fgets之后从下一行起始位置开始继续读取fputs
#include <stdio.h> int fputs(const char *s, FILE *stream);功能:向指定文件中输入字符串 参数: s:输入字符串的首地址 stream:指向目标文件 返回值: 成功返回输入字符个数 失败返回EOF
5.5.4. perror #incldue <errno.h> void perror(const char *s);功能:根据errno获取错误信息,将信息输出到终端 参数:s:提示内容 返回值:无
5.5.5. fprintf #include <stdio.h> int fprintf(FILE *stream, const char *format, ...);功能:向指定文件中以指定的格式写入数据 参数: stream:流指针,指向目标文件 format:指定格式 …:多个参数 返回值: 成功返回输出字符个数 失败返回EOF 示例1 在printf的基础上多了一个指向文件的参数
int age = 15; char* name = "Zhangsan"; double height = 5.9; fprintf(file, "name is %s\n", name); fprintf(file, "age is %d\n", age); fprintf(file, "height is %.1f", height);示例2
#include <stdio.h> int main () { // 打开一个文件 FILE* fp = fopen("./file.txt", "w"); // 容错 if(fp == NULL) { perror("open file.txt err"); return -1; } // 向打开的文件中输入内容 fprintf(file.txt,"hello world\n"); // 关闭文件 fclose(file.txt); } #练习一用fgetc和fputc实现cat -n file的功能,从命令行输入文件名
// 用fgetc和fputc实现cat -n file的功能,从命令行输入文件名 #include <stdio.h> int main(int argc, char const *argv[]) { FILE *fp = NULL; // 定义流指针,指向目标文件 int n = 1; // 行号,从1开始 char ch = 0; // 获取从文件中获得的字符,方便输出和判断换行 int flag = 1; // 作为一个从新一行的开始读取的标志,文件读取到新一行的开始时置1 // 命令行传参判错 if (argc != 2) { perror("argc err"); return EOF; } // 打开文件 fp = fopen(argv[1], "r"); // 容错判断 if (fp == NULL) { perror("open err"); return EOF; } // 循环输出文件内容 while (1) // 死循环一直输出 { // 获取字符 ch = fgetc(fp); // 文件内容打印完成之后结束循环 if (ch == EOF) break; // 判断是否需要打印行号 if (flag == 1) // flag作为新一行的标志,为1时说明是新的一行 { printf("%-4d", n++); // ++在后先输出后自加 flag = 0; // 新的一行开始后flag置0,说明后面的字符不是新的一行,不需要打印行数 } // 判断获取到的字符是否为\n if (ch == '\n') flag = 1; // 读取到\n说明这个字符打印之后,就是新的一行,标志变量置1,表示后面要换行了 // 输出字符 fputc(ch, stdout); // stdout表示向终端输入 } // 关闭文件 fclose(fp); return 0; } #练习二编程读写一个文件test.txt,每隔1秒向文件中写入一行数据,类似这样:
1, 2007-7-30 15:16:42 2, 2007-7-30 15:16:43
该程序应该无限循环,直到按Ctrl+C中断程序。 再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的序号,比如:
1, 2007-7-30 15:16:42 2, 2007-7-30 15:16:43 3, 2007-7-30 15:19:02 4, 2007-7-30 15:19:03 5, 2007-7-30 15:19:04
time #include <time.h> time_t time(time_t *tloc);功能:获取1970-01-01 00:00:00到现在的秒数 参数:tloc:保存获取到的时间的地址 返回值: 成功返回获取到的时间 失败返回-1
localtime #include <time.h> struct tm *localtime(const time_t *timep);功能:将time获取的时间转换成结构体中的格式 参数:timep保存时间变量的地址 返回值: 成功返回结构体地址 失败返回NULL
struct tm { int tm_sec; // seconds(0-60) int tm_min; // minutes(0-59) int tm_hour; // Hours(0-23) int tm_yday; // day in the year(0-365, 1 Jan = 0) int tm_wday; // day in the week (0-6, sunday = 0) int tm_mday; // day in the month (1-31) int tm_mon; // month(0-11) int tm_year; // year-1900 } #include <stdio.h> #include <time.h> #include <string.h> #define N 64 int main(int argc, char const *argv[]) { // 定义变量 FILE *fp = NULL; // 指向文件 time_t t = 0; // 获取当前时间(秒) struct tm *tm = NULL; // 获取当前的时间 int n = 1; // 行号 char ch[N] = {}; // 计算行数时获取字符串判断\n // 打开文件 fp = fopen("./file1.txt", "a+"); // 计算行数 while (fgets(ch,64,fp) != NULL) // fgets获取到文件结尾后会返回NULL,然后循环结束 if (ch[strlen(ch)-1 == '\n']) // strlen不算\0,strlen是长度,长度-1是最后一个元素 n++; while (1) { // 获取时间 time(&t); tm = localtime(&t); // 向终端和文件输入时间 fprintf(fp, "%-4d%d-%d-%d %d:%d:%d\n", n, tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); printf("%-4d%d-%d-%d %d:%d:%d\n", n, tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); n++; // 刷新缓存 fflush(NULL); // 步长为1秒 sleep(1); }