第三次:程序优化及命令行操作
第二次作业中,我的程序明明是O(nm),但运行时间在40秒左右,实在太慢啦!以及仍未能实现作业要求的命令行操作(后继将学习相关算法继续优化)
于是开始慢慢摸索请教问题并改进,最终矛头指向了freopen和main函数的参数,详情如下
关于main参数
-
main的实际编写为
int main(int argc, char** argv){...}
,其中argc为main函数从控制台(也可以理解成命令行)读入的参数个数(可以理解成文件个数),而argv则为指向各个参数的char型数组指针,可以理解成argv[i]所存储的即为各个参数的文件名 -
且使用该种方法多次freopen文件不需要使用
cin.clear();
-
于是则可以在代码中利用freopen多次打开不同packet文件,freopen的第一个参数为文件名字符串数组,打开不同packet文件时用argv[]作为参数传入freopen即可,相关代码如下:
int file=grgc; for(file=2;file<argc;file++){ FILE *p=freopen(argv[file],"r",stdin); ...; }
-
相关命令行命令如下:
程序设计思路框架
-
打开packet1.txt,读入一条数据;
-
打开rule1.txt,匹配规则集,直到规则匹配成功或遍历整个规则集;
-
重复上述步骤1和2遍历整个数据包;
-
接着打开packet2.txt,重复步骤1.2.3。
关键代码匹配IP地址的方法
- 将数据包和规则集的ip转化为二进制表示;
- 比较两者的二进制表示前n位是否相同(n为规则IP地址的网络前缀)
可能存在的问题
- 使用循环对二进制数比较的效率低于两个十进制数的直接比较;
- 多次重复使用freopen可能降低程序效率
优化方案
方案一:根据规则IP的网络前缀及CIDR划分法算出地址块的最小和最大地址,并将其转换成十进制数直接与数据包的IP进行比较匹配;
方案二:利用结构体对规则集读入一次并进行存储,减少freopen的使用次数。
优化结果及分析
方案一:程序运行效率没有明显提高;分析:求解最大最小地址时,仍需要用循环将其网络前缀位数之后的数转换成0/1,与原方案相比,在复杂度上只是乘以的常数小了一点,区别并不大
方案二:程序运行效率明显提高,从40s-50s优化至9.6s;分析:优化后减少了n次的freopen调用次数(n为数据量),极大地提高了程序运行效率
几个问题
- 若使用string类似乎不能作为参数直接传入函数,只能通过传入其地址进行调用
- 写class的时候注意:
- 构造函数和析构函数声明的时候要加上一对大括号,其他函数不用
- 使用命令行编译时,要连同类所在的.cpp文件一起编译
- 上述两项若操作错误均会提示undifined reference to `某函数
- 但是由于不太熟悉class操作以及认为struct就足够,所以我写完规则的类后最终还是选择使用struct
最终方案及其完整代码
结合方案一和方案二优化程序,完整代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
char ex='0'; //用于补全不够位数的二进制数
string ip_rule="";//用于存储转换二进制后的规则IP网络前缀
void ip_chansform(int a);//转换规则IP,返回前缀长度
// void chansform(int c,char *a); //函数1:IP地址十进制表示转换成点分十进制表示(参数1:IP十进制 , 参数2:存储转换后的点分十进制IP地址的字符数组地址)
int ip_check(ll a, int b,int c); //函数2:检测IP地址是否匹配,并以0/1作为返回值表示否/是;
int port_check(int p, int b, int c); //函数3:检测端口是否匹配,并以0/1作为返回值表示否/是;
int tcp_check(int t, int b); //函数4:检测传输协议是否匹配,并以0/1作为返回值表示否/是;
int stot(char *p,float length);//十六进制转换十进制
ll my_strtol(string *a);//二进制转十进制
struct rule{
char rule_tcp[10];
int rule_ip1[10],rule_ip2[10];
int port_1_l,port_1_r,port_2_l,port_2_r;
};
rule r[1000];
int main(int argc, char** argv){
clock_t start,finish;
start=clock();
fpos_t pos_1=0,pos_2=0;
char waste;
freopen("out.txt","w",stdout);
int file,i,j,k;
int check_1,check_2,check_3,check_4,check_5,port_1,port_2,tcp;
ll ip_1,ip_2;
// cin.clear();
freopen(argv[1],"r",stdin);//读入规则集存储于struct rule中
for(i=0;i<918;i++){
for(j=0;j<5;j++){
cin>>waste>>r[i].rule_ip1[j];
// cout<<r[i].rule_ip1[j]<<" ";
}
cin>>r[i].rule_ip2[0];
for(j=1;j<5;j++){
cin>>waste>>r[i].rule_ip2[j];
// cout<<r[i].rule_ip2[j]<<" ";
}
cin>>r[i].port_1_l>>waste>>r[i].port_1_r>>r[i].port_2_l>>waste>>r[i].port_2_r>>waste;
cin>>waste>>r[i].rule_tcp;
// cout<<r[i].port_1_l<<" "<<r[i].port_1_r<<" "<<r[i].port_2_l<<" "<<r[i].port_2_r<<" "<<r[i].rule_tcp<<endl;
}
for(file=2;file<argc;file++){
FILE *p=freopen(argv[file],"r",stdin);
while(cin>>ip_1,cin>>ip_2,cin>>port_1,cin>>port_2,cin>>tcp){
int cnt=0;
// fgetpos(p,&pos_1);
while(cnt<918){
check_1=ip_check(ip_1,cnt,1);
check_2=ip_check(ip_2,cnt,2);
check_3=port_check(port_1,cnt,1);
check_4=port_check(port_2,cnt,2);
check_5=tcp_check(tcp,cnt);
if(check_1&&check_2&&check_3&&check_4&&check_5) break;
else{
cnt++;
}
check_1=check_2=check_3=check_4=check_5=0;
}
if(cnt==918) cout<<"-1"<<endl;
else cout<<cnt<<endl;
// cin.clear();
// freopen(argv[file],"r",stdin);
// fsetpos(p,&pos_1);
}
finish=clock();
cout<<"程序执行时间为:"<<(double)(finish-start)/CLOCKS_PER_SEC<<endl;
}
return 0;
}
void ip_chansform(int a){//只读一条规则
char s[10];
int i,j;
ltoa(a,s,2);
if(strlen(s)<8){
for(j=0;j<8-strlen(s);j++){
ip_rule+=ex;
}
}
ip_rule+=s;
return ;
}
int ip_check(ll a, int b, int c){
ip_rule="";
ll t,min,max,i;
if(c==1){
for(i=0;i<4;i++){
ip_chansform(r[b].rule_ip1[i]);
}
t=r[b].rule_ip1[4];
}else{
for(i=0;i<4;i++){
ip_chansform(r[b].rule_ip2[i]);
}
t=r[b].rule_ip2[4];
}
string ip_min=ip_rule,ip_max=ip_rule;
for(i=t;i<ip_rule.length();i++){
ip_min[i]='0';
ip_max[i]='1';
}
min=my_strtol(&ip_min);
max=my_strtol(&ip_max);
if(a>=min&&a<=max){
return 1;
}else return 0;
}
int port_check(int p, int b, int c){
if(c==1){
if(p>=r[b].port_1_l&&p<=r[b].port_1_r) return 1;
else return 0;
}else{
if(p>=r[b].port_2_l&&p<=r[b].port_2_r) return 1;
else return 0;
}
}
int tcp_check(int t, int b){
char *p=r[b].rule_tcp;
int i,x;
if(r[b].rule_tcp[5]=='0'&&r[b].rule_tcp[6]=='0') return 1;
x=stot(p,2);
if(t==x) return 1;
else return 0;
}
int stot(char *p,float length){
int sum=0,i,t=length;
for(i=0;i<t;i++){
if(p[i]>='0'&&p[i]<='9'){
sum+=(p[i]-'0')*pow(16,--length);
}else if(p[i]>='A'&&p[i]<='F'){
sum+=(p[i]-'A'+10)*pow(16,--length);
}
}
return sum;
}
ll my_strtol(string *a){
ll sum=0;
float i;
for(i=((*a).length()-1);i>=0;i--){
if((*a)[i]=='1')
sum+=pow(2,31-i);
}
return sum;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?