【C语言】文件的输入与输出

news2025/1/13 14:12:42

在此之前,我极少使用C语言处理文件。因为我认为使用Python、matlab处理文件是及其方便的。

事实果真如此吗?

文章目录

  • 一、与文件进行通信
    • 1.1 文件的定义
    • 1.2 文本文件和二进制文件
    • 1.3 底层 I/O 和 标准I/O
    • 1.4 标准文件
    • 1.5 标准 I/O
  • 二、文件的打开和关闭:fopen()、fclose
    • 2.1 打开一个文件:fopen()函数
    • 2.2 getc()函数和putc函数(文件的字符处理)
    • 2.3 文件结尾:EOF
    • 2.4 关闭一个打开的文件:fclose()函数
    • 2.5 指向标准文件的指针
    • 2.6 例程
  • 三、文件I/O:fprintf()、fscanf()、fgets()、fputs()
    • 3.1 fscanf()和fprintf()函数
    • 3.2 fgets()和fputs()
  • 四、定位函数fseek()和ftell()
  • 五、标准I/O的原理

一、与文件进行通信

1.1 文件的定义

文件(file)通常是在磁盘或固态硬盘上的一段已命名的存储区。对我们而言,stdio.h就是一个文件的名称,该文件中包含一些有用的信息。然而,对操作系统而言,文件更复杂一些。例如,大型文件会被分开储存,或者包含一些额外的数据,方便操作系统确定文件的种类,这都是操作系统所关心的,程序员关心的是C程序如何处理文件。

C把文件看作是一系列连续的字节,每个字节都能被单独读取。这与UNIX环境中的文件结构相对应。由于其他环境中可能无法完全对应这个模型,C提供两种文件模式:文本模式和二进制模式

1.2 文本文件和二进制文件

需要区分:文本内容和二进制内容文本文件格式和二进制文件格式以及文件的文本模式和二进制模式

所有文件的内容都以二进制形式(0或1)储存(即在物理层面上,文本文件和二进制文件没有区别)。

如果文件最初使用二进制编码的字符(例如,ASCII或Unicode)表示文本(就像C字符串那样),该文件就是文本文件,其中包含文本内容。

如果文件中的二进制值代表机器语言代码或数值数据(使用相同的内部表示,假设,用于long或double类型的值)或图片或音乐编码,该文件就是二进制文件,其中包含二进制内容。

比如 bmp文件,它开始的部分是文件头信息,前2个字节表示文件格式为BMP格式,接着的 8个字节表示文件的长度,再接着的4个字节表示 bmp文件头的长度。从中可以看出,解析bmp文件时是不定长度的,2、4、8字节长度的都有。因此bmp文件是二进制文件。

不同系统在文件处理方面有着不同的标准(如换行符在不同系统中可以是:\n,\r,\r\n),为了规范文本文件的处理,C提供两种访问文件的途径:二进制模式和文本模式。

  • 在二进制模式中,程序可以访问文件的每个字节。

  • 在文本模式中,程序所见的内容和文件的实际内容不同。程序以文本模式读取文件时,本地环境表示的行末尾或文件结尾映射为C模式

例如:

  • C程序在旧式Macintosh中以文本模式读取文件时,把文件中的\r转换成\n;以文本模式写入文件时,把\n转换成\r。

  • 或者,C文本模式程序在MS-DOS平台读取文件时,把\r\n转换成\n;写入文件时,把\n转换成\r\n。在其他环境中编写的文本模式程序也会做类似的转换。

  • 除了以文本模式读写文本文件,还能以二进制模式读写文本文件。如果读写一个旧式MS-DOS文本文件,程序会看到文件中的\r和\n字符,不会发生映射。如果要编写旧式Mac格式、MS-DOS格式或UNIX/Linux格式的文件模式程序,应该使用二进制模式,这样程序才能确定实际的文件内容并执行相应的动作。

虽然C提供了二进制模式和文本模式,但是这两种模式的实现可以相同。前面提到过,因为UNIX使用一种文件格式,这两种模式对于UNIX实现而言完全相同。Linux也是如此。

1.3 底层 I/O 和 标准I/O

除了选择文件的模式,大多数情况下,还可以选择I/O的两个级别(即处理文件访问的两个级别)。

底层I/O(low-level I/O)使用操作系统提供的基本I/O服务

标准高级I/O(standardhigh-level I/O)使用C库的标准包和stdio.h头文件定义。因为无法保证所有的操作系统都使用相同的底层I/O模型,C标准只支持标准I/O包。有些实现会提供底层库,但是C标准建立了可移植的I/O模型,本文主要讨论这些I/O。

1.4 标准文件

C程序会自动打开3个文件,它们被称为标准输入(standard input)、标准输出(standard output)和标准错误输出(standard error output)

  • 在默认情况下,标准输入是系统的普通输入设备,通常为键盘;

  • 标准输出和标准错误输出是系统的普通输出设备,通常为显示屏。

  • 通常,标准输入为程序提供输入,它是getchar()和scanf()使用的文件。

  • 程序通常输出到标准输出,它是putchar()、puts()和printf()使用的文件。

  • 重定向把其他文件视为标准输入或标准输出

标准错误输出提供了一个逻辑上不同的地方来发送错误消息。例如,如果使用重定向把输出发送给文件而不是屏幕,那么发送至标准错误输出的内容仍然会被发送到屏幕上。这样很好,因为如果把错误消息发送至文件,就只能打开文件才能看到。

1.5 标准 I/O

与底层I/O相比,标准I/O包除了可移植以外还有两个好处。

  1. 第一,标准I/O有许多专门的函数简化了处理不同I/O的问题。例如,printf()把不同形式的数据转换成与终端相适应的字符串输出。
  2. 第二,输入和输出都是缓冲的。(一次转移一大块信息而不是一字节信息(通常至少512字节))

例如,当程序读取文件时,一块数据被拷贝到缓冲区(一块中介存储区域)。这种缓冲极大地提高了数据传输速率。程序可以检查
缓冲区中的字节。缓冲在后台处理,所以让人有逐字符访问的错觉(如果使用底层I/O,要自己完成大部分工作)

二、文件的打开和关闭:fopen()、fclose

2.1 打开一个文件:fopen()函数

函数原型:

 FILE* fopen(char const* _FileName,char const* _Mode
  • 第一个参数:文件名;
  • 第二个参数:打开的模式;
  • 返回文件指针,即指向FILE的指针,FILE是一个定义在stdio.h中的派生类型。文件指针fp并不指向实际的文件,它指向一个包含文件信息的数据对象,其中包含操作文件的I/O函数所用的缓冲区信息。

第二个参数值指定以什么模式打开文件,是一个字符串,可取的值有:

模式字符串含义
“r”以读模式打开文件
“w”以写模式打开文件,把现有文件的长度截为0,如果文件不存在,则创建一个新文件
“a”以写模式打开文件,在现有文件末尾添加内容,如果文件不存在,则创建一个新文件
“r+”以更新模式打开文件(即可以读写文件)
“w+”以更新模式打开文件(即,读和写),如果文件存在,则将其长度截为0;如果文件不存在,则创建一个新文件
“a+”以更新模式打开文件(即,读和写),在现有文件的末尾添加内容,如果文件不存在则创建一个新文件;可以读整个文件,但是只能从末尾添加内容
“rb”、“wb”、“ab”、“rb+”、“r+b”、“wb+”、“w+b”、“ab+”、“a+b”与上一个模式类似,但是以二进制模式而不是文本模式打开文件
“wx”、“wbx”、“w+x”、“wb+x"或"w+bx”(C11)类似非x模式,但是如果文件已存在或以独占模式打开文件,则打开文件失败

像UNIX和Linux这样只有一种文件类型的系统,带b字母的模式和不带b字母的模式相同。

上面的“将长度截为0”意思是删除现有文件已有的内容。使用任何一种不带X的“w”模式,都会这样。

示例:

FILE* fp = fopen("test.txt","r");

2.2 getc()函数和putc函数(文件的字符处理)

这两个函数用于从文件中获取一个字符和将一个字符写入文件,每次执行后,位置都会自动向后移动一个字符。

和getchar()、putchar()类似。

示例:

ch = getc();
putc(ch,fp);

2.3 文件结尾:EOF

getc()函数在读取一个字符时发现是文件结尾,它将返回一个特殊值EOF。

它定义在stdio.h中:

#define EOF    (-1)

现在就可以使用C语言来读取一个文本文件了:

#include<stdio.h>

int main()
{
	FILE* fp = fopen("origin.txt","r");
	int ch;
	while ((ch=getc(fp))!=EOF)
	{
		putchar(ch);

	}
	fclose(fp);

	return 0;
}

输出:

Hello,CSDN.

2.4 关闭一个打开的文件:fclose()函数

fclose(fp)函数关闭fp指定的文件,必要时刷新缓冲区。对于较正式的程序,应该检查是否成功关闭文件。如果成功关闭,fclose()函数返回0
,否则返回EOF:

if (fclose(fp) != 0)
    printf("Error in closing file %s\n", argv[1]);

如果磁盘已满、移动硬盘被移除或出现I/O错误,都会导致调用fclose()函数失败。

2.5 指向标准文件的指针

  • 标准输入:stdin
  • 标准输出:stdout
  • 标准错误:stderr

都是指向文件的指针。

2.6 例程

写一个程序,读取一个文件,并copy内容到新文件中。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define LEN 30

void file_copy(const char *file_in);


int main()
{
	file_copy("origin.txt");

	return 0;
}


void file_copy(const char *file_in)
{
	FILE *in, *out;
	char name[LEN];
	int ch;

	// 检查文件名称
	if ((in = fopen(file_in, "r")) == NULL)
	{
		fprintf(stderr,"The file: %s is not exit.\n",file_in);
		exit(EXIT_FAILURE);
	}


	strncpy(name, file_in, LEN - 9);
	name[LEN - 5] = '\0';
	strcat(name,".new.txt");

	if ((out = fopen(name, "w+")) == NULL)
	{
		fprintf(stderr, "Can't creat output file.\n");
		exit(EXIT_FAILURE);
	}



	while ((ch = getc(in)) != EOF)
	{
		putc(ch, out);
	}

	if (fclose(in) != 0 || fclose(out) != 0)
	{
		fprintf(stderr, "Error with closing files.\n");
		exit(EXIT_FAILURE);
	}

	puts("Processing success!");

}

效果:
在这里插入图片描述

注:

exit()函数关闭所有打开的文件并结束程序。exit(EXIT_FAILURE);表示技术程序失败,并将标准错误(stderr)输出到屏幕,exit(EXIT_SUCCESS);即exit(0);在main()函数中和return效果一样。

三、文件I/O:fprintf()、fscanf()、fgets()、fputs()

3.1 fscanf()和fprintf()函数

文件I/O函数fprintf()和fscanf()函数的工作方式与printf()和scanf()类似,区别在于前者需要用第1个参数指定待处理的文件。

例:

void fp_s()
{
	FILE* fp;
	char words[30];

	if ((fp = fopen("word.txt", "a+")) == NULL)
	{
		fprintf(stderr, "打开文件失败。\n");
		exit(EXIT_FAILURE);
	}

	puts("输入单词,q表示结束\n");
	while ((fscanf(stdin, "%s", words) == 1) && words[0] != 'q')
	{
		fprintf(fp, "%s\n", words);
	}

	rewind(fp); //将指针移到开头

	puts("你输入了以下单词:");

	while ((fscanf(fp, "%s\n", words)) == 1)
		puts(words);

	if ((fclose(fp)) != NULL)
		fprintf(stderr,"关闭文件失败。\n");
}

输出:

输入单词,q表示结束

dog
cat
Jay Chou
你好
123
q
你输入了以下单词:
dog
cat
Jay
Chou
你好
123

3.2 fgets()和fputs()

fgets()函数的第1个参数和gets()函数一样,也是表示储存输入位置的地址(char * 类型);第2个参数是一个整数,表示待输入字符串的大小(不是长度);最后一个参数是文件指针,指定待读取的文件。下面是一个调用该函数的例子:

fgets(buf, STLEN, fp);

这里,buf是char类型数组的名称,STLEN是字符串的大小,fp是指向FILE的指针。fgets()函数读取输入直到第1个换行符的后面,或读到文件结尾,或者读取STLEN-1个字符(以上面的fgets()为例)。然后,fgets()在末尾添加一个空字符使之成为一个字符串。字符串的大小是其字符数加上一个空字符。如果fgets()在读到字符上限之前已读完一整行,它会把表示行结尾的换行符放在空字符前面。fgets()函数在遇到EOF时将返回NULL值,可以利用这一机制检查是否到达文件结尾;如果未遇到EOF则返回之前传给它的第一个参数地址。

fputs()函数接受两个参数:第1个是字符串的地址;第2个是文件指针。该函数根据传入地址找到的字符串写入指定的文件中。和puts()函数不同,fputs()在打印字符串时不会在其末尾添加换行符。下面是一个调用该函数的例子:

fputs(buf, fp);

这里,buf是字符串的地址,fp用于指定目标文件。由于fgets()保留了换行符,fputs()就不会再添加换行符。

四、定位函数fseek()和ftell()

fseek()函数原型:

 int  fseek(  FILE* _Stream,long  _Offset, int _Origin);

该函数的功能是修改当前位置。

  • 第一个参数是文件指针(已经打开的文件);
  • 第二个参数是偏移的字节数,long型;
  • 第三个参数是气势位置:SEEK_SET:文件开始处,SEEK_END:文件结尾,SEEK_CUR:当前位置。

成功返回0,出错返回-1。

ftell()函数的返回类型是long,就一个参数:文件指针,它返回的是参数指向文件的当前位置距文件开始处的字节数。

编写一个函数将文件中的内容逆序添加到文件结尾,原内容:

ABCDEFGHIJK

运行一次:

ABCDEFGHIJK

KJIHGFEDCBA

代码:

void fseek_tell()
{
	FILE* pt;
	long count;
	int ch;

	pt = fopen("sort.txt","a+");

	fseek(pt,0L,SEEK_END);
	count = ftell(pt);

	fprintf(pt,"%s","\n"); //换行

	while (count != -1)
	{
		fseek(pt,count,SEEK_SET);
		ch = getc(pt);

		count--;

		fseek(pt, 0L, SEEK_END);
		fprintf(pt, "%c", ch);
	}

	fclose(pt);
}

fseek()和ftell()潜在的问题是,它们都把文件大小限制在long类型能表示的范围内。也许20亿字节看起来相当大,但是随着存储设备的容量迅猛增长,文件也越来越大。鉴于此,ANSI C新增了两个处理较大文件的新定位函数:fgetpos()和fsetpos().

五、标准I/O的原理

通常,使用标准I/O的第1步是调用fopen()打开文件(前面介绍过,C程序会自动打开3种标准文件)。

fopen()函数不仅打开一个文件,还创建了一个缓冲区(在读写模式下会创建两个缓冲区)以及一个包含文件和缓冲区数据的结构

另外,fopen()返回一个指向该结构的指针,以便其他函数知道如何找到该结构。假设把该指针赋给一个指针变量fp,我们说fopen()函数“打开一个”。如果以文本模式打开该文件,就获得一个文本流;如果以二进制模式打开该文件,就获得一个二进制流。

这个结构通常包含一个指定流中当前位置的文件位置指示器。除此之外,它还包含错误和文件结尾的指示器、一个指向缓冲区开始处的指针、一个文件标识符和一个计数(统计实际拷贝进缓冲区的字节数)。

通常,使用标准I/O的第2步是调用一个定义在stdio.h中的输入函数,如fscanf()、getc()或fgets()。一调用这些函数,文件中的缓冲大小数据块就被拷贝到缓冲区中。缓冲区的大小因实现而异,一般是512字节或是它的倍数,如4096或16384。最初调用函数,除了填充缓冲区外,还要设置fp所指向的结构中的值。尤其要设置流中的当前位置和拷贝进缓冲区的字节数。通常,当前位置从字节0开始。

在初始化结构和缓冲区后,输入函数按要求从缓冲区中读取数据。在它读取数据时,文件位置指示器被设置为指向刚读取字符的下一个字符。由于stdio.h系列的所有输入函数都使用相同的缓冲区,所以调用任何一个函数都将从上一次函数停止调用的位置开始。

当输入函数发现已读完缓冲区中的所有字符时,会请求把下一个缓冲大小的数据块从文件拷贝到该缓冲区中。以这种方式,输入函数可以读取文件中的所有内容,直到文件结尾。函数在读取缓冲区中的最后一个字符后,把结尾指示器设置为真。于是,下一次被调用的输入函数将返回EOF。输出函数以类似的方式把数据写

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

flume 的Channel的种类

目录 1、MemoryChannel 2、FileChannel 3、KafkaChannel Flume拦截器 消息队列传输消息 1、MemoryChannel 数据放在内存中,会在Flume宕机的时候丢失数据,可以⽤在对数据安全性要求没有那么⾼的场景中⽐如⽇志数据。 2、FileChannel 不会丢失数据,因为数据是放在磁盘上边的…

前端使用国密SM4进行加密

目录 需求【方法1】 - 使用 sm4util 依赖【方法2】sm4.js引入1. /public/sm4.js2. body 标签上引入该文件3. 使用 - ECB 模式加密 【方法3】1. 本地写 js 文件2. 使用 - ECB 模式加解密 需求 前端/后端使用 国密SM4 进行加密/解密&#xff0c; 【注意】前后端配合加解密时&…

【数据结构】线性表之——“顺序表”

文章目录 前言顺序表主体结构顺序表操作函数介绍顺序表操作函数实现实现顺序&#xff1a;顺序表的初始化&#xff1a;顺序表插入函数&#xff1a;头插尾插指定位置插入 顺序表打印函数查找顺序表数据顺序表删除函数头删尾删指定位置删除 修改顺序表销毁顺序表 文件分类test.cSe…

webpack5搭建react框架-生产环境配置

webpack5配置react基础生产环境 一、前言 在项目构建时不同的环境下会有不同配置&#xff0c;在前面文章中已经使用webpack5配置好了基础环境和开发环境&#xff0c;但是在生产环境时有些配置和开发环境是不需要的&#xff0c;有些是可以在优化的&#xff0c;所以下面继续生产…

分支和循环语句——1

老铁们&#xff0c;这是博主初识C之后的第一篇C语言学习博客&#xff0c;希望可以给你们带来帮助。 文章目录 一、什么是语句? 二、分支语句 1、if语句 2、switch语句 三、while循环 一、什么是语句? C语句可分为以下五类&#xff1a; 1. 表达式语句 2. 函数调用语句…

模拟不同MIMO-OFDM方案的MATLAB代码(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果​ &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 MIMO技术指在发射端和接收端分别使用多个发射天线和接收天线&#xff0c;使信号通过发射端与接收端的多个天线传送和接收&…

杭州旭航集团,申请纳斯达克IPO上市,募资9800万美元

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;为中国企业提供数字内容营销服务的杭州旭航网络科技有限公司的控股公司Xuhang Holdings Ltd(以下简称&#xff1a;旭航集团)&#xff0c;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提…

随想录Day53--动态规划: 1143.最长公共子序列 ,1035.不相交的线 , 53. 最大子序和

1143.最长公共子序列,这题要画一个二维数组&#xff0c;用两层for循环来遍历每个字符&#xff0c;从而比较是否相等。用dp[i][j]来表示当遍历到text2的第i个字符和text1的第j个字符时&#xff0c;最长的公共子序列为多少。比如说两个字符串&#xff08;“abcde”和“ace”&…

怎么样成为一名Python工程师?到底要会哪些东西?你会了多少?

目录 重点&#xff1a;爬虫部分项目、源码展示python数据分析可视化大屏看板python爬虫爬取淘宝卤鸭货商品数据python游戏开发python自动化办公 重点&#xff1a; 1、做一名程序员&#xff0c;绝对要耐得住寂寞&#xff0c;并且要一直有点兴趣促进你学习。如果你完全没兴趣&am…

electron+vue3全家桶+vite项目搭建【十】vite路径取别名、多环境相关配置

文章目录 引入1.路径取别名配置2.测试别名配置3.环境变量配置4.验证环境变量配置 引入 我们之前写代码的时候用相对路径不是很方便&#xff0c;并且所有环境共用同一套配置也不太好&#xff0c;接下来我们通过vite配置一下路径别名和环境变量 视频讲解 vite官网 demo项目地址…

DIN论文翻译

摘要 在电子商务行业&#xff0c;利用丰富的历史行为数据更好地提取用户兴趣对于构建在线广告系统的点击率(CTR)预测模型至关重要。关于用户行为数据有两个关键观察结果&#xff1a;i) 多样性(diversity)。用户在访问电子商务网站时对不同种类的商品感兴趣。ii) 局部激活(local…

Linux驱动之GPIO函数、IO内存映射、混杂设备驱动

之前学习完了字符设备驱动的大体框架&#xff0c;现在我们就使用这个基本的框架来对硬件进行操作&#xff0c;例如通过指令控制led的状态&#xff0c;编写LED驱动。LED驱动有多种实现方式。 目录 GPIO函数 IO内存映射 混杂设备驱动 GPIO函数 首先加入需要的头文件。 #incl…

欧盟立法者签署公开信,近万人联名“暂停高级AI研发”

来源丨CoinTelegraph 编辑丨liuruiWeb3CN.Pro ChatGPT 曾经的势头有多猛烈如今就被行业大佬抵制的就有多严重。 近日&#xff0c;十几位欧盟 (EU) 政客签署了“暂停高级AI研发”的公开信&#xff0c;呼吁 AI &#xff08;人工智能&#xff09;的“安全”发展&#xff0c;特斯拉…

【Android -- 软技能】聊聊高效开发的一些套路与实践

前言 在开发中&#xff0c;编码我们有分层架构、设计模式做为套路来高效开发&#xff0c;但你也知道编码不是开发的全部&#xff0c;一个完全的开发流程用面向对象思想来概括&#xff0c;它分为OOA&#xff08;面向对象分析&#xff09;、OOD&#xff08;面向对象设计&#xf…

Flutter - 动画使用及自定义动画组件(tabbar跳动动画或文字抖动)

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 Flutter - 动画组件&#xff08;tabbar跳动动画或文字抖动&#xff09; 序效果图动画简介动画类型Flutter动画的一些概念 常用动画实现隐式动画Tween动画Curve动画H…

低代码/无代码平台在软件开发中的应用

随着技术的不断发展&#xff0c;软件开发也在不断地进步。低代码/无代码平台已经成为软件开发的一个新的趋势。在这篇文章中&#xff0c;我们将深入探讨低代码/无代码平台在软件开发中的应用&#xff0c;包括它们的优势、如何选择合适的平台以及如何使用这些平台来开发高质量的…

机器学习 CarRentalData数据集分析和预测

介绍数据集 fuelType&#xff1a;燃料类型 rating&#xff1a;评级 renterTripsTaken&#xff1a;租房者出行 reviewCount&#xff1a;审阅计数 location.city&#xff1a;位置.城市 location.country&#xff1a;地点.国家/地区 location.latitude&#xff1a;位置.纬度 loca…

STM32按键实验中连接按键的GPIO管脚是上拉输入还是下拉输入

一、理解 关于STM32按键实验中连接按键的GPIO管脚是配置为上拉输入还是下拉输入的理解&#xff1a; 以江科大自动协教学视频按键输入实验为例&#xff1a; &#xff08;1&#xff09;按键KEY0<——>PE4 按键另一端接GND &#xff08;2&#xff09;按键KEY1<——&…

入门教学 | 快速了解集简云

集简云是一款超级软件连接器,无需开发,无需代码知识就可以轻松打通数百款软件之间的数据连接,构建自动化与智能化的业务流程。通过自动化业务流程,每月可节省企业数百甚至数万小时的人工成本。 集简云是什么? 集简云是一款超级软件连接器,无需开发,无需代码知识,就可以…

在现成的3D打印机上进行实验理论:一种数据孪生的攻击探测框架

在现成的3D打印机上提供了一种DT中攻击探测框架的DT解决方案的实验演示&#xff0c;作为说明性CPMS资源。通过网络安全DT对打印机正常运行、异常运行和攻击三种情况下的实验数据进行收集和分析&#xff0c;得出攻击检测结果。实验装置概述如下图所示。该实验研究是在现实世界设…