当前位置: 首页 > article >正文

IO进程 day01

IO进程

  • 1. 课程介绍
  • 2. IO
  • 3. 标准IO和文件IO
    • 3.1. 标准IO
    • 3.2. 文件IO
    • 3.3. 标准IO的调用逻辑
    • 3.4. 缓存机制
  • 5. 标准IO
    • 5.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/fclose
      • 5.5.2. fgetc/fputc
      • 5.5.3.fgets/fputs
      • 5.5.4. perror
      • 5.5.5. fprintf
  • #练习一
  • #练习二
    • time
    • localtime

1. 课程介绍

  1. IO
    文件IO
    标准IO
    目录IO
    库:静态库,动态库
  2. 进程
    进程:进程,进程通信
    线程:线程,线程通信

2. IO

对文件进行输入输出操作,打开文件——读文件——写文件
I:input——输入——写文件
O: output——输出——读文件
文件放在磁盘空间,断电不丢失

bcd-lsp文件类型
b块设备文件
c字符设备文件
d目录文件
普通文件
L链接文件
s套接字文件
P管道文件

3. 标准IO和文件IO

在这里插入图片描述

3.1. 标准IO

  1. 由C库定义的标准输入输出函数,不同的操作系统只要有C库,就可以直接用,可移植性强
  2. C库定义时调用文件IO,在C库调用标准IO之前,由C库判断操作系统,从而调用相应的文件IO

3.2. 文件IO

  1. 内核层向应用层提供的输入输出函数接口,由内核提供。
  2. 内核不同文件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/fputc

fgetc

#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/fputs

fgets

#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);

 功能:从文件中获取指定长度的字符串
 参数:
  s:字符串存放首地址
  size:期望获取的字符个数
  stream:流指针,指向目标文件
 返回值:
  成功:返回获取的字符串首地址
  失败或读到文件结尾返回NULL
注意事项

  1. fgets获取到字符串之后会自动补’\0’,所以实际获取的字符个数为size-1
  2. 文件中不满size-1个字符,有多少字符读多少字符,最后自动补’\0’
  3. 当读取到’\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);
    }

http://www.kler.cn/a/553148.html

相关文章:

  • MySQL 安装过程记录以及安装选项详解
  • 寒假总结。
  • 基于Java(JSP)+MySQL设计与实现的 MVC 鲜花订购系统
  • “以数治税”时代 数据要素的价值挖掘
  • 昇腾DeepSeek模型部署优秀实践及FAQ
  • 图解长短期记忆网络(LSTM)
  • Yocto项目:如何部署AI——完整指南*
  • 基于开源Odoo、SKF Phoenix API与IMAX-8数采网关的圆织机设备智慧运维实施方案 ——以某纺织集团圆织机设备管理场景为例
  • SpringCloud面试题----什么是Feign?是如何实现负载均衡的
  • OSPF(开放路径最短优先)
  • JAX-RS与JAXB:实现XML数据交互的完整指南
  • 萌新学 Python 之 if 语句的三目运算符
  • C++ stack:数据结构的“叠盘子艺术”与“后进先出法则
  • Python 爬虫selenium
  • 细说Java 引用(强、软、弱、虚)和 GC 流程(一)
  • DeepSeek + Claude 提升效果
  • win32汇编环境,窗口程序中使用月历控件示例二
  • deepseek写的文章如何自动下载保存
  • 动态网格图片展示中的自适应逻辑
  • 基于Django快递物流管理可视化分析系统(完整系统源码+数据库+详细开发文档+万字详细论文+答辩PPT+详细部署教程等资料)