第六题.稀疏数据文件处理程序
6.1题目
为节省存储空间和提高文件的网络传输效率,数据文件常采用稀疏方式存储,如图像压缩、稀疏编码等技术。而在计算时,又需要从稀疏数据(sparse data)中恢复出原始数据(full data),以便采用向量或矩阵运算。现有如下稀疏数据,如LIBSVM提供的公开数据aloi文件(附下载网址), 格式如下图所示:
附网址: https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/aloi.bz2
文件的第一列表示样本的类别,共有1000类,采用0-999标记;而对于二分类数据,其类别符号采用“+1”和“-1”标示。图1中,每一行表示一个样本,样本的结束符采用回车符。例如,第一行中的“76:1”,它表示该样本的第76个属性值为1。每行中未列出的属性,它们的属性值均为0,故无需在文件中存储。按要求完成以下任务:
自动计算LIBSVM类型数据的样本数和特征数(属性个数)。
从稀疏文件中恢复出全部数据(去除列标记,每个样本的属性值全部列出,以空格分隔),并将类别标记写入与该文件对应的文本文件中,如记为”aloi_full.txt”和”aloi_label.txt”
为便于共享计算结果,结果仍采用稀疏形式存储和传输,即实现问题2的逆变换。受限于目前知识,本题暂不考虑计算问题,仅要求从格式形如文件”aloi_full.txt”和”aloi_label.txt”,获得稀疏的aloi数据,记为“restore_aloi.txt”.
* 比对aloi和restore_aloi.txt文件的差异,并记录文件正反变换的时间,将结果回显在屏幕上。
6.2需求分析:
- 题目需要我们将一个压缩后的向量文件给恢复成原始文件,并统计特征向量的数量和特征向量的种类数量。然后再将原始的向量文件压缩,并对压缩前后的文件进行比较。
- 题目需要统计每一步操作所需要的时间。
6.3方案设计:
- 创建一个类的实例,在实例中读取压缩后的文件,逐行读取并对数据文件进行处理。在完成实习之后我发现其实可以不写的那么复杂,可以直接通过格式化输入完成这样的一个过程。
- 程序采用面向对象的编程思路,将函数进行类封装,仅对外暴露极少部分的函数。
6.4核心代码:
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<stdlib.h>
const int LINE_MAX=1e5;
const int VECTOR_MAX=129;
const int SAMPLE_MAX=1e3+10;
class Line
{
private:
char line[LINE_MAX];
int vectors[VECTOR_MAX];
int sample_cnt[SAMPLE_MAX];
FILE *fp,*wfp,*lfp;
int sample_num;
int eigenvector;//记录特征向量,也就是第一个参数的值
void read_file(const char filename[])
{
fp=fopen(filename,"r+");
if(fp==nullptr) printf("File open failed\n");
}
void write_file(const char filename[],const char lable[])
{
wfp=fopen(filename,"w");
lfp=fopen(lable,"w");
if(wfp==nullptr||lfp==nullptr) printf("File open failed\n");
}
void rtrim(char *str)
{
size_t len=strlen(str);
while(len>0&&(str[len-1]==' '||str[len-1]=='\n')) str[--len]='\0';
}
public:
bool read_one_line()//读取成功返回1,否则返回0
{
++sample_num;
fgets(line,sizeof line,fp);
int linelength=strlen(line);
int tmp=0,tmp_cnt=0;
int vector_position;//记录向量的位置
memset(vectors,0,sizeof vectors);
line[linelength-1]=' ';
line[linelength]='\0';
linelength=strlen(line);
for(int i=0;i<linelength+1;++i)
{
if(line[i]!=' '&&line[i]!=':')
{
tmp=tmp*10+line[i]-'0';
}
else if(line[i]==' '&&tmp_cnt==0)
{
eigenvector=tmp;
++sample_cnt[tmp];
tmp=0;
++tmp_cnt;
}
else if(line[i]==' '||i==linelength-1)
{
vectors[vector_position]=tmp;
tmp=0;
++tmp_cnt;
}
else if(line[i]==':')
{
vector_position=tmp;
tmp=0;
++tmp_cnt;
}
}
if(feof(fp)) return 0;
else return 1;
}
void write_one_line()
{fprintf(lfp,"%d ",eigenvector);
for(int i=1;i<=1<<7;++i)
{
fprintf(wfp,"%d ",vectors[i]);
}
fprintf(wfp,"\n");
}
void print_newest_line()
{
for(int i=1;i<=1<<7;++i)
{
printf("%d ",vectors[i]);
}
}
void restore_file(const char lable[],const char full[],const char restore[])
{
FILE *restore_lable=fopen(lable,"r+");
FILE *restore_full=fopen(full,"r+");
FILE *restore_restore=fopen(restore,"w");
do
{
fscanf(restore_lable,"%d",&eigenvector);
fprintf(restore_restore,"%d ",eigenvector);
fgets(line,sizeof line,restore_full);
int linelength=strlen(line);
line[linelength]=' ';
int tmp=0,tmp_cnt=0;
for(int i=0;i<linelength;++i)
{
if(line[i]==' ')
{
++tmp_cnt;
if(tmp==0) continue;
else
{
fprintf(restore_restore,"%d:%d ",tmp_cnt,tmp);
}
tmp=0;
}
else tmp=tmp*10+line[i]-'0';
}
fprintf(restore_restore,"\n");
}while(!feof(restore_full)||!feof(restore_lable));
fclose(restore_lable);
fclose(restore_full);
fclose(restore_restore);
}
void display_sample()
{
int cnt=0;
for(int i=0;i<=999;++i)
if(sample_cnt[i]) ++cnt;
printf("eigenvector cnt:%d\n",cnt);
printf("sample cnt:%d\n",sample_num);
}
void compare(const char file_1[],const char file_2[])
{
FILE *file1=fopen(file_1,"r+");
FILE *file2=fopen(file_2,"r+");
if (!file1||!file2)
{
printf("Compare:File open failed\n");
if (file1) fclose(file1);
if (file2) fclose(file2);
}
char f1[LINE_MAX],f2[LINE_MAX];
int line_number=0;
while(1)
{
char *line1=fgets(f1,sizeof(f1),file1);
char *line2=fgets(f2,sizeof(f2),file2);
++line_number;
if((!line1&&!line2)||strcmp(line1,"\n")||strcmp(line2,"\n"))
{
fclose(file1);
fclose(file2);
printf("File is same\n");
return;
}
if((!line1||!line2)&&(strlen(line1)>2||strlen(line2)>2))
{
printf("Line %d different\n",line_number);
printf("f1:%s",f1);
printf("f2:%s\n",f2);
fclose(file1);
fclose(file2);
return;
}
rtrim(f1);
rtrim(f2);
if (strcmp(f1,f2)!=0)
{
printf("Line %d different\n",line_number);
printf("f1:%s\n",f1);
printf("f2:%s\n",f2);
fclose(file1);
fclose(file2);
return;
}
}
}
Line(const char readfile[],const char writefile[],const char writelable[])
{
read_file(readfile);
write_file(writefile,writelable);
}
~Line()
{
memset(line,0,sizeof line);
fclose(fp);
fclose(lfp);
fclose(wfp);
}
};
int main()
{
Line *line=new Line("aloi","aloi_full.txt","aloi_lable.txt");
int cnt=0;
clock_t start=clock();
double cpu_time_used;
while(line->read_one_line()) line->write_one_line();
line->display_sample();
//line->print_newest_line();
clock_t end=clock();
cpu_time_used=((double)(end-start))/CLOCKS_PER_SEC;
printf("Unzip_Process_Time%.2fseconds\n", cpu_time_used);
start=clock();
line->restore_file("aloi_lable.txt","aloi_full.txt","aloi_restore.txt");
end=clock();
cpu_time_used=((double)(end-start))/CLOCKS_PER_SEC;
start=clock();
line->compare("aloi","aloi_restore.txt");
//printf("%d",line->debug);
delete line;
end=clock();
cpu_time_used=((double)(end-start))/CLOCKS_PER_SEC;
printf("Compare_Process_Time%.2fseconds\n", cpu_time_used);
return 0;
}
6.5测试用例及运行结果
在macOS14.6.1操作系统下,采用如下编译命令:
g++ -std=c++14 conduct.cpp -o conduct
文件aloi摘要如下:
文件aloi_lable.txt摘要如下:
文件aloi_full.txt摘要如下:
文件aloi_restore.txt摘要如下:
运行结果如下:
6.6总结
- 本次实习过程中我使用了大量的字符串处理代码,但是后来发现可以通过文件的格式化输入避开这一麻烦的操作,并且也有助于提高代码的运行效率。
- 在本次实习中我遇到过文件比较不一致的问题,后来通过修改代码改变了文件读取到末尾的判断方式,从而解决了这个问题。
说明
- 题目可以直接复制;
- 需求分析:可根据题目要求,分析需要解决什么样的问题;
- 设计思想和方案设计:该部分可写设计思路,如怎样构造类,该类的变量及成员函数功能是什么;变量、函数名规定等等,特别是抽象问题是如何具体化的;
- 核心代码:如果代码比较多,复制主要代码即可,量少就全部复制;
- 测试用例:如果少数键盘输入,直接给出用例;以文件输入,交待下文件内容格式,并附简要截图;
- 总结部分:主要学会了什么,还有哪些问题没能实现(与现实问题相比较,因为专业知识所限,目前我们能解决的问题往往都是实际问题的简化版);课设过程遇到了什么样的问题,你是如何解决的;对比课设前后都有哪方面的提高;后续如何学习等。