这几天做了一个对Excel进行对比操作的一个小项目,凑了点学费:)。主要涉及到的功能就是先用QT做一个简单的界面,可供选择输入为文件或文件夹,选择完成之后对输入文件的格式进行检查,然后对比已有的数据库中(很多个文件,大概一共十几万行数据)的数据,分析输入文件中数据的错误,最后将分析后的结果写到Excel文件中,在这个项目主要涉及到以下几个知识点:
1. QT中文乱码的解决
QString 是不存在中文支持问题的,很多人遇到问题,并不是本身 QString 的问题,而是没有将自己希望的字符串正确赋给QString。
“我是中文”这样写的时候,它是传统的 char 类型的窄字符串,我们需要的只不过是通过某种方式告诉QString 这四个汉字采用的那种编码。而问题一般都出在很多用户对自己当前的编码没太多概念。
在这里介绍一篇对字符编码讲解的很透彻的一篇文章:ASCII、Unicode、GBK和UTF-8字符编码的区别联系
QString的内部是使用的unicode编码,所以本质就是将所有字符转换成unicode编码即可正常显示。
所以当使用这样的代码:1
QString a = "我是中文"
其实等价于1
2const char* s = "我是中文"
QString a = s;
那么当需要从窄字符串char转成Unicode的QString字符串时,你需要告诉Qt你的这个char 是什么编码?GBK、BIG5、Latin-1.
所以关于Qt的中文乱码,Qt4和Qt5有不同的解决方案。
在Qt4中,常用的解决方案是在main.cpp加这几行代码1
2
3
4QTextCodec *codec = QTextCodec::codecForName("system");//获取系统中文编码
QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForCStrings(codec);
QTextCodec::setCodecForTr(codec);
这几行代码就是告诉程序你的char* 中到底使用的是什么编码。
而在Qt5中取消了这几个函数,取而代之的是另外的解决方案。
用QTextCodec类中的转换函数1
2
3
4
5
6
7
8
9
10
11std::string utf82gbk(const QString &inStr)
{
QTextCodec *gbk = QTextCodec::codecForName("GB18030");
return gbk->fromUnicode(inStr).data();
}
QString gbk2utf8(const std::string &inStr)
{
QTextCodec *gbk = QTextCodec::codecForName("GB18030");
return gbk->toUnicode(inStr.c_str());
}
2. QString,string,wstring三者中文字符的比较
QString是unicode串,对应QChar为2个字节;
string一般如果不包含中文则是ascii串,包含中文会自动转换成gbk串,对应字符char 1个字节;
wstring是宽字符,也是unicode串,对应wchar_t 2字节(或4字节,linux);
1 | QString q1 = QObject::tr("abc哈哈"); |
在上面代码中,len1和len3都为5,返回的是字符数,而len2为7返回的是字节数。
下面是三者之间的互相转换1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50//QString - string
string str = qstr.toStdString();
QString qstr(str.c_str());
//QString - wstring
wstring wstr = qstr.toStdWString();
QString qstr = QString::fromStdWString(wstr);
//string - wstring
//不用C++11的方案
// 需包含locale、string头文件、使用setlocale函数。
wstring StringToWstring(const string str)
{// string转wstring
unsigned len = str.size() * 2;// 预留字节数
setlocale(LC_CTYPE, ""); //必须调用此函数
wchar_t *p = new wchar_t[len];// 申请一段内存存放转换后的字符串
mbstowcs(p,str.c_str(),len);// 转换
wstring str1(p);
delete[] p;// 释放申请的内存
return str1;
}
string WstringToString(const wstring str)
{// wstring转string
unsigned len = str.size() * 4;
setlocale(LC_CTYPE, "");
char *p = new char[len];
wcstombs(p,str.c_str(),len);
string str1(p);
delete[] p;
return str1;
}
//C++11 现在提供了专门的转换函数
wstring s2ws(const std::string& str)
{
typedef std::codecvt_utf8<wchar_t> convert_typeX;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.from_bytes(str);
}
string ws2s(const std::wstring& wstr)
{
typedef std::codecvt_utf8<wchar_t> convert_typeX;
std::wstring_convert<convert_typeX, wchar_t> converterX;
return converterX.to_bytes(wstr);
}
3. C++获取文件夹及其子文件夹下所有文件和特定后缀文件
1 | //检查文件的后缀名 |
4. xls和xlsx读取方式的不同
现有的C++ Excel处理库中,开源的解决方案中,没有可以支持.xls和.xlsx这两种文件的,所以我选取了两个库都分别处理,先判断他的后缀名,最后分析处理。
.xls的处理库我选择的是BasicExcel,但是不支持中文,需要进去源码里面去修改编码。
.xlsx的处理库选择的是Qt Xlsx,这个对中文支持较好,对xlsx文档的读写都比较友好,文档也很详细。
5.其他的小功能
比较两个字符串,忽略大小写1
2
3
4
5
6
7
8
9
10
11bool iequal(const string& str1, const string& str2) {
if (str1.size() != str2.size()) {
return false;
}
for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
if (tolower(*c1) != tolower(*c2)) {
return false;
}
}
return true;
}
6.总结
这个小工具本身比较简单,几天就写完了,但是又很多小地方需要特别注意,如规定好输入Excel文件的格式,对中文的处理,Excel文件中每个单元格中是否有空格或其他的一些违法字符,还有对输入路径合法性的检查,文件后缀的判定等等。尽管比较简单,但花了几天时间,凑了点学费还是不错的,O(∩_∩)O哈哈哈~