跟着上一篇继续。本篇介绍 Qt 重新造的轮子:容器系统和文件操作系统。

1 容器

按理说标准库里面就有各种容器的实现了,这里重新造轮子了?

容器分为了两类:序列容器 和 关联容器。

1.1 QVector

动态数组容器,内部本质就是内存连续的数组,从而使得随机存取的效率非常高。但是对于元素的删除插入操作性能会比较差。对于长度很大的序列,推荐采用链表实现的的QList

QVector的例子如下。使用非常简单,不再赘述。

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
#include <QVector>
#include <QTextStream>

int main(void) {

QTextStream out(stdout);

QVector<int> vals = {1, 2, 3, 4, 5};

out << "The size of the vector is: " << vals.size() << endl;

out << "The first item is: " << vals.first() << endl;
out << "The last item is: " << vals.last() << endl;

vals.append(6);
vals.prepend(0);

out << "Elements: ";

for (int val : vals) {
out << val << " ";
}

out << endl;

return 0;
}

1.2 QList

QList 也是序列容器,内部是链表实现。其随机访问、删除、插入操作效率都比较高。因此是 Qt 中最常用的容器。参考下面👇这个例子。

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
#include <QTextStream>
#include <QList>
#include <algorithm>

int main(void) {

QTextStream out(stdout);

QList<QString> authors = {"Balzac", "Tolstoy",
"Gulbranssen", "London"};

for (int i=0; i < authors.size(); ++i) {

out << authors.at(i) << endl;
}

authors << "Galsworthy" << "Sienkiewicz";

out << "***********************" << endl;

std::sort(authors.begin(), authors.end());

out << "Sorted:" << endl;
for (QString author : authors) {

out << author << endl;
}
}

1.3 QStringList

为字符串做了优化的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <QTextStream>
#include <QList>

int main(void) {

QTextStream out(stdout);

QString string = "coin, book, cup, pencil, clock, bookmark";
QStringList items = string.split(",");
QStringListIterator it(items);

while (it.hasNext()) {
out << it.next().trimmed() << endl;
}
}

1.4 QSet

集合可以确保其中存储的值是唯一的。

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
#include <QSet>
#include <QList>
#include <QTextStream>
#include <algorithm>

int main(void) {

QTextStream out(stdout);

QSet<QString> cols1 = {"yellow", "red", "blue"};
QSet<QString> cols2 = {"blue", "pink", "orange"};

out << "There are " << cols1.size() << " values in the set" << endl;

cols1.insert("brown");

out << "There are " << cols1.size() << " values in the set" << endl;

cols1.unite(cols2);

out << "There are " << cols1.size() << " values in the set" << endl;

for (QString val : cols1) {
out << val << endl;
}

QList<QString> lcols = cols1.values();
std::sort(lcols.begin(), lcols.end());

out << "*********************" << endl;
out << "Sorted:" << endl;

for (QString val : lcols) {
out << val << endl;
}

return 0;
}

可以看到合并(unite())后重复元素被移除了,只留下一份。

1.5 QMap

QMap是关联式容器,存储 key-value 对。下面是QMap使用的例子:

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
#include <QTextStream>
#include <QMap>

int main(void) {

QTextStream out(stdout);

QMap<QString, int> items = { {"coins", 5}, {"books", 3} };

items.insert("bottles", 7);

QList<int> values = items.values();

out << "Values:" << endl;

for (int val : values) {
out << val << endl;
}

QList<QString> keys = items.keys();

out << "Keys:" << endl;
for (QString key : keys) {
out << key << endl;
}

QMapIterator<QString, int> it(items);

out << "Pairs:" << endl;

while (it.hasNext()) {
it.next();
out << it.key() << ": " << it.value() << endl;
}
}

1.6 自定义排序

自定义排序其实就是要自定义比较函数。以下面的类为例:

1
2
3
4
5
6
7
8
9
10
11
12
// book.h
class Book {

public:
Book(QString, QString);
QString getAuthor() const;
QString getTitle() const;

private:
QString author;
QString title;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// book.cpp
#include <QString>
#include "book.h"

Book::Book(QString auth, QString tit) {
author = auth;
title = tit;
}

QString Book::getAuthor() const {
return author;
}

QString Book::getTitle() const {
return title;
}

使用自定义的方式排序如下:

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
#include <QTextStream>
#include <QList>
#include <algorithm>
#include "book.h"

bool compareByTitle(const Book &b1, const Book &b2) {

return b1.getTitle() < b2.getTitle();
}

int main(void) {

QTextStream out(stdout);

QList<Book> books = {
Book("Jack London", "The Call of the Wild"),
Book("Honoré de Balzac", "Father Goriot"),
Book("Leo Tolstoy", "War and Peace"),
Book("Gustave Flaubert", "Sentimental education"),
Book("Guy de Maupassant", "Une vie"),
Book("William Shakespeare", "Hamlet")
};

std::sort(books.begin(), books.end(), compareByTitle);

for (Book book : books) {
out << book.getAuthor() << ": " << book.getTitle() << endl;
}
}

2 文件系统

这里涉及QFile, QDir, QFileInfo三个类。其中QFile负责操作文件(读取和写入),QDir提供了访问路径结构及其内容的接口,QFileInfo提供了与独立于操作系统的文件信息,包括文件名、文件存储的路径,访问时间,修改时间,权限,文件所有权信息。

2.1 文件大小

文件的大小通过QFileInfo::size()函数来获取。见下面的完整例子:

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
#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {

QTextStream out(stdout);

if (argc != 2) {
qWarning("Usage: file_size file");
return 1;
}

QString filename = argv[1];

if (!QFile(filename).exists()) {
qWarning("The file does not exist");
return 1;
}

QFileInfo fileinfo(filename);

qint64 size = fileinfo.size();

QString str = "The size is: %1 bytes";

out << str.arg(size) << endl;
}

2.2 读取文件内容

读取文件之前需要打开文件,并基于这个文件创建流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <QTextStream>
#include <QFile>

int main(void) {
QTextStream out(stdout);

QFile file("colours");

if (!file.open(QIODevice::ReadOnly)) {
qWarning("Cannot open file for reading");
return 1;
}

QTextStream in(&file);

while (!in.atEnd()) {
QString line = in.readLine();
out << line << endl;
}

file.close();
}

2.3 写入文件内容

要写入文件需要文件以写入模式打开。

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
#include <QTextStream>
#include <QFile>

int main(void) {

QTextStream out(stdout);

QString filename = "distros";
QFile file(filename);

if (file.open(QIODevice::WriteOnly)) {

QTextStream out(&file);
out << "Xubuntu" << endl;
out << "Arch" << endl;
out << "Debian" << endl;
out << "Redhat" << endl;
out << "Slackware" << endl;

} else {

qWarning("Could not open file");
}

file.close();
}

2.4 复制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <QTextStream>
#include <QFile>

int main(int argc, char *argv[]) {
QTextStream out(stdout);

if (argc != 3) {
qWarning("Usage: copyfile source destination");
return 1;
}

QString source = argv[1];

if (!QFile(source).exists()) {
qWarning("The source file does not exist");
return 1;
}

QString destin(argv[2]);

QFile::copy(source, destin);
}

2.5 文件信息的获取

  • 文件所有者:QFileInfo::group()QFileInfo::owner(),返回的都是QString;
  • 修改时间: QFileInfo::lastRead()QFileInfo::lastModified(),返回的是QDateTime;
  • 文件路径:参见下面的例子
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
// filepath.cpp
#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {
QTextStream out(stdout);

if (argc != 2) {
out << "Usage: file_times file" << endl;
return 1;
}

QString filename = argv[1];

QFileInfo fileinfo(filename);

QString absPath = fileinfo.absoluteFilePath();
QString baseName = fileinfo.baseName();
QString compBaseName = fileinfo.completeBaseName();
QString fileName = fileinfo.fileName();
QString suffix = fileinfo.suffix();
QString compSuffix = fileinfo.completeSuffix();

out << "Absolute file path: " << absPath << endl;
out << "Base name: " << baseName << endl;
out << "Complete base name: " << compBaseName << endl;
out << "File name: " << fileName << endl;
out << "Suffix: " << suffix << endl;
out << "Whole suffix: " << compSuffix << endl;
}

输出为

1
2
3
4
5
6
7
$ ./file_path ~/Downloads/qt-everywhere-opensource-src-5.5.1.tar.gz 
Absolute file path: /home/janbodnar/Downloads/qt-everywhere-opensource-src-5.5.1.tar.gz
Base name: qt-everywhere-opensource-src-5
Complete base name: qt-everywhere-opensource-src-5.5.1.tar
File name: qt-everywhere-opensource-src-5.5.1.tar.gz
Suffix: gz
Whole suffix: 5.1.tar.gz
  • 权限信息:通过QFile::permissions方法可以权限信息,具体的例子如下:
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <QTextStream>
#include <QFile>

int main(int argc, char *argv[]) {
QTextStream out(stdout);

if (argc != 2) {
out << "Usage: permissions file" << endl;
return 1;
}

QString filename = argv[1];

QFile::Permissions ps = QFile::permissions(filename);

QString fper;

if (ps & QFile::ReadOwner) {
fper.append('r');
} else {
fper.append('-');
}

if (ps & QFile::WriteOwner) {
fper.append('w');
} else {
fper.append('-');
}

if (ps & QFile::ExeOwner) {
fper.append('x');
} else {
fper.append('-');
}

if (ps & QFile::ReadGroup) {
fper.append('r');
} else {
fper.append('-');
}

if (ps & QFile::WriteGroup) {
fper.append('w');
} else {
fper.append('-');
}

if (ps & QFile::ExeGroup) {
fper.append('x');
} else {
fper.append('-');
}

if (ps & QFile::ReadOther) {
fper.append('r');
} else {
fper.append('-');
}

if (ps & QFile::WriteOther) {
fper.append('w');
} else {
fper.append('-');
}

if (ps & QFile::ExeOther) {
fper.append('x');
} else {
fper.append('-');
}

out << fper << endl;
}

2.6 操作路径(directories)

  • 创建文件夹: QDir::mkdir(),返回布尔值表明创建是否成功。QDir::mkpath()也是创建文件夹 ,其区别在于mkpath()函数会同时创建缺少的父文件夹。
  • 重命名: QDir::exists(), 文件是否存在。
  • 获取 一些特殊的文件夹的位置:
    • QDir::currentPath()
    • QDir::homePath()
    • QDir::tempPath()
    • QDir::rootPath()
  • 输出文件夹内的内容
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
#include <QTextStream>
#include <QFileInfo>
#include <QDir>

int main(int argc, char *argv[]) {
QTextStream out(stdout);

if (argc != 2) {
qWarning("Usage: list_dir directory");
return 1;
}

QString directory = argv[1];

QDir dir(directory);

if (!dir.exists()) {
qWarning("The directory does not exist");
return 1;
}

dir.setFilter(QDir::Files | QDir::AllDirs);
dir.setSorting(QDir::Size | QDir::Reversed);

QFileInfoList list = dir.entryInfoList();

int max_size = 0;

foreach (QFileInfo finfo, list) {
QString name = finfo.fileName();
int size = name.size();
if (size > max_size) {
max_size = size;
}
}

int len = max_size + 2;

out << QString("Filename").leftJustified(len).append("Bytes") << endl;

for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
QString str = fileInfo.fileName().leftJustified(len);
str.append(QString("%1").arg(fileInfo.size()));
out << str << endl;
}

return 0;
}