Linux篇:文件管理

一、共识原理:
1. 文件=内容+属性,内容与属性都是数据,都要在磁盘中保存。
2. 文件分为打开的文件和没打开的文件。
3. 研究打开的文件:本质是研究进程和文件的关系,因为是进程负责打开文件。
4. 没打开的文件在存储介质(如磁盘上)存储。没有被打开的文件非常多,所以文件如何被分门别类地归置好成为了我们需要重视的问题,我们要快速地进行找到并增删查改文件。

二、研究被打开的文件:
1、文件被打开,必须先被加载到内存。而文件的属性必须先加载到内存,而内容是否加载取决于用户后续是否要将文件写入读取。
2、系统启动时默认以文件形式(文件指针/文件句柄file*)打开三个输入输出流。由此观之,一个进程可以打开多个文件。显然,操作系统内部,一定存在大量的被打开的文件!
3、那么OS(操作系统)如何管理这些被打开的文件呢?
先描述,再组织:在内核中,一个被打开的文件都必须有自己的文件打开对象(struct结构体),其中包含文件的很多属性。所有定义的数据结构对象在内核里就可以以链表的形式管理起来,从而对打开文件的管理就转变为对链表的增删查改。

4、常用接口:

①先打开一个文件试试:

#include <stdio.h>int main()
{//打开文件的路径和文件名,默认在当前路径下新建一个文件。FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);return 0;
}


②打开文件的路径和文件名,默认在当前路径下新建一个文件。(如果打开文件不存在,则会新建一个文件。)

当前路径即为进程当前路径cwd.

那让我们实操一下寻找一下进程的当前路径吧~

#include <stdio.h>
#include <unistd.h>int main()
{printf("Pid: %d\n", getpid());//打开文件的路径和文件名,默认在当前路径下新建一个文件。FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);sleep(1000);return 0;
}

如果用户更改了当前进程的cwd,就可以把文件新建到其他目录。
可以使用chdir()修改路径。

 

#include <stdio.h>
#include <unistd.h>int main()
{chdir("/home/aaa");//更改当前路径printf("Pid: %d\n", getpid());//打开文件的路径和文件名,默认在当前路径下新建一个文件。FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}fclose(fp);sleep(1000);return 0;
}

③ 写入(注:该接口返回值为实际写入基本单位个数,而非总大小):

#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{printf("Pid: %d\n", getpid());//打开文件的路径和文件名,默认在当前路径下新建一个文件。FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}const char *message = "hello Linux message";    //strlen(message)无需+1 ,因为/0是一种不可显字符,会被vim这样的文本编辑器解释成乱码。fwrite(message, strlen(message), 1, fp);        fclose(fp);return 0;
}

w:清空并写入。

重定向写入等同于fopen("log.txt", "w"):

echo "hello Linux" > log.txt

a:追加写入。

注:在c标准库里,fopen在语言层为用户malloc(FILE),故返回值为FILE*。


C语言默认在启动的时候会打开三个标准输入输出流文件:


stdin:键盘文件
stdout:显示器文件
stderr:显示器文件

C++默认在启动的时候会打开三个标准输入输出流文件:
cin:键盘文件
cout:显示器文件
cerr:显示器文件

#include <stdio.h>
#include <unistd.h>
#include <string.h>int main()
{printf("Pid: %d\n", getpid());FILE *fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}const char *message = "hello Linux message";//strlen(message)无需+1 fwrite(message, strlen(message), 1, stdout);fclose(fp);return 0;
}

三、过渡到系统,认识文件系统调用
文件其实是在磁盘上的,磁盘是外部设备,访问磁盘文件实际上是访问硬件!几乎所有的库只要是访问硬件设备,必定要封装系统调用接口!


1、open:返回文件描述符表下标

上图的大写选项均为宏,一个整数有32个比特位,一个比特位就能表示一种状态,所以在Linux中仅用一个整数就能同时传递多个比特位,向系统传递多个选项。接下来就让我们用代码阐述比特位级别的标志位传递方式:

#include <stdio.h>
//#include <unistd.h>
//#include <string.h>#define ONE (1<<0)// 1
#define TWO (1<<1)// 2
#define THREE (1<<2)// 4
#define FOUR (1<<3)// 8void show(int flags)
{if(flags&ONE) printf("hello function1\n");if(flags&TWO) printf("hello function2\n");if(flags&THREE) printf("hello function3\n");if(flags&FOUR) printf("hello function4\n");
}int main()
{show(ONE);printf("-----------------\n");show(TWO);printf("-----------------\n");show(ONE|TWO);printf("-----------------\n");show(ONE|TWO|THREE);printf("-----------------\n");show(ONE|TWO|THREE|FOUR);printf("-----------------\n");
}

 接下来让我们实战演练一下吧:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);if(fd < 0){printf("open file error\n");return 1;}return 0;
}

我们会发现一个奇怪的现象,我们打开文件时设置权限为0666,可为何log.txt的权限为0664呢?

这当然是因为当前文件的权限掩码为0022

那要是我们一定要创建文件权限为0666呢?main函数中umask清零即可。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{umask(0);//权限掩码清零int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);if(fd < 0){printf("open file error\n");return 1;}return 0;
}

2、close

3、write(覆盖式向文件写入,但不会清空)

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{umask(0);//file descriptor: 文件描述符,fd,intint fd = open("log.txt",O_WRONLY|O_CREAT,0666);if(fd < 0){printf("open file error\n");return 1;}const char *message = "hello file system call";write(fd, message, strlen(message));//无需+1close(fd);return 0;
}

其余库函数的底层原理都是对系统调用接口的封装。

四、访问文件的本质
1、操作系统内描述一个被打开文件信息的结构体:struct file。
直接或者间接包含如下属性:在磁盘的位置,文件基本属性(权限,大小,读写位置,打开者),文件的内核缓冲区信息,struct file *next指针,引用计数count,文件描述符表,对应打开文件的缓冲区字段和维护信息。(该结构体对象属于用户,不属于操作系统)

2、而现在知道,文件描述符就是从 0 开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了FILE 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表 files_struct, 该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。
3、
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{umask(0);//file descriptor: 文件描述符,fd,intint fd = open("log.txt",O_WRONLY|O_CREAT,0666);if(fd < 0){printf("open file error\n");return 1;}printf("fd: %d\n", fd);const char *message = "hello file system call";write(fd, message, strlen(message));//无需+1close(fd);return 0;
}

可是文件描述符表的下标为什么是从三开始呢?

因为C语言默认在启动的时候,会打开三个标准输入流。
以下对应的文件描述符为:
stdio:键盘文件 0
stdout:显示器文件 1
stderr:显示器文件 2

4、所以这只是C语言的特性吗?不是,这是操作系统的特性进程,默认会打开键盘,显示器,显示器。

5、FILE是C库自己封装的结构体,这里面必须封装文件描述符。

6、文件描述符的分配规则:从0下标开始,寻找最小的没有使用的数组位置,它的下标就是新文件的文件描述符。

总结:两层封装:①库函数封装系统调用接口 ②文件FILE*类型必定包含文件描述符

7、关闭文件的核心动作①引用计数--②文件描述符表数组下标置空。


五、输入输出重定向

1、重定向:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define filename "log.txt"int main()
{//close();int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);const char *msg = "hello Linux\n";int cnt = 5;while(cnt){write(fd, msg, strlen(msg));cnt--;}close(fd);return 0;
}

以下是关闭不同fd的结果: 

        close(0);

        close(1);

        close(2);

 由此观之,文件描述符表的分配规则为:从0下标开始,寻找最小的没使用的数组位置,它的下标就是新文件的文件描述符。

②再来做个小实验:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define filename "log.txt"int main()
{//close(1);int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}//printf("fd: %d\n", fd);       const char *msg = "hello Linux\n";int cnt = 5;while(cnt){write(1, msg, strlen(msg));cnt--;}close(fd);return 0;
}

 如上代码的结果是这样的:

[root@hecs-210801 lesson21]# ./mytest
hello Linux
hello Linux
hello Linux
hello Linux
hello Linux

关闭下标为1的fd:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define filename "log.txt"int main()
{close(1);int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);//该文件的fd变为1if(fd < 0){perror("open");return 1;}//printf("fd: %d\n", fd);       const char *msg = "hello Linux\n";int cnt = 5;while(cnt){write(1, msg, strlen(msg));cnt--;}close(fd);return 0;
}
[root@hecs-210801 lesson21]# ./mytest
[root@hecs-210801 lesson21]# cat log.txt
hello Linux
hello Linux
hello Linux
hello Linux
hello Linux

本该打印在显示器上的内容转而打印在了普通文件中,这就叫做输出重定向!

重定向的本质:是对进程的指定描述符表进行内核级别的文件对象地址作拷贝的工作。

2、重定向的接口:dup2

 ①输入重定向:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>#define filename "log.txt"int main()
{//close(1);int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}//printf("fd: %d\n", fd);       //重定向dup2(fd, 1);close(fd);const char *msg = "hello Linux\n";int cnt = 5;while(cnt){write(1, msg, strlen(msg));cnt--;}close(fd);return 0;
}

  ②输出重定向:

int main()
{int fd = open(filename, O_RDONLY);if(fd < 0){perror("open");return 1;}dup2(fd, 0);char inbuffer[1024];ssize_t s = read(0, inbuffer, sizeof(inbuffer)-1);if(s>0){inbuffer[s] = '\0';printf("echo %s\n", inbuffer);}close(fd);return 0;
}

3、 内存管理和文件操作解耦:进程历史打开的文件与进行的各种重定向关系都和未来进行程序替换无关!程序替换并不影响文件访问!

4、将stdout中内容写到stderr:

[root@hecs-210801 lesson21]# 2>&1

六、如何理解“一切皆文件”:
1、所有操作计算机的动作,都是以进程的形式进行操作的,所有访问文件的操作,最终都是由进程来访问文件的。

2、文件都可以以文件的形式被操作系统用open打开,每一个文件都要在内核中创建一个结构体对象(struct file),还创建了一个方法级对象(struct operation_func)(分为基类与派生类),通过struct file中的指针指向struct operation_func,其中的函数指针指向底层方法。

3、上层统一使用一个read接口,直接通过一个数据结构调用读方法写方法,它可以根据指针指向的不同,动态的根据不同的设备,使用不同的方法。(多态)

4、以上的设计称为VFS(虚拟文件系统)

七、重定向缓冲区

1、观察下列两组代码:

①C接口

#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{//C接口const char *fstr = "hello fwrite";printf("hello printf"); // stout ->1fprintf(stdout, "hello fprintf");// stout -> 1fwrite (fstr, strlen(fstr), 1, stdout);// fread, stout->1close(1);return 0;
}

输出结果:不打印任何内容 

[root@hecs-210801 lesson24]# make
gcc -o myfile myfile.c -std=c99
[root@hecs-210801 lesson24]# ./myfile

 ②系统调用接口

#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{//操作系统提供的systemcallconst char *str = "hello write";write(1, str, strlen(str));close(1);return 0;
}

输出结果:打印

[root@hecs-210801 lesson24]# make
gcc -o myfile myfile.c -std=c99
[root@hecs-210801 lesson24]# ./myfile
hello write[root@hecs-210801 lesson24]# 

2、为什么会出现这种现象呢?
系统调用接口能直接通过进程找到数据对象将数据写到了系统内部缓冲区中,close自动将数据刷新打印。而C语言缓冲区是用户级缓冲区,不是系统级别的缓冲区,一定不在操作系统内部!只有在合适的时候,c库才会调用对应的write接口,将数据写入到操作系统里面。(语言都属于用户层,不属于操作系统。)

而调用close把1号文件描述符关闭,进程退出时无法刷新C语言缓冲区,所以C程序没有对应的显示结果。而系统接口通过操作系统直接写到系统级缓冲区内部,直接交给操纵系统的数据自然而然就能被刷新出来啦!(目前我们认为,只要将数据刷新到了内核,数据就可以到硬件了。)(一般情况下,在进程退出的时候也会刷新完成区。)

当然,使用\n能够立刻清空缓冲区,显示器的文件的刷新方案是行刷新,所以在printf执行遇到\n的时候,将数据进行刷新。(刷新的本质就是将数据通过1号文件描述符和write写入到内核中)。

 这不由得让我们想起了之前所讲的exit和_exit,感兴趣的同学请自行阅读上一篇文章——进程控制哦。

3、(应用层)缓冲区刷新策略:
①无缓冲--直接刷新。
②行缓冲--不刷新,直到碰到\n。
③全缓冲--缓冲区满了,才刷新(普通文件写入)(重定向:若将显示器打印变成向文件打印,缓冲方案变成了全缓冲,遇到\n不再刷新)。

4、为什么要有这个缓冲区?
①解决效率问题-用户的效率问题。
②配合格式化。

5、fork创建子进程时:
操作系统在对这个缓冲区做写入的时候,发生写时拷贝,父子进程各自私有一份,最后被系统向文件刷新了两份同样的数据。
①重定向刷新方案发生更改
②数据在写入时在缓冲区由刷新改为暂存,因为缓冲区没有被写满。
③fork后父子退出时均要刷新缓冲区,且共享该缓冲区,各自刷新一次。

八、模拟实现

Mystdio.h

//Mystdio.h//#pragma once
#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__#include <string.h>#define SIZE 1024#define FLUSH_NOW 1 //立即刷新
#define FLUSH_LINE 2 //行刷新
#define FLUSH_ALL 4 //全刷新typedef struct IO_FILE{int fileno;int flag;char inbuffer[SIZE];//输入缓冲区char outbuffer[SIZE];//输出缓冲区int in_pos;int out_pos;
}_FILE;_FILE *_fopen(const char*filename, const char* flag);
int _fwrite(_FILE *fp, const char *s, int len);
void _fclose(_FILE *fp);#endif

Mystdio.c 

//Mystdio.c#include "Mystdio.h"#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>#define FILE_MODE 0666//"w","a","r"
_FILE *_fopen(const char*filename, const char* flag)
{assert(filename);assert(flag);int f = 0;int fd = -1;if(strcmp(flag,"w") == 0){f = (O_CREAT|O_WRONLY|O_TRUNC);fd = open(filename, f, FILE_MODE);}else if(strcmp(flag,"a") == 0){f = (O_CREAT|O_WRONLY|O_APPEND);fd = open(filename, f, FILE_MODE);}else if(strcmp(flag,"w") == 0){f = O_RDONLY;fd = open(filename, f);}else{return NULL;}if(fd == -1) return NULL;_FILE* fp =  (_FILE*)malloc(sizeof(_FILE));if(fp == NULL) return NULL;fp->fileno = fd;fp->flag = FLUSH_LINE;fp->out_pos = 0;return fp;}int _fwrite(_FILE *fp, const char *s, int len)
{memcpy(&fp->outbuffer[fp->out_pos], s, len);//没有做异常处理,也不考虑局部问题。fp->out_pos += len;if(fp->flag & FLUSH_NOW){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}else if(fp->flag & FLUSH_LINE){if(fp->outbuffer[fp->out_pos-1] == '\n'){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}}else if(fp->flag & FLUSH_ALL){if(fp->out_pos == SIZE){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}}return len;
}void _fflush(_FILE *fp)
{if(fp->out_pos > 0){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}
}void _fclose(_FILE *fp)
{if(fp == NULL) return;_fflush(fp);close(fp->fileno);free(fp);
}                    

main.c

//main.c
#include "Mystdio.h"
#include <unistd.h>
#define myfile "test.txt"int main()
{_FILE * fp = _fopen(myfile, "w");if(fp == NULL) return 1;const char *msg = "hello world\n";int cnt = 10;while(cnt){_fwrite(fp, msg, strlen(msg));//fflush(fp);sleep(5);cnt--;}_fclose(fp);return 0;
}

 

 

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

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

相关文章

红黑树(万字图文详解)

红黑树 1. 红黑树的概念2. 红黑树的性质3. 红黑树节点的定义4. 红黑树结构5. 红黑树的插入操作5.1 按照二叉搜索的树规则插入新节点5.2 检测新节点插入后&#xff0c;红黑树的性质是否造到破坏5.2.1 情况一: cur为红&#xff0c;p为红&#xff0c;g为黑&#xff0c;u存在且为红…

智能优化算法应用:基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜻蜓算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜻蜓算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

【springboot】宝塔简单部署springboot 配置https

宝塔简单部署springboot配置https 需求步骤1. springboot通过maven组件打成jar包2. 将jar包部署到宝塔上3. 下载安装nginx并创建网站节点4. 设置域名或者IP5. 设置反向代理:代理后端服务的ip和端口7. 配置SSL/TLS 需求 宝塔部署springboot项目,用nginx反向代理后端IP端口&…

深度学习黎明时期的LeNet:揭开卷积神经网络的序幕

在深度学习的历史长河中&#xff0c;Yann LeCun 的 LeNet 是一个里程碑式的研究成果&#xff0c;它为后来的卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;的发展奠定了基础。LeNet 的诞生标志着深度学习黎明时期的到来&#xff0c;为人工…

【已解决】在windows下,gitlab修改了密码导致remote: HTTP Basic: Access denied

背景 网上好多答案遇到此问题都是修改凭证,故写此文。 在控制面板》用户账户》凭据管理器》管理windows凭据》普通凭据 --找到对应的git地址&#xff0c;编辑用户名和密码 本文解决方案适用于windows下没有凭证的情况&#xff0c;因修改密码导致下拉代码出错的情况。 Git是常…

Scrapy框架内置管道之图片视频和文件(一篇文章齐全)

1、Scrapy框架初识&#xff08;点击前往查阅&#xff09; 2、Scrapy框架持久化存储&#xff08;点击前往查阅&#xff09; 3、Scrapy框架内置管道 4、Scrapy框架中间件&#xff08;点击前往查阅&#xff09; Scrapy 是一个开源的、基于Python的爬虫框架&#xff0c;它提供了…

JAVA配置jdk17 Graa1VM

按照网上内容下载好对应的jdk17版本的Graa1VM&#xff0c; 解压后&#xff0c;修改环境变量中的JAVA_HOME为当前的目录&#xff0c;例如 D:\ruanjian\jdk\gra_jdk17\graalvm-ce-java17-22.3.0 。 然后在命令行中输入java -version的时候&#xff0c; 返回的并不是 Graa1VM 相关…

文件权限中 chmod、u+x、u、r、w、x分别代表什么

Linux系统中的每个文件和目录都有访问许可权限&#xff0c;如下面所示&#xff1a; 要说清楚问题&#xff0c;我们截取一些内容&#xff1a; ypyubuntu:~$ ls -l drwxr-xr-- 2 ypy ypy 4096 Nov 30 18:33 Desktop/ drwxr-xr-- 2 ypy ypy 4096 Nov 30 18:33 Documen…

【扩散模型】DDIM从原理到实战

DDIM从原理到实战 1. DDIM简介2. 实战DDIM2.1 载入一个预训练过的pipeline2.2 DDIM采样2.3 反转&#xff08;invert&#xff09; 3. 组合封装参考资料 DDPM过程的一个问题是在训练后生成图像的速度。当然&#xff0c;我们可能能够生成令人惊叹的图像&#xff0c;但生成一张图像…

本地训练,立等可取,30秒音频素材复刻霉霉讲中文音色基于Bert-VITS2V2.0.2

之前我们使用Bert-VITS2V2.0.2版本对现有的原神数据集进行了本地训练&#xff0c;但如果克隆对象脱离了原神角色&#xff0c;我们就需要自己构建数据集了&#xff0c;事实上&#xff0c;深度学习模型的性能和泛化能力都依托于所使用的数据集的质量和多样性&#xff0c;本次我们…

【密码学引论】序列密码

第五章 序列密码 1、序列密码 定义&#xff1a; 加密过程&#xff1a;把明文与密钥序列进行异或运算得到密文解密过程&#xff1a;把密文与密钥序列进行异或运算得到明文以字/字节为单位加解密密钥&#xff1a;采用一个比特流发生器随机产生二进制比特流 2、序列密码和分组密…

【docker】docker安装与优化

目录 一、安装Docker 1、关闭防火墙 2、安装依赖包 3、设置阿里云镜像源 4、安装Docker-CE社区版并设置为开机自启动 5、查看Docker信息 二、设置镜像加速 1、申请加速地址 2、实现加速操作 三、网络优化 1、如何网络优化 2、具体操作 四、docker-server端配置文件…

汇编实验2-2 查找匹配字符串笔记

一、数据段 1.字符串结尾&#xff1a;13,10&#xff0c;$ 2.设置格式控制字符串(这样就不用再写clrf函数了) 3.设置存关键字和句子的地址标签&#xff0c;以关键字为例 二、代码段 1.输入字符串 2.字符串比较 2.1 每次的比较长度&#xff0c;KLEN->CL 2.2 设置目标串起始…

【Linux系统编程】操作系统详解(什么是操作系统?为什么会存在操作系统?设计操作系统的目的是什么?)

目录 一、前言 二、 什么是操作系统 &#x1f4a6;操作系统的引入 &#x1f4a6;操作系统的概念理解 &#x1f4a6;操作系统设计的目的与定位 &#x1f4a6;总结 二、操作系统之上之下分别有什么 三、深度理解操作系统的“管理” &#x1f4a6;场景理解 &#x1f4a6;操…

2023信息技术应用创新论坛|云轴科技ZStack分享云原生超融合在智慧交通的应用

11月25日&#xff0c;2023信息技术应用创新论坛在常州开幕。江苏省工业和信息化厅副厅长池宇、中国电子工业标准化技术协会理事长胡燕、常州市常务副市长李林等领导出席论坛并致辞。中国工程院院士郑纬民出席并作主题报告。来自产学研用金等各界的千余名代表参加本次论坛。 在“…

GitHub上8个强烈推荐的 Python 项目

文章目录 前言1. Manim2. DeepFaceLab3. Airflow4. GPT-25. XSStrike6. 谷歌图片下载7. Gensim8. SocialMapper总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③…

【刷题笔记】加油站||符合思维方式

加油站 文章目录 加油站1 题目描述2 思路3 解题方法 1 题目描述 https://leetcode.cn/problems/gas-station/ 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消…

Element-UI Upload 手动上传文件的实现与优化

文章目录 引言第一部分&#xff1a;Element-UI Upload 基本用法1.1 安装 Element-UI1.2 使用 <el-upload> 组件 第二部分&#xff1a;手动上传文件2.1 手动触发上传2.2 手动上传时的文件处理 第三部分&#xff1a;性能优化3.1 并发上传3.2 文件上传限制 结语 &#x1f38…

Jmeter工具学习三——CSV文件、关联、断言

Jmeter学习三——CSV文件和关联 jmeter做功能测试和做性能测试的区别CSV数据文件设置&#xff08;读取外部文件&#xff0c;进行分数据驱动&#xff09;文件设置字段介绍&#xff1a;文件名文件编码如果出现编码问题导致的乱码&#xff0c;如何解决&#xff1f; 变量名忽略首行…

【MATLAB】LMD分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 LMDFFTHHT组合算法是一种基于局部均值分解&#xff08;LMD&#xff09;、快速傅里叶变换&#xff08;FFT&#xff09;和希尔伯特-黄变换&#xff08;HHT&#xff09;的组合算法。 LMD是…