C语言详解(文件操作)2

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C语言

🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。


目录

  • 前言
  • 四、文件的顺序读写
    • 4.1 顺序读写函数介绍
      • 4.1.1 fputc
      • 4.1.2 fgetc
      • 4.1.3 fputs
      • 4.1.4 fgets
      • 4.1.5 fprintf
      • 4.1.6 fscanf
      • 4.1.7 sprintf(操作的不是文件)
      • 4.1.8 sscanf(操作的不是文件)
      • 4.1.9 fwrite
      • 4.1.10 fread
  • 五、文件结束的判定
    • 5.1 被错误使用的feof
    • 5.2 文本文件读取结束
    • 5.3 二进制文件读取结束
  • 六、文件缓冲区
  • 总结

前言

上篇文章中我们初步了解了文件的相关信息,文件的打开和关闭,以及文件的随机读写等
本篇文章将详细介绍一些文件顺序读写函数的作用、特点和用法,使我们更加方便地操作文件,还会讲到如何判定文件的结束等,内容可能有点多,请耐心看完哦


四、文件的顺序读写

4.1 顺序读写函数介绍

下面这些函数都在头文件<stdio.h>中定义

函数名功能适用于
fgetc字符输入函数所有输入流
fputc字符输出函数所有输出流
fgets文本行输入函数所有输入流
fputs文本行输出函数所有输出流
fscanf格式化输出函数所有输入流
fprintf格式化输出函数所有输出流
fread二进制输入文件输入流
fwrite二进制输出文件输出流

4.1.1 fputc

fputc函数的原型如下:

int fputc( int ch, FILE *stream );

fputc函数的功能是:写入字符ch到给定输出流stream

  • ch:要写入的字符
  • stream: 输出流

开始时在当前工程目录底下创建一个文本文档,存入数据:
请添加图片描述

运行下面的代码:

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写文件
	int i = 0;
	for (i = 'a'; i <= 'z'; i++)
	{
		//fputc函数一次只能写入一个字符
		fputc(i, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

运行上面的代码后查看文档,可以看到里面的内容已经被修改

请添加图片描述


4.1.2 fgetc

fgetc函数原型如下:

int fgetc( FILE *stream );
  • stream:读取字符的来源

fgetc函数读取正常时返回读取到的字符的ASCII码值,失败时返回EOF

运行下面的代码:

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	int ch = 0;//注意:为处理 EOF 需要 int 而非 char
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

运行成功,在终端上打印出 ‘a’ ~ ‘z’:

请添加图片描述

fgetcfputc适用所有输入流和所有输出流,当然包括标准输入流stdin和标准输出流stdout

#include <stdio.h>

int main()
{
	int ch = 0;
	ch = fgetc(stdin);//从键盘(标准输入流)上读取
	fputc(ch, stdout);//将字符输出(写)到屏幕(标准输出流)
	return 0;
}

请添加图片描述


4.1.3 fputs

fputs函数原型如下:

int fputs( const char *str, FILE *stream );

fputs函数的功能是:将以NULL结尾的字符串str的每个字符写入到输出流stream,如同通过重复执行fputc,不将 str 的空字符串写入

运行下面的代码:

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("Are you ok?", pf);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

运行成功后查看文档,内容已经被重写:

请添加图片描述

fputs函数在写入字符串的时候是不主动换行的

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("Are you ok?", pf);
	fputs("What can I say? man.", pf);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述


4.1.4 fgets

fgets函数原型如下:

char* fgets( char *str, int count, FILE *stream );
  • str:指向char型数组元素的指针
  • count:写入的最大字符数(典型的为 str 的长度)
  • stream:读取数据来源的文件流

fgets函数的返回值:成功时为str,失败时为NULL

fgets函数的作用:

  • 从给定文件流读取最多count-1个字符并将它们存储于str所指向的字符数组
  • 若文件尾出现或发现换行符则终止分析,后一情况下 str 将包含一个换行符
  • 若读入字符且无错误发生,则紧随str的最后一个字符后写入空字符'\0'

test.txt文档中的内容改为“abcdefghijklmnopq”:

在这里插入图片描述

调试下面的代码:

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char str[20] = "xxxxxxxxxxxx";
	fgets(str, 5, pf);
	printf("%s\n", str);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

可以看到,虽然函数fgets确实在数组str中存入了5个字符,但是只读取了文档test.txt实际的4个字符存入数组str中,还有一个是字符‘\0’

也就是说当参数count给的值是5的时候,实际只从文件中读取4个字符

test.txt文档中的内容改为:
在这里插入图片描述

调试下面的代码:

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char str[20] = "xxxxxxxxxxxx";
	fgets(str, 10, pf);
	printf("%s\n", str);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

可以看到,数组str中并没有存入我们预想的10个字符,所以fgets函数遇到换行符‘\n’会停止读取,并且将‘\n’也存入数组str

当然不管哪种情况最后都会补‘\0’

同样的,fgetsfputs也适用所有输入流和所有输出流,当然也包括标准输入流stdin和标准输出流stdout

#include <stdio.h>

int main()
{
	char str[20] = { 0 };
	fgets(str, 20, stdin);
	fputs(str, stdout);
	return 0;
}

在这里插入图片描述


4.1.5 fprintf

fprintf函数原型如下:

int fprintf( FILE *stream, const char *format, ... );

将结果写入输出流stream

对比printf

int printf( const char *format, ... );

将结果写入输出流stdout

可以看到fprintf函数比printf多了一个参数——文件指针
其中 ...表示可变参数列表,所以函数fprintf也可以有若干个参数

运行下面的代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	struct S s = { "xiaoshuai", 25, 75.3 };
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写文件
	fprintf(pf, "%s\n%d\n%.1lf\n", s.name, s.age, s.weight);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

请添加图片描述

可以看到,文档的内容已经被重写


4.1.6 fscanf

fscanf函数的原型如下:

int fscanf( FILE *stream, const char *format, ... );

对比scanf函数:

int scanf( const char *format, ... );

也是多了一个文件指针参数

运行下面的代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	struct S s = { 0 };
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	fscanf(pf, "%s %d %lf\n", s.name, &s.age, &s.weight);
	//从文件中读取的信息存到结构体s中
	//注意:从文件中读的时候不要用  %.1lf
	//s.name是数组名不需要加取地址操作符
	printf("%s\n%d\n%.1lf\n", s.name, s.age, s.weight);
	//打印在屏幕上
	
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

请添加图片描述

当然也可以用fprintf打印:

在这里插入图片描述

fprintffscanf就相当于printfscanf的升级版,功能是在它们两个之上的


4.1.7 sprintf(操作的不是文件)

注意:函数sprintf操作的不是文件,在这里介绍是为了对比

sprintf函数原型如下:

int sprintf( char *buffer, const char *format, ... );

sprintf函数的功能:将结果写入字符串buffer, 如果所写入的字符串(加上终止空字符)超出由 buffer所指向的数组的大小,则行为未定义。

运行下面的代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	char str[100] = { 0 };
	struct S s = { "xiaomei", 24, 55.2 };
	sprintf(str, "%s %d %.1lf", s.name, s.age, s.weight);
	printf("%s\n", str);
	return 0;
}

在这里插入图片描述

sprintf其实是将格式化的数据转化为字符串


4.1.8 sscanf(操作的不是文件)

注意:函数sscanf操作的不是文件,在这里介绍是为了对比

sscanf函数的原型如下:

int sscanf( const char *buffer, const char *format, ... );

sscanf函数的功能是从字符数组中提取数据,然后格式化

运行下面的代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	char str[100] = { 0 };
	struct S s = { "xiaomei", 24, 55.2 };
	//将结构体变量s中格式化的数据转化为字符串存入字符数组str中
	sprintf(str, "%s %d %.1lf", s.name, s.age, s.weight);
	//printf("%s\n", str);

	//临时变量
	struct S tmp = { 0 };

	//将字符数组str中的数据格式化的存入结构体变量tmp中
	sscanf(str, "%s%d%lf", tmp.name, &tmp.age, &tmp.weight);
	fprintf(stdout, "%s %d %.1lf", tmp.name, tmp.age, tmp.weight);

	return 0;
}

请添加图片描述


  • scanf/printf针对标准输入流 / 标准输出流的格式化输入 / 输出函数
  • fscanf/fprintf针对所有输入流 / 所有输出流的格式化输入 / 输出函数
  • sscanf/sprintf将格式化的数据转换为字符串 / 将字符串转化为格式化的数据

4.1.9 fwrite

fwrite函数原型如下:

size_t fwrite( const void *buffer, size_t size, size_t count,FILE *stream );

fwrite函数的参数:

  • buffer:指向数组中要被写入的首个对象的指针
  • size:每个对象的大小
  • count:要被写入的对象数
  • stream:指向输出流的指针

fwrite函数的返回值:成功写入的对象数,若错误发生则可能小于count

fwrite函数的作用:将buffer指向空间内count个大小为size的元素数据写到输出流stream(文件)中

运行下面的代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	struct S s = { "xiaochou", 23, 65.7 };
	//打开文件
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	//写文件
	//以二进制的形式写到文件中
	fwrite(&s, sizeof(struct S), 1, pf);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

请添加图片描述

运行成功后文档内是我们看不懂的二进制的信息


4.1.10 fread

fread函数的原型如下:

size_t fread( void *buffer, size_t size, size_t count,FILE *stream );

fread函数的参数:

  • buffer:指向要读取的数组中首个对象的指针
  • size:每个对象的字节大小
  • count:要读取的对象数
  • stream:读取来源的输入文件流

fread函数的返回值:成功读取的对象数,若出现错误或文件尾条件,则可能小于count

fread函数的作用:从输入流stream(文件)中读取count个大小为size个字节的数据存到buffer指向的空间内

运行下面的代码:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	struct S s = { 0 };
	//打开文件
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	
	//写文件
	//读取二进制的信息到文件中
	fread(&s, sizeof(struct S), 1, pf);
	//打印结构s的成员
	fprintf(stdout, "%s\n%d\n%.1lf\n", s.name, s.age, s.weight);

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

请添加图片描述

可以看到fread函数又将二进制的信息读文件中变成了我们可以看懂的信息


五、文件结束的判定

5.1 被错误使用的feof

文件读取结束有两个原因:

    1. 遇到文件结尾
    1. 遇到错误

feof函数的原型如下:

int feof( FILE *stream );

feof函数的返回值:若已抵达流尾则为非零值,否则为 ​0​

feof函数的作用是:当文件已经读取结束的时候,判断读取结束的原因是否是遇到文件结尾

但是这个函数经常被用错,部分人以为feof函数的作用是判断文件读取是否结束,其实不是的


5.2 文本文件读取结束

文本文件读取是否结束,判断返回值:

  • fgetc:判断是否为EOF
  • fgets:判断是否为NULL

例如:

#include <stdio.h>

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件
	int ch = 0;//注意:为处理 EOF 需要 int 而非 char
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c ", ch);
	}

	//判断是什么原因结束的
	if (ferror(pf))
	{
		puts("I/O error when reading");
	}
	else if (feof(pf))
	{
		puts("End of file reached successfully");
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

5.3 二进制文件读取结束

二进制文件读取是否结束,判断返回值是否小于实际要读的个数。
例如:

#include <stdio.h>

struct S
{
	char name[20];
	int age;
	double weight;
};

int main()
{
	struct S s = { 0 };
	//打开文件
	FILE* pf = fopen("test.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读取二进制的信息到文件中
	size_t num = fread(&s, sizeof(struct S), 1, pf);
	if (num == 1)
	{
		puts("Array read successfully, contents:");
	}
	else if (feof(pf))
	{
		printf("Error reading test.bin: unexpected end of file\n");
	}
	else if (ferror(pf))
	{
		perror("Error reading test.bin");
	}
	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

六、文件缓冲区

ANSIC标准采用“缓冲文件系统”处理数据文件的,所谓缓冲文件系统是指系统自动的在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区,充满缓冲区后再逐个地将数据送到程序数据区(程序变量等),缓冲区的大小根据C编译系统决定。


总结

  • 文件读写函数在编程中具有非常重要的作用,能够帮助程序员实现数据的持久化存储、数据交换、日志记录、配置文件处理等功能,提高程序的灵活性、可维护性和可扩展性,从而提升整个程序的质量和效率

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/697663.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅析嵌入式实时系统中信号量的概念

目录 概述 1. 认识信号量 1.1 定义信号量 1.2 信号量的类型 1.2.1 二值信号量 1.2.2 计数信号量 1.2.3 互斥信号量 1.2.3.1 认识互斥信号量 1.2.3.2 互斥信号量的其他特性 2 典型信号量的使用 2.1 等待和信号同步 2.2 多任务等待和信号同步 2.3 信用跟踪同步 2.…

C++ - Clion安装Qt msvc2017版本教程,基础环境配置clion+ Qt5.12.12 msvc2017 + VS2019

背景&#xff1a;平时代码开发使用clion&#xff0c;但使用项目要制定mscv2017版本Qt。先装过mingw版本Qt无法运行&#xff0c;但msvc版本依赖装有Visual Studio&#xff0c;本地装的又是2019版。就出现了这个大坑&#xff0c;需要配置好clion Qt msvc2017 VS2019。 文章目录 …

深度学习中embedding层的理解

Embedding层作用 在深度学习领域中&#xff0c;Embedding层扮演着至关重要的角色&#xff0c;尤其在处理文本数据或类别数据。Embedding层的功能有两个&#xff1a; 1. 将高维稀疏的输入数据&#xff08;如单词、类别标签等&#xff09;转换为低维稠密的向量表示&#xff0c;…

LNMP配置

文章目录 一、相关概念CGI的由来FastCGIPHP-FPM 二、编译安装编译安装nginxyum安装mysql编译安装php配置nginx支持php解析增加数据库安装论坛 一、相关概念 CGI的由来 最早的Web服务器只能简单地响应浏览器发来的HTTP请求&#xff0c;并将存储在服务器上的HTML文件返回给浏览器…

Python | Leetcode Python题解之第144题二叉树的前序遍历

题目&#xff1a; 题解&#xff1a; class Solution:def preorderTraversal(self, root: TreeNode) -> List[int]:res list()if not root:return resp1 rootwhile p1:p2 p1.leftif p2:while p2.right and p2.right ! p1:p2 p2.rightif not p2.right:res.append(p1.val)…

DeepSpeed MoE

MoE概念 模型参数增加很多&#xff1b;计算量没有增加&#xff08;gating小FNN&#xff0c;比以前的大FNN计算量要小&#xff09;&#xff1b;收敛速度变快&#xff1b; 效果&#xff1a;PR-MoE > 普通MoE > DenseTransformer MoE模型&#xff0c;可视为Sparse Model&…

手机投屏到电脑时,手机提示连接失败

前言 注意&#xff0c;本方法建立在你已经通过其他帖子等解决了前置条件的情况下&#xff0c;手机提示连接失败情况下&#xff0c;包括但不限于关闭防火墙、安装无线投屏工具、手机和电脑连接在同一个WiFi频段下、关闭杀毒软件等。 具体操作方法 1、请进入设置 > 系统和…

电脑存储设备,固态硬盘介绍,usb接口

简介 存储设备分为两大类主存和辅存&#xff0c;另外还有专门提供存储服务的网络存储 主存储器 随机存取存储器&#xff08;RAM, Random Access Memory&#xff09; 特点&#xff1a;高速、易失性存储器&#xff0c;断电后数据丢失。用途&#xff1a;临时存储正在使用的数据…

卷积神经网络 convolution neural network

1.数学卷积&#xff1a;滑动窗口 2.图像具有局部相关性和平移不变性&#xff0c;有许多冗余的特征点&#xff0c;如果用全连接的神经网络会很浪费时间。 3.卷积nn&#xff1a;减少参数&#xff0c;滑动提取特征&#xff0c;特征作为下层卷积的输入&#xff0c;然后放到全连接…

Androidstudio项目加载不出来,显示Connect timed out

Android studio加载不出来所需要的环境依赖,99%的问题都是网络原因 解决办法有两个: 1.科学上网 2.使用国内的镜像 方法一自行解决,下面重点介绍方法二 在项目目录下找到gradle->wrapper->gradle-wrapper.properties 将项目的distributionUrl改为https://mirrors.cl…

设计模式-装饰器模式(结构型)

装饰器模式 装饰器模式是一种结构模式&#xff0c;通过装饰器模式可以在不改变原有类结构的情况下向一个新对象添加新功能&#xff0c;是现有类的包装。 图解 角色 抽象组件&#xff1a;定义组件的抽象方法具体组件&#xff1a;实现组件的抽象方法抽象装饰器&#xff1a;实现…

Unity 笔试题分享

1. 请回答以下代码片段执行时是否会产生堆内存分配 a. void SetChar(string s){s.Replace(b, d);}b. void Update(Transform t){t.localPosition new Vector3(0, 0, 0);}c、 int Sum(List<int> l){int total 0;foreach (int i in l){total i;} return total;}d…

C++ | Leetcode C++题解之第144题二叉树的前序遍历

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> preorderTraversal(TreeNode *root) {vector<int> res;if (root nullptr) {return res;}TreeNode *p1 root, *p2 nullptr;while (p1 ! nullptr) {p2 p1->left;if (p2 ! nullptr) {…

python教程

python解释器的安装 https://www.python.org/ftp/python/3.12.4/python-3.12.4-amd64.exe jetbrains官网 英文 PyCharm 专业的版本 Thank you for downloading PyCharm! 社区 Thank you for downloading PyCharm! 中文 PyCharm 专业的版本 感谢您下载PyCharm&#xff01…

群体优化算法---水波优化算法介绍以及应用于聚类数据挖掘代码示例

介绍 水波优化算法&#xff08;Water Wave Optimization, WWO&#xff09;是一种新兴的群智能优化算法&#xff0c;灵感来自水波在自然环境中的传播和衰减现象。该算法模拟了水波在水面上传播和碰撞的行为&#xff0c;通过这些行为来寻找问题的最优解。WWO算法由三种主要的操作…

【JavaEE】Spring Boot MyBatis详解(一)

一.MyBatis的基本概念与相关配置. 1.基本概念 MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发。MyBatis本是Apache的一个开源项目iBatis&#xff0c;2010年这个项目由apache迁移到了google code&#xff0c;并且改名为MyBatis. 2013年11月迁移到Github.持久层…

区间预测 | Matlab实现GRU-ABKDE门控循环单元自适应带宽核密度估计多变量回归区间预测

区间预测 | Matlab实现GRU-ABKDE门控循环单元自适应带宽核密度估计多变量回归区间预测 目录 区间预测 | Matlab实现GRU-ABKDE门控循环单元自适应带宽核密度估计多变量回归区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现GRU-ABKDE门控循环单元自适应…

[Vue3:组件通信)子组件props接收和watch监听,emit发送父组件 (添加修改设置成绩,添加、删除选课记录)

文章目录 一&#xff1a;系统功能&#xff1a;设置成绩&#xff08;添加或修改&#xff09;交互逻辑&#xff1a;涉及页面 Page02.vue&#xff0c;ModalEdit.vue主页面Page.vue注入子页面&#xff0c;使用子页面标签属性主页面对子页面做通信&#xff0c;子页面ModalEdit接收参…

Nginx+Tomcat负载均衡,动静分离群集

Nginx反向代理原理 Nginx 反向代理&#xff1a;将Nginx接收到的请求转发给其它应用服务器处理 Nginx 负载均衡&#xff1a;通过反向代理实现&#xff0c;还可以将nginx接收到的请求转发给多个后端应用服务器处理 Nginx 动静分离&#xff1a;静态页面请求&#xff0c;由nginx…

将AIRNet集成到yolov8中,实现端到端训练与推理

AIRNet是一个图像修复网络,支持对图像进行去雾、去雨、去噪声的修复。其基于对比的退化编码器(CBDE),将各种退化类型统一到同一嵌入空间;然后,基于退化引导恢复网络(DGRN)将嵌入空间修复为目标图像。可以将AIRNet的输出与yolov8进行端到端集成,实现部署上的简化。 本博…