《Linux C编程实战》笔记:实现自己的ls命令

关键函数的功能及说明

1.void display_attribute(struct stat buf,char *name)

函数功能:打印文件名为name的文件信息,如

含义分别为:文件的类型和访问权限,文件的链接数,文件的所有者,文件所有者所属的组,文件大小,文件创建的时间

2.void dispaly_single(char *name)

函数功能:输出文件的文件名,如果命令中没有-l选项,则输出文件名时要保证上下对齐,如:

3.void display(int flag,char *pathname)

函数功能:根据命令行参数(存放在flag中)和完整路径名(存放在pathname中)显示目标文件,参数flag可以取以下值或者它们的组合

4.void diaplay_dir(int flag_param,char *path)

函数功能:为显示某个目录下的文件做准备,参数flag_param用于在调用display函数时作为其参数flag的实参,path是要显示的目录

函数流程

准备工作代码:

#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
#include<dirent.h>
#include<linux/limits.h>
#include<pwd.h>
#include<grp.h>
#include<algorithm>
using namespace std;#define PARAM_NONE 0//无参数
#define PARAM_A 1//-a 显示所有文件
#define PARAM_L 2  //-l 一行只显示一个文件的详细信息
#define MAXROWLEN 80    //一行显示的最多字符数int g_leave_len=MAXROWLEN;//一行剩余长度,用于输出对齐
int g_maxlen;             //存放某目录下最长文件名的长度
//自定义错误处理函数
void my_err(const char *err_string,int line){fprintf(stderr,"line:%d ",line);perror(err_string);exit(1);
}
int cmp(const void *a, const void *b)
{return ((char *)a)[0] - ((char *)b)[0];
}

顺便了解

pwd.hgrp.h 是两个C语言标准库头文件,用于处理用户和组的信息,通常用于UNIX和类UNIX系统。

  1. pwd.h - Password Database:

    该头文件定义了与用户账户信息相关的结构和函数。主要包括获取和处理用户账户信息的功能,例如用户名、用户ID(UID)、组ID(GID)、用户家目录等。一些常见的函数和结构体包括:
    • getpwnam:通过用户名获取用户信息。
    • getpwuid:通过用户ID获取用户信息。
    • struct passwd:用于存储用户账户信息的结构体。
  2. grp.h - Group Database:

    该头文件定义了与用户组信息相关的结构和函数。主要包括获取和处理用户组信息的功能,例如组名、组ID(GID)、组成员等。一些常见的函数和结构体包括:
    • struct group:用于存储用户组信息的结构体。
    • getgrgid:通过组ID获取组信息。
    • getgrnam:通过组名获取组信息。

如果你不清楚函数代码实在干嘛,可以看一下上面的运行结果,这样才知道到底要输出什么

display_attribute代码:

//获取文件属性并打印
void display_attribute(struct stat buf,char *name){char buf_time[32];struct passwd *psd;//从该结构体中获取文件所有者的用户名struct group *grp;//从该结构体中获取文件所有者所属组的组名//获取并打印文件类型if(S_ISLNK(buf.st_mode))printf("l");//符号链接else if(S_ISREG(buf.st_mode))printf("-");//普通文件else if(S_ISDIR(buf.st_mode))printf("d");//目录else if(S_ISCHR(buf.st_mode))printf("c");//字符设备文件else if(S_ISBLK(buf.st_mode))printf("b");//块设配文件else if(S_ISSOCK(buf.st_mode))printf("s");//套接字文件else if(S_ISFIFO(buf.st_mode))printf("f");//FIFO文件//获取并打印文件所有者的权限if(buf.st_mode&S_IRUSR)printf("r");else printf("-");if(buf.st_mode&S_IWUSR)printf("w");else printf("-");if(buf.st_mode&S_IXUSR)printf("x");else printf("-");//获取并打印与文件所有者同组的用户对该文件的操作权限if(buf.st_mode&S_IRGRP)printf("r");else printf("-");if(buf.st_mode&S_IWGRP)printf("w");else printf("-");if(buf.st_mode&S_IXGRP)printf("x");else printf("-");//获取并打印其他用户对该文件的操作权限if(buf.st_mode&S_IROTH)printf("r");else printf("-");if(buf.st_mode&S_IWOTH)printf("w");else printf("-");if(buf.st_mode&S_IXOTH)printf("x");else printf("-");printf("   ");//根据uid与gid获取文件所有者的用户名与组名psd=getpwuid(buf.st_uid);grp=getgrgid(buf.st_gid);printf("%4d",buf.st_nlink);//打印文件的链接数printf("%-8s",psd->pw_name);printf("%-8s",grp->gr_name);printf("%6d",buf.st_size);//打印文件的大小strcpy(buf_time,ctime(&buf.st_mtime));buf_time[strlen(buf_time)-1]='\0';//去掉换行符printf("  %s",buf_time);//打印文件的时间信息
}

struct stat这个结构体在《Linux C编程实战》笔记:文件属性操作函数-CSDN博客 这里放过具体的信息,可以看到主要是从st_mode获取信息

S_ISLNK 是一个宏(macro),通常用于检查给定文件的模式是否表示一个符号链接(Symbolic Link)。这个宏在UNIX和类UNIX系统的系统编程中很常见,特别是在处理文件和目录时。

S_ISLNK 的定义通常在 <sys/stat.h> 头文件中,这个头文件包含了有关文件状态(file status)的相关宏和结构体定义。它的典型用法是与 st_mode 结构成员一起使用

其他的宏都是类似的作用,用来检查文件具体是那种类型的。

buf.st_mode & S_IRGRP 是一个位运算的操作,用于检查给定文件的权限位

S_IRGRP之类的文件权限的宏在 《Linux C编程实战》笔记:文件读写-CSDN博客 有具体解释过,通过这个操作可以得知文件的具体权限

getpwuidgetgrgid 函数来获取文件或目录的所有者(user)和所属组(group)的相关信息。这两个函数通常与文件状态结构 struct stat 中的 st_uid(用户ID)和 st_gid(组ID)成员一起使用。

#include <pwd.h>struct passwd *getpwuid(uid_t uid);
#include <grp.h>struct group *getgrgid(gid_t gid);
struct passwd {char   *pw_name;       // 用户名char   *pw_passwd;     // 加密后的密码uid_t   pw_uid;        // 用户IDgid_t   pw_gid;        // 主组IDchar   *pw_gecos;      // 用户的真实姓名等描述信息char   *pw_dir;        // 用户的主目录char   *pw_shell;      // 用户的登录shell
};
struct group {char   *gr_name;       // 组名char   *gr_passwd;     // 加密后的密码(在 /etc/gshadow 中)gid_t   gr_gid;        // 组IDchar  **gr_mem;        // 用户名的数组,表示属于这个组的成员
};

ctime的具体用法也在 《Linux C编程实战》笔记:文件属性操作函数-CSDN博客 有提到

%-8s的格式时,它包含两个部分:-8 是一个标志(flag),表示左对齐输出,而 %s 表示将一个字符串插入到该位置。

  • %s:插入一个字符串。
  • -8:左对齐,并占用最少8个字符的宽度。如果字符串长度不足8个字符,将用空格在右侧填充。

dispaly_single代码:

//在没有使用-l选项时,打印一个文件名,打印时上下对齐
void display_single(char *name){int i,len;//如果本行不足以打印一个文件名则换行if(g_leave_len<g_maxlen){printf("\n");g_leave_len=MAXROWLEN;}len=strlen(name);len=g_maxlen-len;printf("%-s",name);//为了保持对齐,需要填充空格来实现和最长文件名占位相同for(i=0;i<len;i++){printf(" ");}printf("  ");g_leave_len-=(g_maxlen+2);//改行剩下的长度减去最长文件名加两个空格的长度
}

这里主要是格式控制,在准备代码里有每个变量具体的含义,每次输出主要是更新g_leave_len

display代码:

/*
*    根据命令行参数和完整路径名显示目标文件
*    参数flag:命令行参数
*    参数pathname:包含了文件名的路径名
*/
void display(int flag,char *pathname){int i,j;struct stat buf;char name[NAME_MAX+1];//从路径中解析出文件名,其实就是最后一个/后面的字符串了//书中代码是这么来获得的,感觉应该有更好的方法(C语言好像没有split函数,多少会麻烦一些)for(i=0,j=0;i<strlen(pathname);i++){if(pathname[i]=='/'){j=0;continue;}name[j++]=pathname[i];}name[j]='\0';//用lstat而不是stat以方便解析链接文件if(lstat(pathname,&buf)==-1)my_err("stat",__LINE__);switch (flag){case PARAM_NONE://没有选项if(name[0]!='.'){//隐藏的.目录文件就忽略display_single(name);}break;case PARAM_A:display_single(name);break;case PARAM_L:if(name[0]!='.'){display_attribute(buf,name);//详细信息printf(" %s-\n",name);//最后打印文件名}break;case PARAM_A+PARAM_L:display_attribute(buf,name);printf(" %-s\n",name);break;default:break;}
}

NAME_MAX在《Linux C编程实战》笔记:目录操作-CSDN博客

lstat在《Linux C编程实战》笔记:文件属性操作函数-CSDN博客

display_dir代码:

void display_dir(int flag_param,char *path){DIR *dir;struct dirent *ptr;int count=0;char filenames[256][PATH_MAX+1],temp[PATH_MAX+1];//获取该目录下文件总数和最长的文件名dir=opendir(path);if(dir==nullptr)my_err("opendir",__LINE__);while((ptr=readdir(dir))!=nullptr){if(g_maxlen<strlen(ptr->d_name))g_maxlen=strlen(ptr->d_name);//更新最长的文件名长度count++;}closedir(dir);if (count>256){my_err("too maney files under this dir",__LINE__);}int i,j,len=strlen(path);//获取该目录下所有的文件名dir=opendir(path);for(i=0;i<count;i++){ptr=readdir(dir);if(ptr==nullptr)my_err("readdir",__LINE__);strncpy(filenames[i],path,len);filenames[i][len]='\0';strcat(filenames[i],ptr->d_name);//把目录名称和目录下的文件名拼一起filenames[i][len+strlen(ptr->d_name)]='\0';}//给文件名排序//源代码里用的是冒泡,排序不是重点,用一下stl算了,偷个懒qsort(filenames,count,sizeof(filenames[0]),cmp);for(i=0;i<count;i++)display(flag_param,filenames[i]);closedir(dir);//如果命令行中没有-l选项,打印一个换行符if((flag_param&PARAM_L)==0)printf("\n");
}

打开目录和遍历目录的操作我已经演示过了了《Linux C编程实战》笔记:目录操作-CSDN博客

main函数代码:

int main(int argc,char **argv){int i,j,k,num;char path[PATH_MAX+1];char param[32];//保存命令行参数,目标文件名和目录名不在这里int flag_param=PARAM_NONE;//参数种类struct stat buf;//命令行参数的解析,分析 -l、-a、-al、-la选项j=0;num=0;for(i=1;i<argc;i++){if(argv[i][0]=='-'){//是一个命令for(k=1;k<strlen(argv[i]);k++,j++){param[j]=argv[i][k];//把命令的参数(a或l)都放到param数组里}num++;//保存"-"的个数}}//j现在是param数组的长度for(int i=0;i<j;i++){//遍历param数组,看参数都是什么if(param[i]=='a'){flag_param|=PARAM_A;continue;}else if(param[i]=='l'){flag_param|=PARAM_L;continue;}else{//a,l以外的参数,不支持printf("my_ls:invalid option -%c\n",param[i]);exit(1);}}param[j]='\0';//如果没有输入文件名或目录则显示当前目录if((num+1)==argc){//这表示除了参数,没有输入文件或目录strcpy(path,"./");//path变成当前目录path[2]='\0';display_dir(flag_param,path);//用显示目录的函数return 0;}//开始遍历文件或目录i=1;do{//如果不是目标文件或目录,解析下一个命令行参数if(argv[i][0]=='-'){i++;continue;}else{strcpy(path,argv[i]);//如果目标文件或目录不存在,报错并退出if(stat(path,&buf)==-1)my_err("stat",__LINE__);if(S_ISDIR(buf.st_mode)){//argv[i]是一个目录//如果目录的最后一格字符不是'/',就把'/'加上if(path[strlen(argv[i])-1]!='/'){path[strlen(argv[i])]='/';path[strlen(argv[i])+1]='\0';}else path[strlen(argv[i])]='\0';//也是调用显示目录的函数display_dir(flag_param,path);i++;}else{//argv[i]是一个文件display(flag_param,path);i++;}}} while (i<argc);return 0;
}

这里说一下argc和argv,不然可能搞不懂。

运行程序时,如果啥都不跟,直接./a.out这种的话,argc默认为1,argv[0]是程序名字,如果后面带了参数,比如这样

./a.out arg1 arg2 arg3

那argc就是4,argv[1],argv[2],argv[3]存的是参数的字符串

所以argv代码里都是从1开始,因为argv[0]默认存的是程序名称。

编译运行一下

结果还是很完美的

一整个源码也在这,方便直接抄了,但是没有注释

#include<cstdlib>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
#include<dirent.h>
#include<linux/limits.h>
#include<pwd.h>
#include<grp.h>
#include<algorithm>
using namespace std;
#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define MAXROWLEN 80int g_leave_len=MAXROWLEN;
int g_maxlen;
//自定义错误处理函数
void my_err(const char *err_string,int line){fprintf(stderr,"line:%d ",line);perror(err_string);exit(1);
}
int cmp(const void *a, const void *b)
{return ((char *)a)[0] - ((char *)b)[0];
}
void display_attribute(struct stat buf,char *name){char buf_time[32];struct passwd *psd;struct group *grp;if(S_ISLNK(buf.st_mode))printf("l");else if(S_ISREG(buf.st_mode))printf("-");else if(S_ISDIR(buf.st_mode))printf("d");else if(S_ISCHR(buf.st_mode))printf("c");else if(S_ISBLK(buf.st_mode))printf("b");else if(S_ISSOCK(buf.st_mode))printf("s");else if(S_ISFIFO(buf.st_mode))printf("f");if(buf.st_mode&S_IRUSR)printf("r");else printf("-");if(buf.st_mode&S_IWUSR)printf("w");else printf("-");if(buf.st_mode&S_IXUSR)printf("x");else printf("-");if(buf.st_mode&S_IRGRP)printf("r");else printf("-");if(buf.st_mode&S_IWGRP)printf("w");else printf("-");if(buf.st_mode&S_IXGRP)printf("x");else printf("-");if(buf.st_mode&S_IROTH)printf("r");else printf("-");if(buf.st_mode&S_IWOTH)printf("w");else printf("-");if(buf.st_mode&S_IXOTH)printf("x");else printf("-");printf("   ");psd=getpwuid(buf.st_uid);grp=getgrgid(buf.st_gid);printf("%4d",buf.st_nlink);printf("%-8s",psd->pw_name);printf("%-8s",grp->gr_name);printf("%6d",buf.st_size);strcpy(buf_time,ctime(&buf.st_mtime));buf_time[strlen(buf_time)-1]='\0';printf("  %s",buf_time);
}void display_single(char *name){int i,len;if(g_leave_len<g_maxlen){printf("\n");g_leave_len=MAXROWLEN;}len=strlen(name);len=g_maxlen-len;printf("%-s",name);for(i=0;i<len;i++){printf(" ");}printf("  ");g_leave_len-=(g_maxlen+2);
}
void display(int flag,char *pathname){int i,j;struct stat buf;char name[NAME_MAX+1];for(i=0,j=0;i<strlen(pathname);i++){if(pathname[i]=='/'){j=0;continue;}name[j++]=pathname[i];}name[j]='\0';if(lstat(pathname,&buf)==-1)my_err("stat",__LINE__);switch (flag){case PARAM_NONE:if(name[0]!='.'){display_single(name);}break;case PARAM_A:display_single(name);break;case PARAM_L:if(name[0]!='.'){display_attribute(buf,name);printf(" %s-\n",name);}break;case PARAM_A+PARAM_L:display_attribute(buf,name);printf(" %-s\n",name);break;default:break;}
}void display_dir(int flag_param,char *path){DIR *dir;struct dirent *ptr;int count=0;char filenames[256][PATH_MAX+1],temp[PATH_MAX+1];dir=opendir(path);if(dir==nullptr)my_err("opendir",__LINE__);while((ptr=readdir(dir))!=nullptr){if(g_maxlen<strlen(ptr->d_name))g_maxlen=strlen(ptr->d_name);count++;}closedir(dir);if (count>256){my_err("too maney files under this dir",__LINE__);}int i,j,len=strlen(path);dir=opendir(path);for(i=0;i<count;i++){ptr=readdir(dir);if(ptr==nullptr)my_err("readdir",__LINE__);strncpy(filenames[i],path,len);filenames[i][len]='\0';strcat(filenames[i],ptr->d_name);filenames[i][len+strlen(ptr->d_name)]='\0';}qsort(filenames,count,sizeof(filenames[0]),cmp);for(i=0;i<count;i++)display(flag_param,filenames[i]);closedir(dir);if((flag_param&PARAM_L)==0)printf("\n");
}
int main(int argc,char **argv){int i,j,k,num;char path[PATH_MAX+1];char param[32];int flag_param=PARAM_NONE;struct stat buf;j=0;num=0;for(i=1;i<argc;i++){if(argv[i][0]=='-'){for(k=1;k<strlen(argv[i]);k++,j++){param[j]=argv[i][k];}num++;}}for(int i=0;i<j;i++){if(param[i]=='a'){flag_param|=PARAM_A;continue;}else if(param[i]=='l'){flag_param|=PARAM_L;continue;}else{printf("my_ls:invalid option -%c\n",param[i]);exit(1);}}param[j]='\0';if((num+1)==argc){strcpy(path,"./");path[2]='\0';display_dir(flag_param,path);return 0;}i=1;do{if(argv[i][0]=='-'){i++;continue;}else{strcpy(path,argv[i]);if(stat(path,&buf)==-1)my_err("stat",__LINE__);if(S_ISDIR(buf.st_mode)){if(path[strlen(argv[i])-1]!='/'){path[strlen(argv[i])]='/';path[strlen(argv[i])+1]='\0';}else path[strlen(argv[i])]='\0';display_dir(flag_param,path);i++;}else{display(flag_param,path);i++;}}} while (i<argc);return 0;
}

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

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

相关文章

后端项目全局异常处理-使用RuntimeException自定义异常异常分类简单举例

接上篇&#xff1a;后端项目操作数据库-中枢组件Service调用Mapper 自定义异常&#xff1a; 手动抛出异常&#xff0c;为了后续统一捕获&#xff0c;需要异常自定义&#xff1b; 如&#xff1a;当使用抛出异常的方式表示“操作失败”时&#xff0c;为了后续统一捕获&#xff0c…

灯具照明行业MES/开源mes/灯具行业免费MES

一、万界星空科技开源MES系统概述&#xff1a; 万界星空科技免费MES、开源MES、商业开源MES、市面上最好的开源MES、MES源代码、适合二开的开源MES。 1.万界星空开源MES制造执行系统的Java开源版本。 开源mes系统包括系统管理&#xff0c;车间基础数据管理&#xff0c;计划管…

Chapter 7 - 3. Congestion Management in Ethernet Storage Networks以太网存储网络的拥塞管理

Pause Threshold for Long Distance Links长途链路的暂停阈值 This section uses the following basic concepts: 本节使用以下基本概念: Bit Time (BT): It is the time taken to transmit one bit. It is the reciprocal of the bit rate. For example, BT of a 10 GbE po…

喝葡萄酒时观酒闻香尝味究竟有什么用?

对许多人来说&#xff0c;在品尝葡萄酒时能发现大多数人闻不到的香气和尝不到的味道似乎是一种神奇的能力。其他人则认为这是学究式葡萄酒爱好者过于活跃的想象&#xff0c;或者是保持葡萄酒鉴赏精英声誉的一种方式&#xff0c;但两者都不是。 部分是艺术&#xff0c;部分是科…

目前最火的大模型训练框架 DeepSpeed 详解来了

目前&#xff0c;大模型的发展已经非常火热&#xff0c;关于大模型的训练、微调也是各个公司重点关注方向&#xff0c;但是大模型训练的痛点是模型参数过大&#xff0c;动辄上百亿&#xff0c;如果单靠单个GPU来完成训练基本不可能。所以需要多卡或者分布式训练来完成这项工作。…

MBA-数学题概念和公式

{}公差大于零的等差数列:多个数字组成的数列&#xff0c;两两之间差相等,且后值减前值大于0&#xff0c;如&#xff1a;{-2,0,2,4}为公差数列为2的等差数列.因数是指整数a除以整数b(b≠0) 的商正好是整数而没有余数&#xff0c;10的因数为 2和5圆柱体表面积 2πr 2πrh球体表名…

生产环境_Apache Spark技术大牛的实践:使用DataFrame API计算唯一值数量并展示技术(属性报告)

业务背景 给前端提供算法集成好的数据&#xff0c;对算法处理后的数据进行进一步删选展示 可以使用下面代码运行一下看看结果&#xff0c;听有趣的&#xff0c;我写的代码中计算了不同字段的值的数量&#xff0c;并生成了一个显示字符串来描述这些数据的分布情况然后使用"…

MySQL数据库 DDL

目录 一、DDL 二、操作数据库 三、操作表 四、数据类型 五、表操作案例 六、修改表 七、删除表 一、DDL Data Definition Language&#xff0c;数据定义语言&#xff0c;用来定义数据库对象(数据库&#xff0c;表&#xff0c;字段) 。 二、操作数据库 &#xff08;1&am…

Git 使用教程(超级详细)

目录 一&#xff1a;Git二&#xff1a;SVN与Git的的区别三、安装Git四&#xff1a;常规操作五&#xff1a;远程仓库六&#xff1a;创建与合并分支七&#xff1a;bug分支八&#xff1a;多人协作九&#xff1a;git可视化工具 Git Git 是一种分布式版本控制系统&#xff0c;用于…

C# WPF上位机开发(加密和解密)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在报文传输的过程中&#xff0c;根据报文传输的形态&#xff0c;有两种形式&#xff0c;一种是明文传输&#xff0c;一种是加密传输。当然明文传输…

Python+Requests+Pytest+YAML+Allure实现接口自动化

本项目实现接口自动化的技术选型&#xff1a;PythonRequestsPytestYAMLAllure &#xff0c;主要是针对之前开发的一个接口项目来进行学习&#xff0c;通过 PythonRequests 来发送和处理HTTP协议的请求接口&#xff0c;使用 Pytest 作为测试执行器&#xff0c;使用 YAML 来管理测…

Git使用rebase和merge区别

Git使用rebase和merge区别 模拟环境使用merge合并使用rebase 模拟环境 本地dev分支中DevTest增加addRole() 远程dev被同事提交增加了createResource() 使用merge合并 使用idea中merge解决冲突后, 推送远程dev后,日志图显示 使用rebase idea中使用功能rebase 解决冲突…

遥感图像分割系统:融合空间金字塔池化(FocalModulation)改进YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 遥感图像分割是遥感技术领域中的一个重要研究方向&#xff0c;它的目标是将遥感图像中的不同地物或地物类别进行有效的分割和识别。随着遥感技术的不断发展和遥感…

Hystrix使用及原理概述

一、背景 1. 当前问题 一个系统&#xff0c;所有请求共用同一个APP容器&#xff08;Tomcat/jetty/等&#xff09;&#xff0c;共用一个用户线程池&#xff0c;依赖多个不同的远程服务。 当系统健康时&#xff0c;处理请求的延时较低&#xff0c;服务正常运行&#xff1b;当某…

大模型下开源文档解析工具总结及技术思考

1 基于文档解析工具的方法 pdf解析工具 导图一览&#xff1a; PyPDF2提取txt&#xff1a; import PyPDF2 def extract_text_from_pdf(pdf_path):with open(pdf_path, rb) as file:pdf_reader PyPDF2.PdfFileReader(file)num_pages pdf_reader.numPagestext ""f…

漏洞复现-网神SecGate3600防火墙敏感信息泄露漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

算法leetcode|92. 反转链表 II(rust重拳出击)

文章目录 92. 反转链表 II&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a;进阶&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 92. 反转链表 II&#xff1a; 给你单链表的…

迈入数据结构殿堂——时间复杂度和空间复杂度

目录 一&#xff0c;算法效率 1.如何衡量一个算法的好坏&#xff1f; 2.算法效率 二&#xff0c;时间复杂度 1.时间复杂度的概念 2.大O的渐进表示法 3.推导大O的渐进表示法 4.常见时间复杂度举例 三&#xff0c;空间复杂度 一&#xff0c;算法效率 数据结构和算法是密…

迅腾文化品牌网络推广助力企业:保持品牌稳定,发展更多消费者信任,提升品牌忠诚度

迅腾文化品牌网络推广助力企业&#xff1a;保持品牌稳定&#xff0c;发展更多消费者信任&#xff0c;提升品牌忠诚度 在当今快速发展的互联网时代&#xff0c;品牌网络推广已经成为企业发展的重要手段。迅腾文化作为专业的品牌网络推广公司&#xff0c;致力于帮助企业实现品牌…

产品经理之如何编写需求PRD文档(医疗HIS项目详细案例模板)

目录 前言 一.需求文档的含义 二.需求文档的作用及目的 三.编写前的准备 四.需求大纲 五.案例模板 前言 继上两篇的可行性分析文档和竞品分析报告&#xff0c;本篇将继续介绍如何编写PRD文档&#xff0c;并且会附上以医疗项目为例的模板 一.需求文档的含义 需求文…