c++ 解析csv文件
一、项目要求:
(1) 这个程序需要做到将csv文件的内容读取进来解析,并将每一个联系人的数据进行打印;
(2) 用户输入排序的属性key,将排序完成的结果打印出来;
(3) 能够对错误格式的文件进行检测;
(4) 能够对错误的数据进行容错;
(5) 文件编码格式不限,输出为UTF8格式
二、分析问题
1.问题1对与csv文件的读取,要如何读取,想到可以对文件进行读操作的文件方法函数,对文件读取之后怎么去进行将每一行的数据进行剪切成每一个主题。剪切后的数据如何保持,是用二维数组,还是用结构体(分析得出为了后面的任务方便进行,还是用结构体来存储数据是最好的)
2.问题二 属性key的排序,对于排序,比如 用id ,age 等数字型的数据项进行排序,其中的排序是如何实现?是升序还是降序?这个都要了解。排序好了之后怎么再次打印出来,个用户查看等
3.要考虑到csv文件的错误格式有哪些,什么场景下会比较容易产生错误的格式文件,又如何对其进行检测,检测完之后如何让用户更改过来等。
4.问题四 对错误的数据进行容错,首先要知道了解,数据的错误又那些?其中我下面的代码实现中实现两个常见的错误数据例如 用分号注释的字符 "****",或 ","这些,csv文件有编码格式可能不符合,导致解析到数据乱码
三、代码实现
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include<algorithm>
#include <Windows.h>
using namespace std;
struct student { //成员结构体
int id;
string name;
int age;
string hobby;
};
class new_csv { //创建一个功能类
public:
void output(vector<student>& vectorclient); //排序输出函数
int examine(int* num, int& bit);
char* Fault(string sum);
};
std::wstring utf8ToUtf16(const std::string& utf8String)
{
std::wstring sResult;
int nUTF8Len = MultiByteToWideChar(CP_UTF8, 0, utf8String.c_str(), -1, NULL, NULL);
wchar_t* pUTF8 = new wchar_t[nUTF8Len + 1];
ZeroMemory(pUTF8, nUTF8Len + 1);
MultiByteToWideChar(CP_UTF8, 0, utf8String.c_str(), -1, pUTF8, nUTF8Len);
sResult = pUTF8;
delete[] pUTF8;
return sResult;
}
LPCWSTR stringToLPCWSTR(std::string orig)
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
return wcstring;
}
std::string wstring2string(wstring wstr)
{
string result;
//获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的
int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);
char* buffer = new char[len + 1];
//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);
buffer[len] = '\0';
//删除缓冲区并返回值
result.append(buffer);
delete[] buffer;
return result;
}
char* UnicodeToUTF8(wchar_t* wszcString)
{
int utf8Len = ::WideCharToMultiByte(CP_UTF8, NULL, wszcString, int(wcslen(wszcString)), NULL, 0, NULL, NULL);
char* szUTF8 = new char[utf8Len + 1]; //给'\0'分配空间
::WideCharToMultiByte(CP_UTF8, NULL, wszcString, int(wcslen(wszcString)), szUTF8, utf8Len, NULL, NULL); //转换
szUTF8[utf8Len] = '\0';
return szUTF8;
}
std::string utf8ToUnicode(const char* route) {
//return route;
wstring wfilepath = utf8ToUtf16(route);
return wstring2string(wfilepath);
}
std::string TCHAR2STRING(TCHAR* STR)
{
int iLen = ::WideCharToMultiByte(CP_ACP, 0, STR, -1, NULL, 0, NULL, NULL);
char* chRtn = new char[iLen * sizeof(char)];
::WideCharToMultiByte(CP_ACP, 0, STR, -1, chRtn, iLen, NULL, NULL);
std::string str(chRtn);
return str;
}
//id的排序函数
bool sortid(const student& a, const student& b)
{
if (a.id < b.id)
{
return true;
}
else {
return false;
}
}
//age排序函数
bool sortage(const student& a, const student& b)
{
if (a.age < b.age)
{
return true;
}
else {
return false;
}
}
void new_csv::output(vector<student>& vectorclient) //排序输出函数
{
for (vector<student>::iterator it = vectorclient.begin(); it != vectorclient.end(); it++)
{
::cout << "id: " << it->id << " name: " << it->name << " age: " << it->age << " hobby: " << it->hobby << endl;
}
}
int new_csv::examine(int* num, int& bit) //对输入文件格式进行判断是否有误
{
for (int i = 1; i < bit - 1; i++)
{
if (num[i] == num[i + 1])
{
cout << "csv文件对应要求无误" << endl;
return 0;
}
else
cout << "请检查文件中是否有空格,或文件编写格式有问题,导致文件信息对应不上" << endl;
return 0;
}
}
char* new_csv::Fault(string linestr)
{
char* sum;
sum = (char*)malloc(100);
int len_str = strlen(linestr.c_str());
int k = 0;
memset(sum, 0, 100);
for (int j = 0; j < len_str; j++)
{
/*if (linestr[j] == '*')
{
linestr[j] = ',';
}*/
if (linestr[j] == '"' && linestr[j + 1] == '"') //对fgets到的数组结尾的换行符进行替换
{
sum[k] = linestr[j];
k++;
j = j + 1;
}
else if (linestr[j] == '"' && linestr[j + 1] != '"') {
sum[k] = linestr[j + 1];
k++;
j = j + 1;
}
else {
if (linestr[j] == ',' && linestr[j + 1] == '"' && linestr[j-1] =='"')
{
sum[k] = '*';
k++;
}
else
{
sum[k] = linestr[j];
k++;
}
}
}
return sum;
}
int main()
{
ifstream infile("newfile.csv", ios::in);//infile来自fstream,ifstream为输入文件流(从文件读入)
string linestr;
int size_1[10] = { 0 };
char* sum;
char* Second_sum;
int n1 = 1;
new_csv pot;
vector<student> vectorclient;
int num = 0;
string key;
int k = 0;
while (getline(infile, linestr)) //读入整行数据到linestr里
{
n1 = 1; //定义对一行中的 逗号分割的每一段进行计数用来判断存储的区域
sum = pot.Fault(linestr);
stringstream ss(sum);//来自sstream
string str;
if (num > 0)
{ //实现只对数据写入vector ,不对标题写入,从而进行排序
//按照逗号分隔
student client;
while (getline(ss, str, ',')) //数据进行循环存储
{
int len = strlen(str.c_str());
for (int j = 0; j < len;j++)
{
if (str[j] == '*')
{
str[j] = ',';
}
}
//cout<<str<<endl;
switch (n1)
{
case 1:
//int intstr = atoi(str);
//需先将string转成char*
client.id = atoi(str.c_str());
break;
case 2:
client.name = utf8ToUnicode(str.c_str());
//client.name = utf8ToUnicode(char1);
break;
case 3:
client.age = atoi(str.c_str());
break;
case 4:
client.hobby = str;;// utf8ToUnicode()将一个字符串的UTF8编码转换成Unicode(UCS-2和UCS-4)编码.
break;
}
n1++;
}
size_1[num] = n1;
vectorclient.push_back(client);//每一行client数据都放到vectorclient中去
}
num++;
free(sum);
}
pot.examine(size_1, num); //对错误数据信息类型进行判断
//未排序
::cout << "————csv文件中获取的信息如下————" << endl;
//输出结果
pot.output(vectorclient);
cout << "注意两个有效key id age 请输入要排序的key:" << endl;
cin >> key;
if (key == "id")
{
sort(vectorclient.begin(), vectorclient.end(), sortid);
::cout << "————id排序后的信息如下————" << endl;
pot.output(vectorclient);
}
else if (key == "age")
{
sort(vectorclient.begin(), vectorclient.end(), sortage);
::cout << "————age排序后的信息如下————" << endl;
pot.output(vectorclient);
}
else {
cout << "无输入选项,无法排序,原信息如下" << endl;
pot.output(vectorclient);
}
return 0;
}
运行结果:
excel(看到文件中乱码,因为设置了不支持utf-8的编码,所以乱码,更改编码可以直接再notepad++中打开更改就行)
vs运行界面(通过代码将utf-8中的编码格式转换为Unicode可以正常输出中文)