OVERVIEW
- 文件读写操作
- 一、文件操作
- 1.QFile
- 2.QFileInfo
- 二、二进制文件读写
- 三、文本文件读写
文件读写操作
文件操作是应用程序必不可少的部分,Qt 作为一个通用开发库提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力,以下是 I/O 设备的类图:
- QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;
- QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。
- QFlie:访问本地文件或者嵌入资源;
- QTemporaryFile:创建和访问本地文件系统的临时文件;
- QBuffer:读写QbyteArray, 内存文件;
- QProcess:运行外部程序,处理进程间通讯;
- QAbstractSocket:所有套接字类的父类;
- QTcpSocket:TCP协议网络数据传输;
- QUdpSocket:传输 UDP 报文;
- QSslSocket:使用 SSL/TLS 传输数据;
-
顺序访问设备:数据访问从头走到尾,从第一个字节开始访问直到最后一个字节,中途不能返回去读取上一个字节,这其中QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。
-
随机访问设备:可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针,QFile、QTemporaryFile和QBuffer是随机访问设备,
一、文件操作
1.QFile
在I/O设备中文件I/O是最重要的部分之一,大多数程序依旧需要首先访问本地文件,QFile提供了从文件中读取和写入数据的能力。
将文件路径作为参数传给QFile的构造函数,使用setFileName()来修改。QFile需要使用/
作为文件分隔符,不过它会自动将其转换成操作系统所需要的形式,
QFile的有关操作:
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QString filepath = nullptr;
private:
Ui::MainWindow *ui;
};
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
//点击选取文件按钮 弹出文件对话框 读取并显示文件内容
connect(ui->open, &QPushButton::clicked, this, [=](){
//1.弹出文件选取对话框 并在文件选择后获取到文件路径
filepath = QFileDialog::getOpenFileName(this, "select a file", "C:\\Users\\Polaris\\Desktop");
//2.将文件路径放入到lineEdit组件中
ui->lineEdit->setText(filepath);
//3.读取文件中的内容 并将内容放入到textEdit组件中(QFile默认支持utf-8)
QFile file(filepath);
//3-1.设置打开方式
file.open(QIODevice::ReadOnly);
//3-2.文件读取
QByteArray array;
//QByteArray array = file.readAll();
while (!file.atEnd()) { array += file.readLine(); }
ui->textEdit->setText(array);//自动进行了类型转换
//QTextCodec *codec = QTextCodec::codecForName("gbk");//设置支持的编码格式
//ui->textEdit->setText(codec->toUnicode(array));//将array中的内容都转为gbk格式
file.close();
});
//点击保存文件按钮 将文件修改保存到磁盘中
connect(ui->save, &QPushButton::clicked, this, [=](){
if (filepath == nullptr) {
qDebug() << "please select a file first!~";
return;
}
QString content = ui->textEdit->toPlainText();
QFile file(filepath);
file.open(QIODevice::WriteOnly);
file.write(content.toUtf8().data());
file.close();
qDebug() << "write success!~";
});
}
2.QFileInfo
//点击选取文件按钮 弹出文件对话框 读取并显示文件内容
connect(ui->open, &QPushButton::clicked, this, [=](){
//1.弹出文件选取对话框 并在文件选择后获取到文件路径
filepath = QFileDialog::getOpenFileName(this, "select a file", "C:\\Users\\Polaris\\Desktop");
//2.将文件路径放入到lineEdit组件中
ui->lineEdit->setText(filepath);
//3.读取文件中的内容 并将内容放入到textEdit组件中(QFile默认支持utf-8)
QFile file(filepath);
//3-1.设置打开方式
file.open(QIODevice::ReadOnly);
//3-2.文件读取
QByteArray array;
//QByteArray array = file.readAll();
while (!file.atEnd()) { array += file.readLine(); }
ui->textEdit->setText(array);//自动进行了类型转换
//QTextCodec *codec = QTextCodec::codecForName("gbk");//设置支持的编码格式
//ui->textEdit->setText(codec->toUnicode(array));//将array中的内容都转为gbk格式
file.close();
//4.读取文件属性信息
QFileInfo info(filepath);
qDebug() << "filename:" << info.fileName();
qDebug() << "filepath:" << info.filePath();
qDebug() << "Size:" << info.size();
qDebug() << "Suffix:" << info.suffix();
qDebug() << "creatTime:" << info.created().toString("yyyy/MM/dd hh:mm:ss");
qDebug() << "updateTime:" << info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
});
可以使用QFileInfo获取有关该文件的信息,QFileInfo有很多类型的函数,比如:
- isDir()检查该文件是否是目录;
- isExecutable() 检查该文件是否是可执行文件等。
- baseName() 可以直接获得文件名;
- completeBaseName() 获取完整的文件名
- suffix() 则直接获取文件后缀名。
- completeSuffix() 获取完整的文件后缀
二、二进制文件读写
QDataStream提供了基于QIODevice的二进制数据的序列化,数据流是一种二进制流全不依赖于底层操作系统、CPU 或者字节顺序大端或小端。例如在安装了 Windows 平台的 PC 上面写入的一个数据流,可以不经过任何处理,直接拿到运行了 Solaris 的 SPARC 机器上读取。由于数据流就是二进制流,因此也可以直接读写没有编码的二进制数据,例如图像、视频、音频等。
QDataStream既能够存取 C++ 基本类型,也可以存取复杂的数据类型,例如自定义的类(实际上QDataStream对于类的存储,是将复杂的类分割为很多基本单元实现的)。
结合QIODevice,QDataStream可以很方便地对文件、网络套接字等进行读写操作。
QFile file("C:\\Users\\Polaris\\Desktop\\file.dat");
file.open(QFile::WriteOnly);//设置打开方式
QDataStream out(&file);
out << QString("this is a binary file write & save test.");
out << QString("today is a good day!~");
out << QString("how old are you?");
out << (qint32)23;
file.close();//如果不想关闭文件可以使用file.flush();
- 首先选择并打开一个文件,然后将刚刚创建的file对象的指针传递给一个QDataStream实例out,类似于std::cout标准输出流,
- QDataStream也重载了输出重定向<<运算符,逐行将QString字符串和数字输出到数据流中,由于out对象建立在file之上因此相当于将问题和答案写入file。
- 最好使用 Qt 整型来进行读写,比如程序中的qint32,这保证了在任意平台和任意编译器都能够有相同的行为。
如果省流掉最后一行代码file.close()
就会得到一个写入任何数据的空白文件,这是因为file没有正常关闭。为性能起见,数据只有在文件关闭时才会真正写入。因此必须在最后添加一行代码:
接下来将存储到文件中的答案取出来
QFile file("C:\\Users\\Polaris\\Desktop\\file.dat");
file.open(QFile::ReadOnly);//设置打开方式
QDataStream in(&file);
QString str;
qint32 num;
in >> str >> num;
file.close();//如果不想关闭文件可以使用file.flush();
注意:必须按照写入的顺序将数据读取出来,如果顺序颠倒程序行为是不确定的,严重时会直接造成程序崩溃。
既然QIODevice提供了read()、readLine()之类的函数,为什么还要有QDataStream呢?QDataStream同QIODevice有什么区别?区别在于QDataStream提供流的形式,性能比直接调用原始 API 更好。
三、文本文件读写
相较于二进制文件,文本文件是一种人可读的文件。操作这种文件需要使用QTextStream类,QTextStream和QDataStream的使用类似,只不过它是操作纯文本文件的。
QTextStream会自动将 Unicode 编码同操作系统的编码进行转换,该操作对开发人员是透明的。它也会将换行符进行转换,同样不需要自己处理。QTextStream使用 16 位的QChar作为基础的数据存储单位,同样也支持 C++ 标准类型(实际上这是将这种标准类型与字符串进行了相互转换)。
QTextStream同QDataStream的使用基本一致,
QFile file("C:\\Users\\Polaris\\Desktop\\file.dat");
file.open(QFile::ReadOnly | QFile::Truncate);//以只写并且覆盖已有内容的形式操作文件
QTextStream out(&file);
out << QString("this is a binary file write & save test.");
out << QString("today is a good day!~");
out << QString("how old are you?");
out << (qint32)23;
file.close();//如果不想关闭文件可以使用file.flush();
这里open()函数中增加了QIODevice::Truncate打开方式:
枚举值 | 描述 |
---|---|
QIODevice::NotOpen | 未打开 |
QIODevice::ReadOnly | 以只读方式打开 |
QIODevice::WriteOnly | 以只写方式打开 |
QIODevice::ReadWrite | 以读写方式打开 |
QIODevice::Append | 以追加的方式打开新增加的内容将被追加到文件末尾 |
QIODevice::Truncate | 以重写的方式打开,在写入新的数据时会将原有数据全部清除,游标设置在文件开头。 |
QIODevice::Text | 在读取时,将行结束符转换成 \n;在写入时,将行结束符转换成本地格式,例如 Win32 平台上是 \r\n |
QIODevice::Unbuffered | 忽略缓存 |
虽然QTextStream的写入内容与QDataStream一致,但是读取时却会有些困难:
QFile file("C:\\Users\\Polaris\\Desktop\\file.dat");
file.open(QFile::ReadOnly);//以只写并且覆盖已有内容的形式操作文件
QTextStream in(&file);
QString str;
qint32 num;
in >> str >> num;
file.close();//如果不想关闭文件可以使用file.flush();
在使用QDataStream的时候代码很方便,但使用了QTextStream时却有所不同,使用QDataStream写入的时候,实际上会在要写入的内容前面,额外添加一个这段内容的长度值。
而以文本形式写入数据,是没有数据之间的分隔的。因此使用文本文件时很少会将其分割开来读取,而是使用诸如使用:
- QTextStream::readLine() 读取一行
- QTextStream::readAll()读取所有文本
默认情况下,QTextStream的编码格式是 Unicode,如果我们需要使用另外的编码,可以使用 stream.setCodec("UTF-8");
这样的函数进行设置。