数据挖掘实验一(主要:转到使用WSL)
代码内容没啥,比较简单,主要验证解决了三个问题,第一:一些之前没用过的IO流的使用写到这里了第二:sort自定义排序函数的使用。写到这里了第三:在Clion中分别尝试使用MInGW和WSL(Windows subsystem of Linux)两种环境下编译调试。MinGW很简单,官网下载----解压-----直接使用WSL需要多费很多事,安装LInux子系统,在LInux子系统里面安装CMake
数据挖掘实验一
代码内容没啥,比较简单,主要验证解决了三个问题,第一:一些之前没用过的IO流的使用
第二:sort自定义排序函数的使用。
第三:在Clion中分别尝试使用MInGW和WSL(Windows subsystem of Linux)两种环境下编译调试。
MinGW很简单,官网下载----解压-----直接使用
WSL需要多费很多事,安装LInux子系统,在LInux子系统里面安装CMake、gcc 、g++ 。我遇到的问题:1、忘记了是不是自己在Linux里装的CMake了,版本号低于我CLion里面的Cmake版本,而CLion的CMake好像不太容易换成低版本,所以我只能又回到LInux里面去卸载CMake再下载指定版本的CMAKe(3.20.2,与CLion中匹配一致)2、中间安装CMake遇到很多事情,先下载、再解压,再执行make 、make install 、详见下面链接。 也许是在解压之后少执行了“sudo apt update”去更新?反正按步骤执行解压之后的操作,清华的源表示找不到某个东西,然后换成了阿里的源,然后也执行了“sudo apt update”,之后正常安装上了3.20.2版本的CMAKe。3、Linux与Windows的文件路径格式问题,详见代码中的注释
这里标红了的位置:wsl默认的搜索cmake的位置不是这而是,而是boot文件路径下的cmake文件,可能这个搜索路径是某种默认安装方式下的cmake的位置,但是由于它是3.16.3版本,所以我把它卸载了,然后重新在用户根目录的位置下载了新的3.20.2版本的CMAKe的压缩包,并解压缩,安装,所以是被安装在了用户根目录下(home…),而CLion只扫描这个boot路径,所以需要手动设置这里的Cmake路径(如图红线所示)
执行结果:
MinGW(WIndows)环境下
WSL(Windows subsystem of Linux)(Ubuntu) 环境下
源代码(这里代码是在WSL的Linux环境下调试通过的,如果需要在MINGW下使用,需要同时修改其中的注释与代码才能运行,如果是别的什么环境,请自己尝试)
//
// Created by Alan on 2022/4/22.
/*数据预处理要实现的功能要求如下:
* 1、日期中的缺失值(#)使用流水号的字串来确定
* 2、商品的购买数量不能为负数
* 3、总额 = 单价 * 购买数量 ,所以新的数据集中可以省略总价字段
* 4、三个流水表的日期格式要统一
* 5、门店号同POS机号一样,可以省略一个
* 6、同一个购物篮的商品按照序号顺序递增保存
* */
//
#include<iostream>
#include<string>
#include<fstream>
#include<algorithm>
//#include<direct.h>
#include<unistd.h>
using namespace std;
class Sales
{
public:
string serial; //流水号
int market; //门店号
int posno; //POS
string date; //短日期
int sn; //购买商品序号
int id; //商品id
float num; //购买数量
float price; //销售单价
float total; //销售总价
void print()
{
/*全部字段打印*/
cout << serial<< " " << market << " " << posno << " " <<
date << " "<<sn<<" "<<id<<" "<<num<<" "<<price<<" "<<total<<endl;
}
};
bool cmp(Sales a, Sales b)
{
return a.sn < b.sn; //从小到大排序(升序)
}
string current_working_directory()
{
/*获取当前项目的绝对路径:当前项目的 WorkingDirectory 路径*/
/*在MinGW编译环境下
搭配#include<direct.h>
char buff[250];
_getcwd(buff, 250);
std::string current_working_directory(buff);
return current_working_directory;*/
//在wsl环境下使用#include<unistd.h>
char *path;
path = get_current_dir_name();
string Path = path;
return Path;
}
int main()
{
/**************************选择进行数据预处理的txt源文件与处理数据的存储目标txt文件**********************/
string path_first = current_working_directory();
/*获取当前源文件所处的working_directroy(工作目录)的完整路径*/
string x = "\\";
/*如果使用MinGW环境,在MinGW环境下可以使用direct.h的_getcwd()方法获取当前工作目录
* (要提前把工作目录设置成源文件所在文件夹的绝对路径)
* 然后再加上'\txtname'构成这个txt文件的完整路径,但是 要注意'\\'才会表示为 '\'
* 所以需要执行:
* path_first = path_first + x; */
//而如果在Linux环境下,根本没有direct.h这个头文件,可以使用unistd.h的get_current_dir_name()方法来获取当前工作目录
//但是注意:在Linux环境下整个工作目录都被解析成以 '/'分隔的路径,所以要执行: +'/textname',
// 如下所示:
path_first = path_first + "/";
string path_from;
string path_to;
string from_txt;
string to_txt;
cout << "请输入要进行预处理操作的txt文件的文件名(1019.txt、1020.txt、1021.txt):“xxx.txt” \n";
getline(cin,from_txt);
if(!from_txt.empty()){
path_from = path_first + from_txt;
cout << "获取数据的文件名为:" << path_from << endl;
}
else{
cout << "输入获取数据的文件名时:存在非法输入";
exit(1);
}
cout << "请输入存放处理后数据的的txt文件的文件名(fl_1019.txt,fl_1020.txt,fl_1021.txt):“xxx.txt” \n";
getline(cin,to_txt);
if(!to_txt.empty()){
path_to = path_first + to_txt;
cout << "存放数据文件的绝对路径为:" << path_to << endl;
}
else{
cout << "输入存放数据文件名时:存在非法输入";
exit(1);
}
/**************************设置IO流来读入原始数据、写入目标文件**********************/
ifstream infile(path_from,ios::in);
/*ios basic_ios<char> : 字符流的基类
* infile为ifstream类的实例化对象,通过此构造函数,
* 作为1021.txt文件的输入流,负责读1021.txt到内存*/
if(infile.fail())
{
/*fail():检查是否发生了可恢复的错误*/
cout << "error open!" << from_txt << "打开发生错误,无法正常打开" << endl;
exit(1);
}
ofstream outfile(path_to,ios::out);
if(!outfile)
{
cout<<"open error!" << to_txt << "打开发生错误,无法正常打开" <<endl;
exit(1);
}
/**************************将原始数据读到数组中,进行数据预处理**********************/
Sales sal[10000];
int sal_size = 0;
//读入
while(!infile.eof())
{
/*将原始数据读入数组存储,为后面的数据处理做准备*/
/*eof(): 检查是否到达了文件末尾*/
if(sal_size == 10000){
cout << "默认处理数据10000条,而此数据文件有大于10000条数据,只处理到前10000条,后面数据不读入不处理\n";
exit(1);
}
infile >> sal[sal_size].serial >> sal[sal_size].market >>
sal[sal_size].posno>> sal[sal_size].date>> sal[sal_size].sn>>
sal[sal_size].id>> sal[sal_size].num>> sal[sal_size].price>> sal[sal_size].total;
//sal[sal_size].print();
/*选择调用print函数来查看是否将原始数据正确读入数组中*/
sal_size++;
}
//预处理操作、写到指定txt文件
/*同一流水号的商品进行排序*/
sal_size = sal_size - 1;
/*前面循环的最后一步执行了sal_size++*/
int i = 0;
while(i < sal_size){
int j = i;
int m = 0;
if( j + 1 <= sal_size){
while(sal[j].serial == sal[j+1].serial){
m++;
j++;
}
sort(sal+i,sal+i+m,cmp);
/*同组排序:前提是同一个流水号的商品要连续紧邻在一起,当i到i+m位置处为同一流水号的商品记录时
* sort函数对i到i+m行的数据记录根据他们的sn字段进行升序排序*/
i = j + 1;
}
}
outfile << "流水号\t\t\t" << "\t门店号\t" << "日期\t" << "\t购买商品序号\t" << " 商品id\t" << "购买数量\t" << "商品单价\t" << endl;
for(int i = 0; i < sal_size; i++)
{
if (sal[i].num<0){
/*购买数量不能为负数,将负数的购买数量直接取反*/
sal[i].num=-sal[i].num;
}
sal[i].date.assign(sal[i].serial ,0,8);
/*调用assign函数实现用流水号的子串来全部重写日期字段,清除原始数据的缺失值:
* 获取serial字符串的子串,从第0位开始,一共取8位字符组成子串赋给date*/
/*将处理过后的一行数组数据写到指定的txt文件*/
outfile << sal[i].serial <<"\t"<<sal[i].market<<"\t"<<sal[i].date<<"\t\t"
<< sal[i].sn<<"\t\t"<<sal[i].id<<"\t\t"<<sal[i].num<<"\t\t" <<sal[i].price << endl;
}
cout << "该数据文件共有" << sal_size << "行数据记录\n";
infile.close();//关闭文件
outfile.close();//关闭文件
}
更多推荐
所有评论(0)