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

news2025/1/18 3:17:45

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编译系统决定。


总结

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

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

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

相关文章

【python】flask 框架

python flask 框架 flask是一个轻量级的python后端框架 (Django, tornado, flask) 官网&#xff1a;欢迎来到 Flask 的世界 — Flask中文文档(3.0.x) 安装&#xff1a;pip install Flask -i https://pypi.douban.com 常识&#xff1a; http,默认端口号为80; https,默认端口号…

攻防世界---misc---embarrass

1、下载附件是一个数据包 2、用wireshark分析 3、ctrlf查找字符 4、 flag{Good_b0y_W3ll_Done}

[Algorithm][动态规划][完全背包问题][零钱兑换][零钱兑换Ⅱ][完全平方数]详细讲解

目录 1.零钱兑换1.题目链接2.算法原理详解3.代码实现 2.零钱兑换 II1.题目链接2.算法原理详解3.代码实现 3.完全平方数1.题目链接2.算法原理详解3.代码实现 1.零钱兑换 1.题目链接 零钱兑换 2.算法原理详解 思路&#xff1a; 确定状态表示 -> dp[i][j]的含义 dp[i][j]&am…

Python学习笔记速成版

数据容器 列表的方法-总览 具体操作 元组 定义 相关操作 注意事项 特点 字符串 总览 常用操作 特点 序列 定义 切片操作 Set集合 总览 定义 常用操作 注意事项 字典 总览 定义 常用操作 获取 嵌套 其他操作 summary 通用操作 字符串大小比较 函数进阶 多个返回值 多种传…

关闭windows11磁盘地址栏上的历史记录

关闭windows11的磁盘地址栏上的历史记录 windows11打开磁盘后访问某一个磁盘路径后会记录这个磁盘路径&#xff0c;而且有时候会卡住这个地址栏&#xff08;关都关不掉&#xff09;&#xff0c;非常麻烦。 如下图所示&#xff1a; 关闭地址栏历史记录 按下windows键打开开…

#15松桑前端后花园周刊-Turborepo 2.0、ESLint v9.4.0、重新学习promise、CSS gap

⚡️行业动态 ESLint 推出了一个新的 ESLint 配置迁移器 ESLint 用户没有升级到 ESLint v9.x 的最大原因之一是迁移配置文件似乎很困难和复杂。因此 Eslint 推出eslint/migrate-config支持将. eslintrc 文件迁移到 eslint.config.js&#xff0c;以帮助用户提高配置文件的速度。…

Qt——升级系列(Level Five):显示类控件、输入类控件、多元素控件、容器类控件、布局管理器

显示类控件 Label QLabel 可以⽤来显⽰⽂本和图⽚. 核⼼属性如下&#xff1a; 属性 说明 text QLabel 中的⽂本 textFormat ⽂本的格式. • Qt::PlainText 纯⽂本 • Qt::RichText 富⽂本(⽀持 html 标签) • Qt::MarkdownText markdown 格式 • Qt::AutoText 根…

MySQL—多表查询—练习(2)

一、引言 接着上篇博客《 MySQL多表查询——练习&#xff08;1&#xff09;》继续完成剩下的案例需求。 二、案例 &#xff08;0&#xff09;三张表&#xff08;员工表、部门表、薪资等级表&#xff09; 员工表&#xff1a;emp 部门表&#xff1a;dept 薪资等级表&#xff1a;…

[Leetcode]同时进行正向和逆向迭代匹配解决回文链表问题

题目链接:234. 回文链表 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 题解&#xff1a; currentNode 指针是先到尾节点&#xff0c;由于递归的特性再从后往前进行比较。frontPointer 是递归函数外的指针。若 currentNode.val ! frontPointer.val 则返回 false。…

从零开始搭建Electron项目(二)之例程解析

本专栏&#xff0c;前面学习了怎么下载例程并运行。 这里解析例程的构成 从零开始搭建Electron项目之运行例程-CSDN博客文章浏览阅读22次。最好的学习方式就是&#xff1a;给一段能够运行的代码示例。本文给出了例程资源&#xff0c;以及运行的步骤。在国内开发electron有一点特…

leetcode刷题记录35-419. 甲板上的战舰

问题描述 给你一个大小为 m x n 的矩阵 board 表示甲板&#xff0c;其中&#xff0c;每个单元格可以是一艘战舰 X 或者是一个空位 . &#xff0c;返回在甲板 board 上放置的 战舰 的数量。 战舰 只能水平或者垂直放置在 board 上。换句话说&#xff0c;战舰只能按 1 x k&#x…

LW-DETR:实时目标检测的Transformer, Apache-2.0 开源可商用,论文实验超 YOLOv8

LW-DETR&#xff1a;实时目标检测的Transformer&#xff0c; Apache-2.0 开源可商用&#xff0c;论文实验超 YOLOv8 LW-DETR 架构实例化高效训练高效推理 目的与解法拆解ViT编码器和DETR解码器多级特征图聚合变形交叉注意力窗口注意力和全局注意力 论文&#xff1a;https://arx…

C语言:结构体数组

结构体数组 介绍定义结构体定义结构体数组初始化结构体数组访问和修改结构体数组的元素遍历结构体数组 示例高级用法动态分配结构体数组使用 malloc 动态分配使用 calloc 动态分配 结构体数组作为函数参数结构体数组与指针多维结构体数组使用 typedef 简化结构体定义结构体数组…

【EDA】SSTA中最慢路径与最快路径统计计算

假设&#xff08;X1&#xff0c;X2&#xff09;为二元高斯随机向量&#xff0c;均值&#xff08;μ1&#xff0c;μ2&#xff09;&#xff0c;标准差&#xff08;σ1&#xff0c;σ2&#xff09;&#xff0c;相关系数ρ 定义&#xff1a;Xmax&#xff08;X1&#xff0c;X2&…

Keil软件仿真的使用

一、软件的初始化设置 初始设置可以按照下图&#xff0c;这里我使用的是STM32F103C8T6&#xff0c;所以单片机型号为STM32F103C8&#xff0c;这个设置在Debug目录下。然后进行时钟的设置&#xff0c;我们板上晶振为8M&#xff0c;这里将时钟改为8. 或许有人想问如果是别的型号单…

JavaScript基础用法(变量定义、输入输出、转义符、注释和编码规范)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

基于ensp的园区网络搭建综合实验

核心技术介绍 1、虚拟局域网&#xff08;VLAN&#xff09; 2、链路聚合&#xff08;E-trunk&#xff09; 3、多生成树协议&#xff08;MSTP&#xff09; 4、VLANIF三层逻辑接口 5、虚拟路由冗余协议&#xff08;VRRP&#xff09; 6、开放式最短路径优先&#xff08;OSPF&…

C语言:(动态内存管理)

目录 动态内存有什么用呢 malloc函数 开辟失败示范 free函数 calloc函数 realloc函数 当然realooc也可以开辟空间 常⻅的动态内存的错误 对NULL指针的解引⽤操作 对动态内存开辟的空间越界访问 对⾮动态开辟内存使⽤free释放 使⽤free释放⼀块动态开辟内存的⼀部分 …

G盘文件系统损坏:全面解析与应对策略

在数字时代&#xff0c;数据的重要性不言而喻。然而&#xff0c;G盘文件系统损坏却时常给我们的数据安全带来威胁。当G盘文件系统受损时&#xff0c;可能导致文件丢失、数据无法访问等严重后果。本文将深入探讨G盘文件系统损坏的现象、原因、恢复方案以及预防措施&#xff0c;帮…

Java学习-JDBC(四)

连接池 现有问题 每次操作数据库都需要重新获取新连接&#xff0c;使用完毕后需close释放&#xff0c;频繁的创建和销毁造成资源浪费连接的数量无法把控&#xff0c;对服务器造成巨大压力 连接池 连接池是数据库连接对象的缓冲区&#xff0c;通过配置&#xff0c;由连接池负…