【Linux】基础IO----系统文件IO 文件描述符fd 重定向

> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:了解在Linux下的系统文件IO,知道什么是文件描述符,什么是重定向

> 毒鸡汤:白日莫闲过,青春不再来。

> 专栏选自:Linux初阶

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

🌟前言

最早我们在C语言中学习关于如何用代码来管理文件,比如文件的输入和文件的输出,一些文件的接口,掌握上述的知识只能说是对文件入门而已,在Linux中我们是一切接文件的,如何深入学习文件的知识这是一个难题,今天我们所探讨就是Linux的基础I/O。

⭐主体

学习【Linux】基础IO咱们按照下面的图解:

🌙 回顾C文件接口

💫 C 读写文件

文件操作:

  • 首先要打开文件:打开成功,返回文件指针;打开失败,返回NULL
  • 最后要关闭文件

代码操作:

FILE *fopen(const char *path, const char *mode);
int fclose(FILE *fp);
1.C 写文件

采用方法:

我们可以fputs/fgets以字符串形式读写;也可以fprintf/fscanf格式化读写

代码操作:

int fputs(const char *s, FILE *stream);  向特定文件流写入字符串
int fprintf(FILE *stream, const char *format, ...);

举个栗子:

①如果以"w"模式打开文件,默认是文本读写,且会把原始内容清掉再写。

代码如下:

#include <stdio.h>

int main()
{
	FILE *fp = fopen("log.txt","w");
	if(fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	// 进行文件操作
	
	fclose(fp);
	return 0;	
}

运行结果:

②如果要以追加方式写,则要以"a" append模式打开文件

代码如下:

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


int main()
{
	FILE *fp = fopen("log.txt","a"); // 追加
	if(fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	// 进行文件操作
	const char* s = "hello world\n";
	fwrite(s,strlen(s),1,fp);
	return 0;	
}

运行结果:

2.C 读文件

解读:

fgets从特定文件流中按行读取,内容放在缓冲区。读取成功返回字符串起始地址,读失败返回NULL.

代码演示:

char *fgets(char *s, int size, FILE *stream); //size:为缓冲区大小
int fscanf(FILE *stream, const char *format, ...);

举个栗子:

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


int main()
{
	FILE *fp = fopen("./log.txt","r");
	if(fp == NULL)
	{
		perror("fopen");
		return 1;
	}

	// 进行文件操作
	char buffer[64];
	while(fgets(buffer,sizeof(buffer),fp))
	{
		printf("%s",buffer);//把我们读到的东西打出来
	}
	
	return 0;	
}

运行结果:

💫 关于stdin stdout stderr

概念分析:

C语言默认会打开三个输入输出流:stdin、stdout、stderr,它们的类型都是FILE*,C语言把它们当做文件看待,本质上我们最终都是访问硬件。C++中也有cin、cout、cerr,几乎所有语言都提供标准输入、标准输出、标准错误。

  • stdin对应的硬件设备是键盘
  • stdout对应显示器
  • stderr对应显示器

总结分析:

既然fputs是向文件写入,stdout也是FILE*类型,我们是不是可以向显示器标准输出打印了?这说明显示器被看做文件即:Linux下,一切皆文件。

举个栗子:

问题拓展:

fputs可以向一般文件(磁盘,也是硬件)或者硬件设备写入。这反映着Linux下一切皆文件

🌙 系统文件I/O

文件操作最终都是访问硬件(显示器、键盘、文件(磁盘))。众所周知,OS是硬件的管理者。所有语言上对“文件”的操作,都必须贯穿操作系统。然而OS不相信任何人,访问操作系统,就必须要通过系统接口!!

其实我们学过的几乎所有的语言中,fopen/fclose,fread/fwrite,fputs/fgets,fgets/fputs 等底层一定需要使用OS提供的系统调用接口,下面咱们就来学习文件的系统调用接口,才能做到万变不离其宗!!

图解:

💫 open & close

采用 man open 指令查看相关资料

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);//路径 + 选项
int open(const char *pathname, int flags, mode_t mode);

三个参数:

pathname: 要打开或创建的目标文件文件名
flags:    打开方式。传递多个标志位,下面的一个或者多个常量进行“或”运算,构成flags.
             O_RDONLY: 只读打开
             O_WRONLY: 只写打开
             O_RDWR  : 读写打开
          以上这三个常量,必须指定一个且只能指定一个
             O_CREAT : 若文件不存在,则创建它。同时需要使用mode选项,来指明新文件的访问权限
             O_APPEND: 追加写
mode: 	  设置默认权限信息 

返回值(int):

return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).
     成功: 新打开的文件描述符 
     失败: -1

采用 man close 指令查看相关资料

#include <unistd.h>

int close(int fd);

举个栗子:

代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
	int fd = open("./log.txt",O_WRONLY | O_CREAT);
	// int fd = open("./log.txt",O_WRONLY | O_CREAT,0644);
	if(fd < 0)
	{
		printf("open error\n");
		// return 1;
	}
	close(fd);
	return 0;	
}

运行结果:

问题分析:

可以看到权限完全是混乱的!这是因为,没有这个文件,要创建它,系统层面就必须指定权限是多少!我们采用权限设置的八进制方案

代码再次更新:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
	//int fd = open("./log.txt",O_WRONLY | O_CREAT);
	int fd = open("./log.txt",O_WRONLY | O_CREAT,0644);
	if(fd < 0)
	{
		printf("open error\n");
		return 1;
	}
	close(fd);
	return 0;	
}

运行更新结果:

分析结果:

之前我们在语言层面,创建时就是一个正常权限,我根本就不关心什么只写、创建、权限这些与系统强相关的概念。语言为我们做了封装,我用就好了

fopen("./log.txt", "w");
int fd = open("./log.txt", O_WRONLY | O_CREAT, 0644);

那第二个参数flags(int)为什么要把模式 | 在一起呢?这是一种用户层给内核传递标志位的常用做法。int有32个bit位,一个bit代表一个标志,就可以传递多个标志位且位运算效率较高。这些O_RDONLY、O_WRONLY、O_RDWR 都是只有一个比特位是1的数据,并且相互不重复,这样 |在一起,就能传递多个标志位。

我们可以来打开/usr/include/bits/fcntl-linux.h这个文件查看

💫 write & read

采用 man write 指令查看相关资料

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
参数:
    buf: 用户缓冲区
    count: 期望写的字节数
返回值:实际写入的字节数

举个栗子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd = open("./log.txt",O_WRONLY | O_CREAT,0644);
	if(fd < 0)
	{
		printf("open error\n");
		return 1;
	}
	const char* msg = "more then words\n";
	int cnt = 5;
	while(cnt--)
	{
		write(fd,msg,strlen(msg));
	}
	close(fd);
	return 0;	
}

运行结果:

问题拓展:

注意小细节,写入文件的过程中,不需要写入\0!因为\0是C语言层面上规定字符串的结束标志,而写入文件关心的是字符串的内容,不需要\0标定字符串结束。


采用 man read 指令查看相关资料

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
参数:
    buf: 读到的内容放在用户层缓冲区中,也就是自己定义缓冲区
    count: 期望读多少个字节
返回值:实际读多少个字节

举个栗子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
	int fd = open("./log.txt",O_RDONLY);
	if(fd < 0)
	{
		printf("open");
		return 1;
	}
	
	char buffer[1024];
	ssize_t s = read(fd,buffer,sizeof(buffer)-1);
	if(s > 0)
	{
		buffer[s] = 0;
		printf("%s",buffer);
	}
	close(fd);
	return 0;	
}

运行结果:

问题拓展:

我们把读到的内容当做一个长字符串处理,写入时不写\0,读也就不会读到,因此需要在末尾添加\0,以字符串打印出来。

🌙 文件描述符fd

问题提出:

open函数的返回值是所谓的文件描述符,既然类型为int,我就好奇它的值是多少呢?

再次分析:

如果我们连续打开若干文件,会发现打印3456… 我们知道打开文件失败返回-1,那么012去哪了呢?012消失的原因,要么是不让用,要么是被别人占用。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
        int fd1 = open("./log1.txt",O_WRONLY | O_CREAT, 0644);
        int fd2 = open("./log2.txt",O_WRONLY | O_CREAT, 0644);
	int fd3 = open("./log3.txt",O_WRONLY | O_CREAT, 0644);
	int fd4 = open("./log4.txt",O_WRONLY | O_CREAT, 0644);
	
	printf("fd:%d\n",fd1);
	printf("fd:%d\n",fd2);
	printf("fd:%d\n",fd3);
	printf("fd:%d\n",fd4);

	close(fd1);
	close(fd2);
	close(fd3);
	close(fd4);
	return 0;	
}

总结分析:

事实上,当我们的程序运行起来变成进程,默认情况下,OS会帮助我们打开三个标准输入输出,012其实分别对应的就是标准输入、标准输出、标准错误。刚刚我们还提到语言上的stdin标准输入、stdout标准输出、stderr标准错误,对应硬件设备也是键盘、显示器、显示器,冥冥之中,这一定是有关联的,不过我们暂时先不考虑语言和系统上如何对应。

这样文件描述符被分配为01234678… 这样从0开始,连续的小整数,会让我们联想到数组下标!

验证:012代表标准输入、标准输出、标准错误

💫 file descriptor

文字描述:

众所周知,所有的文件操作都是进程执行对应的函数,即本质上是进程对文件的操作。

  • 如果一个文件没有被打开,这个文件是在磁盘上。如果我创建一个空文件,该文件也是要占用磁盘空间的,因为文件的属性早就存在了(包括名称、时间、类型、大小、权限、用户名所属组等等),属性也是数据,所谓“空文件”是指文件内容为空。

即磁盘文件 = 文件内容 + 文件属性。事实上,我们之前所学的所有文件操作都可以分为两类:对文件内容的操作 + 对文件属性的操作(fseek、ftell、rewind、chmod、chgrp等等).

  • 要操作文件,必须打开文件(C语言fopen、C++打开流、系统上open),本质上,就是文件相关的属性信息从磁盘加载到内存。

操作系统中存在大量进程,进程可以打开多个文件,即进程 : 文件 = 1 : n ,系统中可能存在着更多的打开的文件(暂时不考虑一个文件被多个进程打开的特殊情况)。那么,OS要不要把打开的文件在内存中(系统中)管理起来呢?那么就要上管理的六字真言:先描述,再组织!

  • 打开的这么多文件,怎么知道哪些是我们进程的呢?操作系统为了让进程和文件之间产生关联,进程在内核创建struct files_struct 的结构,这个结构包含了一个数组 struct file* fd_array[] ,也就是一个指针数组,把表述文件的结构体地址填入到特定下标中。

图解:

分析:

那么现在就能解释了为什么打开文件返回的是3:新打开一个文件本质是内核会为我们描述struct file结构,再把struct file地址填入到fd_array[]数组下标去,因为012已经被占用了,于是填到3号下标,因此在上层可以拿到3.

这也解释了为什么write和read这样的系统调用接口为什么一定要传入文件描述符fd:执行系统调用接口是进程执行的,通过进程PCB,找到自己打开的文件列表,通过fd索引数组找到对应的文件,从而对文件进行操作。

总结:

文件描述符fd,本质是内核中进程和打开文件关联数组下标

💫 理解一切皆文件

对于键盘显示器等等这些外设,一定都有比如像read、write读写方法,因为由冯诺依曼体系结构知,外设是要和内存打交道IO的。这可能有些奇怪,比如键盘能读我知道,但能写吗?难道我键盘安安静静的自己就开始动了?!注意,我们有统一的读写方法,但不代表非要每一个都实现,比如键盘就可以没有写方法,即方法为空。

因为它们的硬件结构不同,这些方法在底层实现是完全不一样的!这些方法都是在硬件的驱动层完成的。那又是如何做到一切皆文件的呢?Linux中做了软件的虚拟层vfs(虚拟文件系统),会统一维护每一个打开文件的结构体struct file.

回忆C++中的多态,我们可以编写一个父类(甚至是纯虚的,相当于定义一个接口类),子类继承父类,重写函数。我们让父类指针指向不同的子类对象,就会调用对应的方法。那么在C语言中,可以通过函数指针,做到调用同一个方法,指向不同对象时可以执行不同的方法,从而实现多态的性质。

我们在每个struct file当中包含上一大坨的函数指针,这样,在struct file上层看来所有的文件都是调用统一的接口;在底层我们通过函数指针指向不同硬件的方法。

同样在继承体系中,我甚至也不关心你到底是那个子类,比如,动物基类Animal被猫狗鸡鸭鹅都继承了,里面有一个eat方法,基类指针指向猫就调用猫的eat,基类指针指向狗就调用狗的eat… 这样看去我们就实现了“一切皆动物”,可以理解为C++的多态是漫长的软件开发摸索中实现**“一切皆…”**的高级版本/语言版本。

💫 文件描述符的分配规则

代码分析:

问题分析:

我把0关掉后,再打开文件是分配的文件描述符就是0,把1关掉分配的就是1

文件描述符的分配规则

每次给新文件分配的fd,是从fd_array[]中找一个最小的、未被使用的作为新的fd.这其实很好理解,打开的文件要和进程产生关联,就要线性遍历数组中找一个未被使用的下标,填入文件地址。

🌙 重定向

💫 输出重定向

问题抛出:

有没有细心的同学,上面我们唯独没有关闭1,我们现在上手试一下。按照文件描述符的规则,再打开就是打印我们刚刚关闭的1

问题分析

本来应该显示到显示器中,却被打印到文件内部,这种行为我们早就知道叫做输出重定向。咱们无意之间居然完成了一次重定向操作,为什么是这样呢?

这是因为:我们以上来就close(1), 断开了和显示器文件的关系,相当于置NULL,对于新打开的log.txt,根据文件分配规则,1是指向log.txt的。

图解:

思考:

printf底层是在做什么?事实上,它本质是向标准输出(stdout)打印 ——

int fprintf(FILE *stream, const char *format, ...);
stdout -> FIEL{fileno = 1} -> log.txt// stdout只认识1,只对1输入输出

这就是重定向的本质:在OS内部,更改fd对应的内容的指向!!

💫 追加重定向

追加重定向与输出重定向唯一的差别就是在打开方式上,增加O_APPEND选项。

💫 输入重定向

输入重定向就是把本来应该从键盘获取内容变成从文件中获取。

char *fgets(char *s, int size, FILE *stream); //详见1.1节

💫 dup2

分析:

如上我通过关闭文件然后再打开文件这样重定向,但是情况不会总是这样理想。

比如两个文件描述符13都已经被打开,如何实现重定向呢?我们勇敢的推测,既然在语言层调用时接口函数只认1,那么只需要把文件描述符表的3中的内容拷贝到1中 ,就实现了原本应向显示器文件写入,而现在向log.txt写入。

图解:

总结:

dup2就是用来做这个操作的。

#include <unistd.h>

int dup2(int oldfd, int newfd); //oldfd->newfd

dup2() makes newfd be the copy of oldfd, closing newfd first if necessary, but note the following:
*  If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
*  If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.

拷贝的是fd对应内容,最终相当于全部变成old

🌙 Linux一切皆文件

在冯诺依曼体系中,我们知道硬件有键盘、显示器、磁盘、网卡等外设,在IO过程中,外设任何的数据处理都需要把数据读到内存,处理完毕之后将内存中的数据刷新到外设当中。因为软硬件资源多,所以操作系统需要对其先描述,在组织。所以这些外设都有对应的结构体,对应着属性信息,同时,对应着自己的IO函数,具体硬件的读写方法都在应用匹配的驱动程序里。每种硬件的访问方法都是不一样的,而Linux一切皆文件是这样体现的:任何一个被打开的文件结构体对象struct file{ //各种文件的属性 }对象,不同的文件对应的读写方法不一样,struct file对象里面可以有很多的(*readp)()、(*writep)()函数指针,通过函数指针指向具体的读写方法。

站在struct file上层看来,所有的设备和文件,统一都是struct file->,就可以调用具体的设备方法了,所以在用户级看到的就是Linux下一切皆文件!

**上层调用不同的文件,底层可以调用不同的方法,在上层看来,只需要使用对应统一的文件,使用struct file,访问不同的文件,这是C语言实现多态的特征。这里struct file称为在操作系统层面上虚拟出来的文件对象vfs(虚拟文件系统)**不用关心底层差别,统一使用文件的接口方式进行文件操作

🌟结束语 

       今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。

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

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

相关文章

实验2-1 进程相关的系统调用

一、实验目的 学习Linux中与进程控制相关的系统调用&#xff0c;加深对进程、进程树等概念的理解。 二、实验内容 1. 学习使用以下几类系统调用&#xff0c;进行编程练习 获取进程的信息&#xff0c;getpid(), getppid() 父子进程控制&#xff0c;fork()&#xff0c;wai…

前端三剑客 —— JavaScript (第九节)

目录 内容回顾&#xff1a; 1.事件解除 2. Ajax jQuery选择器 回顾CSS选择器 CSS选择 1.基本选择器 2.包含选择器 3.伪类选择器 4.伪元素选择器 5.属性选择器 jQuery 库 jQuery 动画 系统动画 自定义动画 常见API操作 内容回顾&#xff1a; 1.事件解除 如果是使…

一文读懂RISC-V与ARM

RISC-V和ARM是近年来备受关注的两种处理器架构。RISC-V是一种基于精简指令集计算(RISC)原理的开源指令集架构(ISA)&#xff0c;而ARM是一种专有ISA&#xff0c;由于其长期存在于嵌入式系统和移动设备中&#xff0c;已成为嵌入式系统和移动设备的主导选择。市场以及多年积累的信…

安装图数据库Nebula Graph

前言 今年开始&#xff0c;很多机关单位、央国企都要求所有新建的信息系统必须走国产化技术路线&#xff0c;而且还要求满足“信创”要求。“信创”通俗来讲就是要自研&#xff0c;那种拿个开源套壳的都不满足信创要求。之前研究了一段时间的neo4j&#xff0c;显然neo4j不满足…

雪亮工程视频联网综合管理/视频智能分析系统建设方案(一)

一、行业背景 雪亮工程主要是针对农村地区治安防控的监控项目&#xff0c;在乡村的主干道、路口、人群聚集地部署高清摄像头&#xff0c;通过三级综治中心和指挥平台&#xff0c;将视频图像信息系统纵向下延至县、乡、村&#xff0c;同时利用系统拓展在安防、社会治理、智慧交…

MWeb Pro For Mac v4.5.9 强大的 Markdown 软件中文版

MWeb 是专业的 Markdown 写作、记笔记、静态博客生成软件&#xff0c;目前已支持 Mac&#xff0c;iPad 和 iPhone。MWeb 有以下特色&#xff1a; 软件下载&#xff1a;MWeb Pro For Mac v4.5.9 软件本身&#xff1a; 使用原生的 macOS 技术打造&#xff0c;追求与系统的完美结合…

Linux从入门到精通 --- 3.用户、权限

文章目录 第三章&#xff1a;3.1 root用户3.1.1 su3.1.2 exit3.1.3 sudo 3.2 用户和用户组3.2.1 用户组管理创建用户组删除用户组 3.2.2 用户管理创建用户删除用户查看用户所属组修改用户所属组 3.2.3 getent一&#xff1a;二&#xff1a; 3.3 查看权限控制信息3.3.1 认知权限信…

IDEA 控制台中文乱码 4 种解决方案

前言 IntelliJ IDEA 如果不进行相关设置&#xff0c;可能会导致控制台中文乱码、配置文件中文乱码等问题&#xff0c;非常影响编码过程中进行问题追踪。本文总结了 IDEA 中常见的中文乱码解决方法&#xff0c;希望能够帮助到大家。 IDEA 中文乱码 解决方案 一、设置字体为支…

软件安全评估之设计评审入门(上)

壹 基础概念 在软件开发生命周期&#xff08;Software Development Life Cycle&#xff0c;简称SDLC&#xff09;中&#xff0c;设计评审&#xff08;Design Review&#xff09;是一个关键的阶段&#xff0c;旨在确保软件设计满足项目需求和目标&#xff0c;并且能够高效、可靠…

GDAL源码剖析(九)之GDAL体系架构

GDAL源码剖析&#xff08;九&#xff09;之GDAL体系架构_gdal 源码-CSDN博客 在GDAL库中包含栅格数据的读写&#xff0c;矢量数据的读写&#xff0c;以及栅格和矢量数据的相关算法。下面主要对GDAL中栅格数据和矢量数据的体系架构做一个简单的说明。本人英文很烂&#xff0c;有…

集装箱5G智能制造工厂数字孪生可视化平台,推进企业数字化转型

集装箱5G智能制造工厂数字孪生可视化平台&#xff0c;推进企业数字化转型。在当下数字化转型的热潮中&#xff0c;集装箱5G智能制造工厂数字孪生可视化平台成为了推动企业转型升级的重要工具。这一平台将先进的5G技术与智能制造相结合&#xff0c;通过数字孪生技术实现生产过程…

数字化赋能农业创新发展新篇章:数字乡村建设推动农业现代化、提升农业综合效益与竞争力

目录 一、数字乡村建设的内涵与意义 二、数字化赋能农业创新发展的路径 1、推动智慧农业发展 2、加强农村电子商务建设 3、提升农业信息化水平 三、数字乡村建设推动农业现代化与提升综合效益与竞争力 1、推动农业现代化进程 2、提升农业综合效益 3、增强农业竞争力 …

HTML5+CSS3+JS小实例:图片切换特效之模糊变清晰

实例:图片切换特效之模糊变清晰 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, i…

Windows搭建Jellyfin影音服务结合内网穿透实现公网访问本地视频文件

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

大势智慧在出模型时输入七参数可以导出地方坐标系吗?

大势智慧自主研发的网格大师或者DasViewer有坐标转换功能&#xff0c;可以使用七参数计算功能转换到地方坐标&#xff0c;直接输以前的七参是不行的&#xff0c;需要准备源坐标和目标坐标。 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自…

【Unity】组件组合使用心得(单行可自动拓展Scroll View)

在这之前&#xff0c;一直是在使用Scroll View进行滑动内容设置&#xff0c;但设置的都是不明不白的&#xff0c;而且有的时候设置好了之后也不知道是为什么&#xff0c;总感觉哪里不对劲&#xff0c;而且好也不知道为什么好&#xff0c;可能是长时间在做管理上的内容&#xff…

淘宝详情API接口文档(java)get调用

淘宝详情API接口是用于获取淘宝商品详细信息的接口&#xff0c;它允许开发者通过发送请求&#xff0c;获取商品的描述、价格、评价等信息。下面是一个关于淘宝详情API接口的示例文档&#xff0c;包括接口地址、请求参数、响应参数等内容。 淘宝详情API接口文档 一、接口地址 …

Lobe UI - 基于 AntDesign 开发的 AIGC Web 应用的开源 UI 组件库

今天推荐一个可以快速开发 ChatGPT UI 界面的组件库&#xff0c;质量很高&#xff0c;拿来就能用。 Lobe UI 是由 lobehub 团队开发的一套 web UI 组件库&#xff0c;和我之前推荐的很多通用型的 UI 组件库不同&#xff0c;Lobe UI 是专门为目前火热的 AIGC 应用开发而打造&am…

分布式 SpringCloudAlibaba、Feign与RabbitMQ实现MySQL到ES数据同步

文章目录 ⛄引言一、思路分析⛅实现方式⚡框架选择 二、实现数据同步⌚需求分析⏰搭建环境⚡核心源码 三、测试四、源码获取⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助…

R语言绘制一次和二次相关性热图

在数据探索的过程中&#xff0c;我们往往会对数据与数据的相关性进行分析&#xff0c;例如我们常用的corrplot包&#xff0c;或者psych包中的corr.test函数&#xff0c;对两两变量间的相关性进行分析。我们常常会看到这样的相关性热图&#xff1a; 但有时变量间的关系并非线性…