跟着上一篇继续。本篇介绍 Qt 重新造的轮子:容器系统和文件操作系统。
容器
按理说标准库里面就有各种容器的实现了,这里重新造轮子了?
容器分为了两类:序列容器 和 关联容器。
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; }
|
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; } }
|
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; } }
|
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()
)后重复元素被移除了,只留下一份。
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 2 3 4 5 6 7 8 9 10 11 12
| 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
| #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; } }
|
文件系统
这里涉及QFile
, QDir
, QFileInfo
三个类。其中QFile
负责操作文件(读取和写入),QDir
提供了访问路径结构及其内容的接口,QFileInfo
提供了与独立于操作系统的文件信息,包括文件名、文件存储的路径,访问时间,修改时间,权限,文件所有权信息。
文件大小
文件的大小通过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; }
|
读取文件内容
读取文件之前需要打开文件,并基于这个文件创建流。
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(); }
|
写入文件内容
要写入文件需要文件以写入模式打开。
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(); }
|
复制文件
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); }
|
文件信息的获取
- 文件所有者:
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
| #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; }
|
操作路径(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; }
|