algo2-3-1.c 利用无头结点的单链表结构处理教科书图2.1(学生健康登记表)

本文介绍了一个使用无头结点单链表结构处理学生健康登记表的应用案例,包括记录的增删改查等功能,并提供了文件读写的支持。

 /* algo2-6.c 利用无头结点的单链表结构处理教科书图2.1(学生健康登记表) */
 #include"c1.h"
 #define NAMELEN 8 /* 姓名最大长度 */
 #define CLASSLEN 4 /* 班级名最大长度 */
 typedef struct
 {
   char name[NAMELEN+1]; /* 包括'\0' */
   long num;
   char sex;
   int age;
   char Class[CLASSLEN+1]; /* 包括'\0' */
   int health;
 }stud; /* 记录的结构 */


 typedef stud ElemType; /* 链表结点元素类型为结构体 */
 #include"c2-2.h"
 #include"bo2-8.c" /* 无头结点单链表的部分基本操作 */
 #include"func2-1.c" /* 无头结点单链表的扩展操作 */
 char sta[3][9]={"健康  ","一般  ","神经衰弱"}; /* 健康状况(3类) */
 FILE *fp; /* 全局变量 */

 void Print(stud e)
 { /* 显示记录e的内容 */
   printf("%-8s %6ld",e.name,e.num);
   if(e.sex=='m')
     printf(" 男");
   else
     printf(" 女");
   printf("%5d  %-4s",e.age,e.Class);
   printf("%9s\n",sta[e.health]);
 }

 void ReadIn(stud *e)
 { /* 由键盘输入结点信息 */
   printf("请输入姓名(<=%d个字符): ",NAMELEN);
   scanf("%s",(*e).name);
   printf("请输入学号: ");
   scanf("%ld",&(*e).num);
   printf("请输入性别(m:男 f:女): ");
   scanf("%*c%c",&(*e).sex);
   printf("请输入年龄: ");
   scanf("%d",&(*e).age);
   printf("请输入班级(<=%d个字符): ",CLASSLEN);
   scanf("%s",(*e).Class);
   printf("请输入健康状况(0:%s 1:%s 2:%s):",sta[0],sta[1],sta[2]);
   scanf("%d",&(*e).health);
 }

 void WriteToFile(stud e)
 { /* 将结点信息写入fp指定的文件 */
   fwrite(&e,sizeof(stud),1,fp);
 }

 Status ReadFromFile(stud *e)
 { /* 由fp指定的文件读取结点信息到e */
   int i;
   i=fread(e,sizeof(stud),1,fp);
   if(i==1) /* 读取文件成功 */
     return OK;
   else
     return ERROR;
 }

 int cmp(ElemType c1,ElemType c2)
 {
   return (int)(c1.num-c2.num);
 }

 void Modify(LinkList *L,ElemType e)
 { /* 修改结点内容,并按学号将结点非降序插入链表L */
   char s[80];
   Print(e); /* 显示原内容 */
   printf("请输入待修改项的内容,不修改的项按回车键保持原值:\n");
   printf("请输入姓名(<=%d个字符): ",NAMELEN);
   gets(s);
   if(strlen(s))
     strcpy(e.name,s);
   printf("请输入学号: ");
   gets(s);
   if(strlen(s))
     e.num=atol(s);
   printf("请输入性别(m:男 f:女): ");
   gets(s);
   if(strlen(s))
     e.sex=s[0];
   printf("请输入年龄: ");
   gets(s);
   if(strlen(s))
     e.age=atoi(s);
   printf("请输入班级(<=%d个字符): ",CLASSLEN);
   gets(s);
   if(strlen(s))
     strcpy(e.Class,s);
   printf("请输入健康状况(0:%s 1:%s 2:%s):",sta[0],sta[1],sta[2]);
   gets(s);
   if(strlen(s))
     e.health=atoi(s); /* 修改完毕 */
   InsertAscend(L,e,cmp); /* 把q所指结点的内容按学号非降序插入L(func2-1.c) */
 }

 #define N 4 /* student记录的个数 */

 Status EqualNum(ElemType c1,ElemType c2)
 {
   if(c1.num==c2.num)
     return OK;
   else
     return ERROR;
 }

 Status EqualName(ElemType c1,ElemType c2)
 {
   if(strcmp(c1.name,c2.name))
     return ERROR;
   else
     return OK;
 }

 void main()
 { /* 表的初始记录 */
   stud student[N]={{"王小林",790631,'m',18,"计91",0},{"陈红",790632,'f',20,"计91",1},
                    {"刘建平",790633,'m',21,"计91",0},{"张立立",790634,'m',17,"计91",2}};
   int i,j,flag=1;
   char filename[13];
   ElemType e;
   LinkList T,p,q;
   InitList(&T); /* 初始化链表 */
   while(flag)
   {
     printf("1:将结构体数组student中的记录按学号非降序插入链表\n");
     printf("2:将文件中的记录按学号非降序插入链表\n");
     printf("3:键盘输入新记录,并将其按学号非降序插入链表\n");
     printf("4:删除链表中第一个有给定学号的记录\n");
     printf("5:删除链表中第一个有给定姓名的记录\n");
     printf("6:修改链表中第一个有给定学号的记录\n");
     printf("7:修改链表中第一个有给定姓名的记录\n");
     printf("8:查找链表中第一个有给定学号的记录\n");
     printf("9:查找链表中第一个有给定姓名的记录\n");
     printf("10:显示所有记录 11:将链表中的所有记录存入文件 12:结束\n");
     printf("请选择操作命令: ");
     scanf("%d",&i);
     switch(i)
     {
       case 1: for(j=0;j<N;j++)
   InsertAscend(&T,student[j],cmp); /* 在func2-1.c中 */
               break;
       case 2: printf("请输入文件名: ");
               scanf("%s",filename);
               if((fp=fopen(filename,"rb"))==NULL)
                 printf("打开文件失败!\n");
               else
               {
                 while(ReadFromFile(&e))
     InsertAscend(&T,e,cmp); /* 在func2-1.c中 */
                 fclose(fp);
               }
               break;
       case 3: ReadIn(&e);
        InsertAscend(&T,e,cmp); /* 在func2-1.c中 */
               break;
       case 4: printf("请输入待删除记录的学号: ");
               scanf("%ld",&e.num);
               if(!DeleteElem(&T,&e,EqualNum)) /* 在func2-1.c中 */
                 printf("没有学号为%ld的记录\n",e.num);
               break;
       case 5: printf("请输入待删除记录的姓名: ");
               scanf("%*c%s",e.name); /* %*c吃掉回车符 */
               if(!DeleteElem(&T,&e,EqualName)) /* 在func2-1.c中 */
                 printf("没有姓名为%s的记录\n",e.name);
               break;
       case 6: printf("请输入待修改记录的学号: ");
               scanf("%ld%*c",&e.num);
        if(!DeleteElem(&T,&e,EqualNum)) /* 在链表中删除该结点,并由e返回(func2-1.c) */
                 printf("没有学号为%ld的记录\n",e.num);
               else
                 Modify(&T,e); /* 修改e并按学号插入链表T */
               break;
       case 7: printf("请输入待修改记录的姓名: ");
               scanf("%*c%s%*c",e.name); /* %*c吃掉回车符 */
               if(!DeleteElem(&T,&e,EqualName)) /* func2-1.c */
                 printf("没有姓名为%s的记录\n",e.name);
               else
                 Modify(&T,e);
               break;
       case 8: printf("请输入待查找记录的学号: ");
               scanf("%ld",&e.num);
               q=Point(T,e,EqualNum,&p); /* func2-1.c */
               if(!q)
                 printf("没有学号为%ld的记录\n",e.num);
               else
                 Print(q->data);
               break;
       case 9: printf("请输入待查找记录的姓名: ");
               scanf("%*c%s",e.name);
               q=Point(T,e,EqualName,&p); /* func2-1.c */
               if(!q)
                 printf("没有姓名为%s的记录\n",e.name);
               else
                 Print(q->data);
               break;
       case 10:printf("  姓名    学号 性别 年龄 班级 健康状况\n");
               ListTraverse(T,Print);
               break;
       case 11:printf("请输入文件名: ");
               scanf("%s",filename);
               if((fp=fopen(filename,"wb"))==NULL)
                 printf("打开文件失败!\n");
               else
                 ListTraverse(T,WriteToFile);
               fclose(fp);
               break;
       case 12:flag=0;
     }
   }
 }

 

 

<think>好的,我现在需要解决用户关于iptables的string模块扩展的问题,特别是针对第7层字符匹配的情况。用户提到了几个参数:--algo--string、--hex-string,还有否定选项!的使用。我得先回忆一下相关知识,确保自己理解正确,然后组织一个清晰易懂的回答。 首先,用户的问题涉及的是iptables的扩展模块string,用于在数据包的应用层(第7层)匹配特定字符串。需要明确的是,这个模块只能处理明文协议,比如HTTP、FTP等,因为加密协议如HTTPS的数据是加密的,无法直接匹配字符串。 接下来,用户提到的三个参数需要逐一解释: 1. --algo {bm|kmp}:指定字符串匹配算法,bm是Boyer-Moore,kmp是Knuth-Morris-Pratt。需要简要说明两者的区别,比如bm在处理大文本时效率更高,而kmp可能在模式串较短时更高效。不过可能用户不需要太深入的算法细节,所以保持简洁。 2. [!] --string 参数:这里的!表示否定,即匹配不包含该字符串的情况。需要说明如何正确使用引号和转义特殊字符,比如空格或符号需要转义或用引号包裹。 3. [!] --hex-string 参数:十六进制格式的字符串匹配,用于匹配非打印字符或二进制数据。需要举例说明如何将字符串转换为十六进制形式,比如"test"转成"74657374"。 然后,需要提到使用限制,比如只能用于明文协议,以及性能影响,因为字符串匹配会消耗较多资源,尤其是在处理大量流量时。 可能用户的实际应用场景包括阻止含有特定关键词的HTTP请求,或者过滤恶意负载。需要给出具体的命令示例,帮助用户理解如何应用这些参数。 还要注意,在iptables中使用扩展模块时需要加载,比如-m string,可能用户会忘记这个步骤,需要提醒。 最后,检查是否有常见误区,比如误用于加密流量,或者算法选择不当导致性能问题。同时,确保命令示例的正确性,比如转义字符是否正确,选项顺序是否合理。 总结下来,回答的结构应该是先介绍模块的作用和限制,然后分点解释每个参数,给出示例,最后注意事项和实际应用案例。这样用户能够逐步理解并正确应用这些选项。</think>### **核心结论** string扩展模块通过**应用层数据扫描**实现7层过滤,**仅适用于明文协议**(HTTP/FTP/SMTP等),HTTPS等加密协议**无法使用**。核心功能围绕**字符串匹配算法**和**匹配模式**展开。 --- ### **参数详解** #### **1. `--algo {bm|kmp}`**(算法选择) | 算法 | 特点 | 适用场景 | |------|------------------------------------------|---------------------------| | **bm** | Boyer-Moore算法,**跳跃式匹配**速度快 | 长文本、大流量场景 | | **kmp** | Knuth-Morris-Pratt算法,**预处理模式串** | 短模式串、频繁匹配操作 | ```bash # 示例:使用kmp算法检测"admin" iptables -A INPUT -p tcp --dport 80 -m string --algo kmp --string "admin" -j DROP ``` --- #### **2. `[!] --string 参数`**(明文匹配) - **功能**:匹配**ASCII可见字符** - **特殊字符处理**: ```bash # 匹配含空格的字符串(必须用引号包裹) --string "user login" # 匹配含特殊符号需转义 --string "\/api\/*" # 匹配"/api/*" ``` - **否定匹配**(加`!`): ```bash # 阻断非包含"safe_content"的HTTP请求 iptables -A INPUT -p tcp --dport 80 -m string --algo bm ! --string "safe_content" -j REJECT ``` --- #### **3. `[!] --hex-string 参数`**(十六进制匹配) - **核心用途**:匹配**非打印字符**或**二进制数据** - **转换方法**: ```bash # 将"test"转为十六进制 echo -n "test" | xxd -pu # 输出:74657374 # 匹配二进制头0x89PNG(PNG文件特征) --hex-string "|89504E47|" ``` - **实战案例**: ```bash # 阻断包含PNG文件头的HTTP响应 iptables -A OUTPUT -p tcp --sport 80 -m string --algo bm --hex-string "|89504E47|" -j DROP ``` --- ### **配置模板** #### 基础防御场景 ```bash # 阻断HTTP请求中的SQL注入特征 iptables -A INPUT -p tcp --dport 80 -m string \ --algo kmp --string "select * from users" -j DROP # 阻断响应中的敏感文件头(如exe) iptables -A OUTPUT -p tcp --sport 80 -m string \ --algo bm --hex-string "|4D5A|" -j REJECT # 匹配"0x4D5A"(Windows PE文件头) ``` #### 高级内容过滤 ```bash # 允许包含"trusted_"前缀的URL访问,阻断其他请求 iptables -A INPUT -p tcp --dport 80 -m string \ ! --algo bm --string "trusted_" -j REJECT # 阻断包含十六进制0xDEADBEEF的流量(自定义特征码) iptables -A FORWARD -m string \ --algo kmp --hex-string "|DEADBEEF|" -j DROP ``` --- ### **性能影响与优化** | 操作 | CPU消耗 | 内存占用 | 优化建议 | |-----------------------|---------|----------|-------------------------------| | 长字符串匹配(>64字节) | 高 | 中 | 优先用bm算法 | | 多规则叠加 | 极高 | 高 | 合并相似规则,使用ipset | | 全流量扫描 | 极高 | 低 | 限制端口范围(如--dport 80) | --- ### **常见踩坑点** 1. **加密协议无效** ```bash # 错误尝试:在HTTPS端口检测(因加密无效) iptables -A INPUT -p tcp --dport 443 -m string --string "password" -j DROP ``` 2. **字符编码问题** UTF-8多字节字符需转换为十六进制: ```bash # 错误:"中文"直接匹配 --string "中文" # 正确:转换中文到hex echo -n "中文" | xxd -pu # 输出:e4b8ade69687 --hex-string "|e4b8ade69687|" ``` 3. **性能雪崩** 避免在主干链(如FORWARD)上不加限制地使用: ```bash # 危险操作:扫描所有端口的流量 iptables -A FORWARD -m string --algo bm --string "block_this" -j DROP ``` --- ### **替代方案建议** 当遇到性能瓶颈时,可考虑: - **L7层代理**:如Nginx的`$http_user_agent`过滤 - **专业WAF**:ModSecurity等Web应用防火墙 - **eBPF技术**:XDP实现更高效的内容过滤 --- ### **终极总结** **string扩展是Linux防火墙的"内容安检仪"**: - `--algo`选算法 → 相当于选择**X光机型号** - `--string`查明文 → 类似**人工开箱检查** - `--hex-string`查二进制 → 类似**金属
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值