- 设计题目
程序源代码的相似性
【问题描述】:
对于两个C++语言的源程序代码,用哈希表的方法分别统计两个程序中使用C++语言关键字的情况,并最终按定量的计算结果,得出两份程序的相似性。
【基本要求】
建立C++语言关键字的哈希表,统计在每个源程序中C++关键字出现的频度, 得到两个向量X1和X2,通过计算向量X1和X2的相对距离来判断两个源程序的相似性。
例如:
关键字 void int for char if else while do break class
程序1关键字频度 4 3 0 4 3 0 7 0 0 2
程序2关键字频度 4 2 0 5 4 0 5 2 0 1
X1=[4,3,0,4,3,0,7,0,0,2]
X2=[4,2,0,5,4,0,5,2,0,1]
设s是向量X1和X2的相对距离,s=sqrt( ∑(xi1-xi2) ² ),当X1=X2时,s=0, 反映出可能是同一个程序;s值越大,则两个程序的差别可能也越大。
【测试数据】
选择若干组编译和运行都无误的C++程序,程序之间有相近的和差别大的,用上述方法求s, 对比两个程序的相似性。
【选作内容】
建立源代码用户标识符表,比较两个源代码用户标识符出现的频度,综合关键字频度和用户标识符频度判断两个程序的相似性。
二、需求分析
1)运行环境(软、硬件环境)
Windows操作系统下的Qt Creator 4.9.1 (纯C++环境)
- 输入的形式和输入值的范围
将C++关键字输入到key.txt文本文件中,关键字类型均为char类型,且都是小写英文字母,以空格分开,共输入37个C++关键字。将标识符输入到identifier.txt文本文件中(标识符符合C++标识符定义的要求),以空格分开,共输入7个C++标识符。将要比较的源程序存入不同的文本文件中,分别为test1.txt(直接插入排序算法),test2.txt(希尔排序算法),运行时按照提示输入源程序个数和对应的文件名称,如果输入多个源程序时,比较时应输入相应的源程序序号(本序号为源程序输入顺序)。
- 输出的形式描述
首先分别输出建立的关键字哈希表和标识符哈希表,然后按照哈希表分别统计两个或多个源程序的关键字和标识符使用情况,通过关键字向量和标识符向量的几何相对距离来比较两个程序的相似性。
- 功能描述
本程序能够遍历输出根据关键字和标识符建立的哈希表以及利用这两个哈希表统计C++语言源程序关键字和标识符的使用情况,最后通过比较向量的相对距离得出源程序的相似性。
- 测试数据
Key.txt(关键字)identifier.txt(标识符)test1.txt(源代码1直接插入排序算法)test2.txt(源代码2希尔排序算法)
三、概要设计
1)抽象数据类型(ADT)定义描述
哈希表抽象数据类型定义:
KeyNode:关键字节点类型
数据成员:char data[10]存储关键字数据的字符数组
int number:关键字出现次数
struct KeyNode*next:指向下一个关键字结点的指针
ppKeyNode:指向关键字结点指针的指针类型
HashList:关键字哈希表类型
数据成员:ppKeyNode head:指向哈希表第一个关键字结点的指针
Int length:哈希表中关键字的数量
基本操作:
int Size(HashList ht);//求哈希表总元素个数
void InitHashList(HashList&ht,int m,FILE*fp);//创建哈希表
void Insert(HashList&ht,char*x);//插入元素
KeyNode*Search(HashList ht,char*x);//查找元素
void Frequency(FILE*fp,HashList&ht);//统计源程序每个关键字频度
double Distance(int w[100][100],int a,int b,int m);//计算两个程序关键字的相对距离
void HashClear(HashList&ht,int m);//关键字哈希表清零
void Traverse(HashList ht);//遍历关键字哈希表
int HashFunc(char*e);//哈希表函数
在key.txt中输入37个关键字,identifier.txt中输入7个标识符,使用test1.txt,test2.txt两个源程序用上述方法求关键字和标识符的相对距离s,以此判断相似性。
- 建立关键字哈希表
2.建立标识符哈希表
3.统计关键字哈希表(源程序1)
4.统计标识符哈希表(源程序1)
5.统计关键字哈希表(源程序2)
6.统计标识符哈希表(源程序2)
7.比较结果(两个程序是否相似)
七 、附录:程序设计源代码
- hash.h
#ifndef HASH_H
#define HASH_H
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
#include<fstream>
using namespace std;
typedef struct KeyNode//定义关键字结点指针类型
{
char data[10];
int number;//出现次数
struct KeyNode*next;
}KeyNode,**ppKeyNode; //**ppKeyNode表示指向结点指针的指针
typedef struct
{
ppKeyNode head;//head指向哈希表第一个关键字结点
int length;//关键字数量
}HashList; //定义关键字哈希表结点类型
typedef struct IdenNode
{
char data[10];
int number;
struct IdenNode*next;
}IdenNode,**ppIdenNode;//**ppIdenNode表示指向结点指针的指针
typedef struct
{
ppIdenNode head;
int length;
}HashList1;
int Size1(HashList1 ht);//求标识哈希表总元素的个数
void InitHashList1(HashList1&ht,int m,FILE*fp);//创建标识符哈希表
void Insert1(HashList1&ht,char*x);//插入标识符元素
IdenNode*Search1(HashList1 ht,char*x);//查找标识符元素
void Frequency1(FILE*fp,HashList1&ht);//统计源程序每个标识符频度
double Distance1(int w[100][100],int a,int b,int m);//计算两个程序标识符的相对距离
void HashClear1(HashList1&ht,int m);//标识符哈希表统计量清零
void Traverse1(HashList1 ht);//遍历标识符哈希表
int Size(HashList ht);//求关键字哈希表总元素个数
void InitHashList(HashList&ht,int m,FILE*fp);//创建关键字哈希表
void Insert(HashList&ht,char*x);//插入关键字元素
KeyNode*Search(HashList ht,char*x);//查找关键字元素
void Frequency(FILE*fp,HashList&ht);//统计源程序每个关键字频度
double Distance(int w[100][100],int a,int b,int m);//计算两个程序关键字的相对距离
void HashClear(HashList&ht,int m);//关键字哈希表清零
void Traverse(HashList ht);//遍历关键字哈希表
int HashFunc(char*e);//哈希表函数
int HashFunc(char*e)//哈希函数(求哈希值)
{
int i;
for( i=0;i<10;i++)
if(e[i]=='\0') break;
return ((e[0]*100+e[i-1])%41);
}
int Size(HashList ht)//求关键字哈希表总元素个数
{
int i;
int count=0;
KeyNode*p;
for(i=0;i<43;i++)
{
p=ht.head[i];
while (p)
{
p=p->next;
count++;
}
}
return count;
}
void Insert(HashList &ht,char*x)//插入关键字元素
{
int d=HashFunc(x);
KeyNode*p=new KeyNode;
strcpy(p->data,x);
p->number=0;
p->next=ht.head[d];
ht.head[d]=p;
}
KeyNode*Search(HashList ht,char*x) //查找关键字元素
{
int d=HashFunc(x);
KeyNode*p=ht.head[d];
while(p&&strcmp(p->data,x)!=0)
p=p->next;
return p;
}
void InitHashList(HashList &ht,int m,FILE*fp)//创建关键字哈希表 m为哈希表的大小
{
int i;
char str[10];
ht.head=new KeyNode*[m];
ht.length=m;
for(i=0;i<m;i++)
{
ht.head[i]=nullptr;
KeyNode*p=new KeyNode;
strcpy(p->data,"\0");
p->number=0;
p->next=ht.head[i];
ht.head[i]=p;
}
char ch=fgetc(fp); //从文件中读取关键字
while (ch!=EOF)
{
if(ch==' '&&ch!=EOF)
do
{
ch=fgetc(fp);
} while(ch==' ');
i=0;
while (ch!=' '&&ch!=EOF)
{
str[i++]=ch;
ch=fgetc(fp);
}
str[i]='\0';
Insert(ht,str);
}
}
void Frequency(FILE*fp,HashList&ht)//统计文件中每个关键字频度 关键字全是小写英文
{
int i;
char str[10];
char ch=fgetc(fp);
while(ch!=EOF)
{
while((ch<'a'||ch>'z')&&ch!=EOF)
{
ch=fgetc(fp);
}
i=0;
if(ch>='a'&&ch<='z')
{
while(ch>='a'&&ch<='z')
{
str[i++]=ch;
ch=fgetc(fp);
}
str[i]='\0';
KeyNode*p=Search(ht,str);
if(p) p->number++;
}
ch=fgetc(fp);
}
}
void HashClear(HashList&ht,int m)//关键字哈希表清零
{
KeyNode*p;
int i;
for(i=0;i<m;i++)
{
p=ht.head[i];
while(p)
{
p->number=0;
p=p->next;
}
}
}
double Distance(int w[100][100],int a,int b,int m)//计算两个程序关键字的相对距离 a,b关键字的索引,m关键字的数量
{
int i,s=0;
for(i=0;i<m;i++)
s=s+(w[a][i]-w[b][i])*(w[a][i]-w[b][i]);
return sqrt(s);
}
void Traverse(HashList ht)//遍历关键字哈希表
{
KeyNode*p;
for(int i=0;i<ht.length;i++)
{
p=ht.head[i];
cout<<"["<<i<<"]";
while(p->next)
{
cout<<" "<<p->data<<"="<<p->number;
p=p->next;
}
cout<<endl;
}
}
int Size1(HashList1 ht)//求标识哈希表总元素的个数
{
int d,count=0;
IdenNode*p;
for(d=0;d<43;d++)
{
p=ht.head[d];
while (p)
{
p=p->next;
count++;
}
}
return count;
}
void Insert1(HashList1&ht,char*x)//插入标识符元素
{
int d=HashFunc(x);
IdenNode*p=new IdenNode;
strcpy(p->data,x);
p->number=0;
p->next=ht.head[d];
ht.head[d]=p;
}
IdenNode*Search1(HashList1 ht,char*x)//查找标识符元素
{
int d=HashFunc(x);
IdenNode*p=ht.head[d];
while(p&&strcmp(p->data,x)!=0) p=p->next;
return p;
}
void InitHashList1(HashList1&ht,int m,FILE*fp)//创建标识符哈希表
{
int i,d;
char str[10];
ht.head=new IdenNode*[m];
ht.length=m;
for(i=0;i<m;i++)
{
ht.head[i]=nullptr;
IdenNode*p=new IdenNode;
strcpy(p->data,"\0");
p->number=0;
p->next=ht.head[i];
ht.head[i]=p;
}
char ch=fgetc(fp);
while(ch!=EOF)
{
if(ch==' '&&ch!=EOF)
do
{
ch=fgetc(fp);
}
while (ch==' ');
i=0;
while (ch!=' '&&ch!=EOF)
{
str[i++]=ch;
ch=fgetc(fp);
}
str[i]='\0';
Insert1(ht,str);
}
}
void Frequency1(FILE*fp,HashList1&ht)//统计源程序每个标识符频度
{
int i,d;
char str[10];
char ch=fgetc(fp);
while (ch!=EOF)
{
while(((ch<'a'&&ch>'Z')||(ch>'9'&&ch<'A')||ch>'z'||ch<'0')&&ch!=EOF&&ch!=95)//标识符对大小写敏感
{
ch=fgetc(fp);
}
i=0;
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||ch==95||(ch>='0'&&ch<='9'))
{
while((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||ch==95||(ch>='0'&&ch<='9'))
{
str[i++]=ch;
ch=fgetc(fp);
}
str[i]='\0';
IdenNode*p=Search1(ht,str);
if(p) p->number++;
}
ch=fgetc(fp);
}
}
double Distance1(int w[100][100],int a,int b,int m)//计算两个程序标识符的相对距离
{
int i,s=0;
for(i=0;i<m;i++)
s=s+(w[a][i]-w[b][i])*(w[a][i]-w[b][i]);
return sqrt(s);
}
void HashClear1(HashList1&ht,int m)//标识符哈希表统计量清零
{
IdenNode*p;
int i;
for(i=0;i<m;i++)
{
p=ht.head[i];
while (p)
{
p->number=0;
p=p->next;
}
}
}
void Traverse1(HashList1 ht)//遍历标识符哈希表
{
IdenNode*p;
for(int i=0;i<ht.length;i++)
{
p=ht.head[i];
cout<<"["<<i<<"]";
while (p->next)
{
cout<<" "<<p->data<<"="<<p->number;
p=p->next;
}
cout<<endl;
}
}
#endif // HASH_H
- main.cpp
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include"hash.h"
using namespace std;
int main()
{
int n;//程序个数
int i,j,p,q;
int d1,d2;
int k=0;
double s1,s2;
char key[100],source[100];//文件名
char choice;//选择
int weight1[10][100],weight2[10][100];
FILE*fp,*fp1;
HashList ht;
HashList1 ht1;
KeyNode*node;//关键字指针
IdenNode*iden;//标识符指针
cout<<"----------欢迎使用程序源代码相似性检测系统------------"<<endl;
cout<<"请输入关键字存储文件名:";
cin>>key;
if((fp=fopen(key,"r"))==nullptr)
{
cout<<"文件不能打开!";
exit(0);
}
InitHashList(ht,43,fp);
cout<<"关键字哈希表创建成功! 关键字哈希表如下:"<<endl;
Traverse(ht);
cout<<endl;
cout<<"请输入标识符储存文件名:";
cin>>key;
if((fp=fopen(key,"r"))==nullptr)
{
cout<<"文件不能打开!";
exit(0);
}
InitHashList1(ht1,43,fp);
cout<<"标识符哈希表建立成功! 标识符哈希表如下:"<<endl;
Traverse1(ht1);
cout<<endl;
cout<<"请输入源程序个数:";
cin>>n;
for(i=0;i<n;i++)
{
cout<<"请输入源程序存储文件名:";
cin>>source;
if((fp1=fopen(source,"r"))==nullptr)
{
cout<<"文件不能打开!";
exit(0);
}
Frequency(fp1,ht);//每个关键字的频度
d1=Size(ht);//关键字个数
cout<<"关键字哈希表统计结果如下:"<<endl;
Traverse(ht);
for(j=0;j<d1&&k<43;)//把统计结果赋值给数组,构造向量
{
node=ht.head[k++];
while (node)
{
weight1[i][j]=node->number;//当前关键字出现的次数
node=node->next;
j++;
}
}
k=0;
rewind(fp1);//把fp1指针重新指向开头
Frequency1(fp1 ,ht1);//每个标识符的频度
d2=Size1(ht1);//标识符次数
cout<<"标识符哈希表统计结果如下:"<<endl;
Traverse1(ht1);
for(j=0;j<d2&&k<43;)//把统计结果赋值给数组,构造向量
{
iden=ht1.head[k++];
while (iden)
{
weight2[i][j]=iden->number;
iden=iden->next;
j++;
}
}
k=0;
HashClear(ht,43);
HashClear1(ht1,43);
cout<<endl;
}
cout<<"是否进行程序比较(是扣1):";
cin>>choice;
while(choice=='1')
{
cout<<"请输入源程序序号(按顺序输入):";//编号i为第i行,向量在数组中存储
cin>>p>>q;
while (p<1||p>n||q>n||q<1)
{
cout<<"输入有误!请重新输入!"<<endl;
cin>>p>>q;
}
s1=Distance(weight1,p-1,q-1,d1);
cout<<"关键字向量相对距离s1="<<s1<<endl;
s2=Distance1(weight2,p-1,q-1,d2);
cout<<"标识符向量相对距离s2="<<s2<<endl;
if(s1==0&&s2==0)
cout<<"两个程序相似!可能为一个程序!!"<<endl;
else {
cout<<"两个程序不相似!"<<endl;
}
cout<<"是否继续比较?(是请扣1,否则退出程序):";
cin>>choice;
}
}