文件系统
文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):
QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;
QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。
QFlie:访问本地文件或者嵌入资源;
QTemporaryFile:创建和访问本地文件系统的临时文件;
QBuffer:读写QbyteArray, 内存文件;
QProcess:运行外部程序,处理进程间通讯;
QAbstractSocket:所有套接字类的父类;
QTcpSocket:TCP协议网络数据传输;
QUdpSocket:传输 UDP 报文;
QSslSocket:使用 SSL/TLS 传输数据;
1 基本文件操作
QFile提供了从文件中读取和写入数据的能力。
我们通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改。
QFile需要使用 / 作为文件分隔符,不过,它会自动将其转换成操作系统所需要的形式。例如 C:/windows 这样的路径在 Windows 平台下同样是可以的。
QFile主要提供了有关文件的各种操作,比如打开文件、关闭文件、刷新文件等。我们可以使用QDataStream或QTextStream类来读写文件,也可以使用QIODevice类提供的read()、readLine()、readAll()以及write()这样的函数。值得注意的是,有关文件本身的信息,比如文件名、文件所在目录的名字等,则是通过QFileInfo获取,而不是自己分析文件路径字符串。
下面我们使用一段代码来看看QFile的有关操作:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QFile file("in.txt"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << "Open file failed."; return -1; } else { while (!file.atEnd()) { qDebug() << file.readLine(); } } QFileInfo info(file); qDebug() << info.isDir(); qDebug() << info.isExecutable(); qDebug() << info.baseName(); qDebug() << info.completeBaseName(); qDebug() << info.suffix(); qDebug() << info.completeSuffix(); return app.exec(); }
我们首先使用QFile创建了一个文件对象。
- 这个文件名字是 in.txt。如果你不知道应该把它放在哪里,可以使用QDir::currentPath()来获得应用程序执行时的当前路径。只要将这个文件放在与当前路径一致的目录下即可。
- 使用open()函数打开这个文件,打开形式是只读方式,文本格式。
这个类似于fopen()的 r 这样的参数。open()函数返回一个 bool 类型,如果打开失败,我们在控制台输出一段提示然后程序退出。否则,我们利用 while 循环,将每一行读到的内容输出。
- 可以使用QFileInfo获取有关该文件的信息。
QFileInfo有很多类型的函数,我们只举出一些例子。比如:
- isDir()检查该文件是否是目录;
- isExecutable() 检查该文件是否是可执行文件等。
- baseName() 可以直接获得文件名;
- completeBaseName() 获取完整的文件名
- suffix() 则直接获取文件后缀名。
- completeSuffix() 获取完整的文件后缀
我们可以由下面的示例看到,baseName()和completeBaseName(),以及suffix()和completeSuffix()的区别:
QFileInfo fi("/tmp/archive.tar.gz"); QString base = fi.baseName(); // base = "archive" QString base = fi.completeBaseName(); // base = "archive.tar" QString ext = fi.suffix(); // ext = "gz" QString ext = fi.completeSuffix(); // ext = "tar.gz"
2 二进制文件读写
我们可以直接读写没有编码的二进制数据,例如图像、视频、音频等。
QDataStream既能够存取 C++ 基本类型,如 int、char、short 等,也可以存取复杂的数据类型,例如自定义的类。实际上,QDataStream对于类的存储,是将复杂的类分割为很多基本单元实现的。
结合QIODevice,QDataStream可以很方便地对文件、网络套接字等进行读写操作。我们从代码开始看起:
QFile file("file.dat"); file.open(QIODevice::WriteOnly); QDataStream out(&file); out << QString("the answer is"); out << (qint32)42;
如果你直接运行这段代码,你会得到一个空白的 file.dat,并没有写入任何数据。这是因为我们的file没有正常关闭。为性能起见,数据只有在文件关闭时才会真正写入。因此,我们必须在最后添加一行代码:
file.close(); // 如果不想关闭文件,可以使用 file.flush();
接下来我们将存储到文件中的答案取出来
QFile file("file.dat"); file.open(QIODevice::ReadOnly); QDataStream in(&file); QString str; qint32 a; in >> str >> a;
唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。
QDataStream同QIODevice有什么区别?
区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API 更好一些。
3 文本文件读写
QTextStream使用 16 位的QChar作为基础的数据存储单位,同样,它也支持 C++ 标准类型,如 int 等。实际上,这是将这种标准类型与字符串进行了相互转换。
QTextStream同QDataStream的使用基本一致,例如下面的代码将把“The answer is 42”写入到 file.txt 文件中:
QFile data("file.txt"); if (data.open(QFile::WriteOnly | QIODevice::Truncate)) { QTextStream out(&data); out << "The answer is " << 42; }
这里,我们在open()函数中增加了QIODevice::Truncate打开方式。我们可以从下表中看到这些打开方式的区别:
QTextStream与QDataStream注意
虽然QTextStream的写入内容与QDataStream一致,但是读取时却会有些困难:
QFile data("file.txt"); if (data.open(QFile::ReadOnly)) { QTextStream in(&data); QString str; int ans = 0; in >> str >> ans; }
在使用QDataStream的时候,这样的代码很方便,但是使用了QTextStream时却有所不同:读出的时候,str 里面将是 The answer is 42,ans 是 0。这是因为当使用QDataStream写入的时候,实际上会在要写入的内容前面,额外添加一个这段内容的长度值。而以文本形式写入数据,是没有数据之间的分隔的。因此,使用文本文件时,很少会将其分割开来读取,而是使用诸如使用:
QTextStream::readLine() 读取一行
QTextStream::readAll()读取所有文本
这种函数之后再对获得的QString对象进行处理。
默认情况下,QTextStream的编码格式是 Unicode,如果我们需要使用另外的编码,可以使用:
stream.setCodec("UTF-8");
这样的函数进行设置。
Qt 工程案例
#include "widget.h" #include "ui_widget.h" #include <QFileDialog> #include <QMessageBox> #include <QFile> #include <QTextCodec> #include <QFileInfo> #include <QDebug> #include <QDateTime> #include <QTextStream> Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //点击按钮 选取文件 connect(ui->pushButton,&QPushButton::clicked,[=](){ QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\zhangtao\\Desktop"); if(path.isEmpty()) { QMessageBox::warning(this,"警告","打开失败"); } else { //将路径 放入到lineEdit ui->lineEdit->setText(path); //读取文件 //QFile默认支持UTF-8格式 //QTextCodec * codec = QTextCodec::codecForName("gbk"); QFile file(path); //参数路径名称 //指定打开方式(只读) //file.open(QIODevice::ReadOnly); file.open(QFileDevice::ReadOnly); QByteArray array; array = file.readAll(); // while( !file.atEnd()) // { // array += file.readLine(); // } //设置到 文本编辑框中 ui->textEdit->setText(array); file.close(); //读gbk // ui->textEdit->setText( codec->toUnicode(array)); //写文件 //重新指定打开方式 // file.open(QFileDevice::Append); // file.write("哦哦哦哦哦哦"); // file.close(); //通过QFileInfo读取文件信息 QFileInfo info(path); qDebug() << "路径: "<< info.filePath() << " 名称: " <<info.fileName() << " 文件大小" << info.size() << "后缀名:"<<info.suffix(); qDebug() << "创建日期:" <<info.created().toString("yyyy-MM-dd hh:mm:ss"); qDebug() << "修改日期:" <<info.lastModified().toString("yyyy/MM/dd hh:mm:ss"); } }); //文件流读写文件 //分类:文本流(基础数据类型) 和 数据流 (大型QImage) //文本流 // QFile file("aaa.txt"); // file.open(QFileDevice::WriteOnly); // QTextStream stream(&file); // stream<< QString("hello World") << 123456 ; // file.close(); // //读取 // file.open(QFileDevice::ReadOnly); // QString str; // //stream >>str; //读取空格 结束 // str = stream.readAll(); // qDebug() << str; //数据流 二进制 QFile file("bbb.txt"); file.open(QFileDevice::WriteOnly); QDataStream stream(&file); stream << QString("hello World") << 123456; file.close(); //读数据 file.open(QFileDevice::ReadOnly); QString str; int num; stream >>str >> num; qDebug() << str << num; } Widget::~Widget() { delete ui; }