一、文件系统:
文件操作是应用程序必不可少的部分。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 传输数据;
文件系统分类:
-
顺序访问设备:
是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节,这其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。
-
随机访问设备:
可以访问任意位置任意次数,还可以使用 QIODevice::seek() 函数来重新定位文件访问位置指针,QFile、QTemporaryFile 和 QBuffer 是随机访问设备。
二、QFile 读写文件:
在 ui 界面上拖两个按钮和一个文本编辑控件,如下:
代码如下:
// 读文件
void Widget::on_btnReadFile_clicked()
{
// 获取文件路径
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
// 根据文件路径实例化一个文件对象
QFile file(filePath);
// 以只读方式打开文件
if (file.open(QFile::ReadOnly))
{
// 读取文件:一次读取文件中所有内容
// QByteArray buf = file.readAll();
// 读取文件:一行一行读取
QByteArray buf;
while (!file.atEnd())
{
buf += file.readLine();
}
// 注意:默认情况下,如果文件中含有中文时,只有 UTF8 格式的文件能正常读取,
// 其他格式的文件读取出的中文数据都是乱码。
// 显示到 textEdit 上
ui->textEdit->setText(buf);
}
// 关闭文件
file.close();
}
}
// 写文件
void Widget::on_btnWriteFile_clicked()
{
// 保存文件路径
QString filePath = QFileDialog::getSaveFileName();
if (!filePath.isEmpty())
{
// 根据文件路径实例化一个文件对象
QFile file(filePath);
// 以只写方式打开文件
if (file.open(QFile::WriteOnly))
{
// 获取 textEdit 中内容;toPlainText 表示获取 textEdit 中内容的纯文本数据
QString text = ui->textEdit->toPlainText();
// 写入文件:并将文件格式设置为 utf8
// file.write(text.toUtf8());
// 写入文件:将数据转换成标准 C++ 的 char* 格式;
// 生成的文件还是 utf8 格式,即默认写入的数据格式就是 utf8;
// file.write(text.toStdString().data());
// 写入文件:文件格式为本地平台8位编码(windows 下默认为 ANSI)
file.write(text.toLocal8Bit());
}
// 关闭文件
file.close();
}
}
QFileDialog 的使用参考 Qt学习笔记(十二):标准文件对话框。
三、QFileInfo 获取文件信息:
// 获取文件路径
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
QFileInfo info(filePath);
qDebug() << info.absoluteFilePath(); // 获取包含文件名的绝对路径
qDebug() << info.absolutePath(); // 获取不包含文件名的绝对路径
qDebug() << info.fileName(); // 获取文件名(不包含路径)
qDebug() << info.filePath(); // 获取文件名(包括路径,绝对的或相对的)
qDebug() << info.path(); // 获取文件路径(不包含文件名)
// 获取文件的创建日期和事件("年-月-日 时:分:秒:毫秒")
qDebug() << info.created().toString("yyyy-MM-dd HH:mm:ss:zzz");
qDebug() << info.exists(); // 判断文件是否存在
qDebug() << info.suffix(); // 获取文件后缀
qDebug() << info.isExecutable(); // 是否是可执行文件
qDebug() << info.isDir(); // 是否是目录
qDebug() << info.isFile(); // 是否是文件
qDebug() << info.isHidden(); // 是否是隐藏文件
}
四、QDataStream 读写文件:
QDataStream 类提供二进制数据的序列化,到一个 IO 设备。
数据流是编码信息的二进制流,它完全独立于主机的操作系统、CPU 或字节顺序。例如,通过 windows 下 PC 编写的数据流,可以被运行在 Solaris 上的 Sun SPARC 读取。
还可以使用数据流来读取/写入未经编码的原始二进制数据。如果想要“解析”输入流,请参阅 QTextStream。
QDataStream 类实现了 c++ 基本数据类型的序列化,如 char、short、int、char * 等。更复杂数据的序列化是通过将数据分解为基本单元来实现的。
QDataStream 可以用来操作图片、音频、视频等非文本数据。
写文件:
// 写文件
void Widget::on_btnWriteFile_clicked()
{
QFile file("file.txt");
// 以只写方式打开文件
if (file.open(QIODevice::WriteOnly))
{
// 根据文件对象实例化一个数据流,向数据流里写数据,就是向文件里写数据
QDataStream stream(&file);
// 使用输出字符写数据,可以写字符串,也可以整型,或者其他类型的数据;
// 注意:因为写入的是二进制数据,所以即使写入的是 .txt 文件,文件中也是乱码;
stream << QString("主要看气质") << 22;
file.close(); // 关闭文件
}
qDebug() << "写文件成功";
}
读文件:
// 读文件
void Widget::on_btnReadFile_clicked()
{
QFile file("file.txt");
if (!file.exists())
{
qDebug() << "文件不存在";
}
else
{
// 以只读方式打开文件
if (file.open(QFile::ReadOnly))
{
// 创建数据流对象
QDataStream stream(&file);
QString str;
int a;
// 读取数据。
// 注意:读取数据的类型和顺序,需要和写入数据的类型和顺序 保持一致.
stream >> str >> a;
// Qt 默认写入的数据是 utf8 格式的
qDebug() << str.toUtf8().data() << a;
file.close(); // 关闭文件
}
}
}
使用 QDataStream 读取图片,并转换成 base64 字符串:
// 读文件
void Widget::on_btnReadFile_clicked()
{
// 获取文件路径
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
// 创建文件对象
QFile file(filePath);
// 以只读方式打开文件
if (file.open(QFile::ReadOnly))
{
// 创建数据流
QDataStream stream(&file);
char *buf = new char[1024];
QByteArray array;
// 循环读取数据
while (!stream.atEnd())
{
// 读取数据到缓冲区 buf 中,最多读取 1024 个字节,返回实际读取的字节数;
int len = stream.readRawData(buf, 1024);
// 将读取的二进制数据转换成 字节数组
array.append(QByteArray(buf, len));
}
delete buf;
// 将读取的数据转换成 base64 字符串
QString str = QString(array.toBase64());
ui->textEdit->setText(str);
// 不知道为什么此处的 qDebug() 方法不能输出!!!
qDebug() << str;
file.close(); // 关闭文件
}
}
}
读取结果:
使用 QDataStream 将 base64 字符串转换成图片:将上图 textEdit 中的数据写入图片;
// 写文件
void Widget::on_btnWriteFile_clicked()
{
// 获取保存文件路径
QString filePath = QFileDialog::getSaveFileName();
if (!filePath.isEmpty())
{
// 创建文件对象
QFile file(filePath);
// 以只写方式打开文件
if (file.open(QFile::WriteOnly))
{
// 获取 textEdit 上的文本信息
QString text = ui->textEdit->toPlainText();
// 将 base64 数据转换成 字节数组
QByteArray array = QByteArray::fromBase64(text.toUtf8());
// 创建数据流
QDataStream stream(&file);
// 写入数据:参数1表示缓存数据,参数2表示缓存数据的字节长度;返回实际写入的字节数。
int len = stream.writeRawData(array.data(), array.length());
qDebug() << QString::number(len);
file.close(); // 关闭文件
}
}
}
上面是使用 QDataStream 读写图片,并和 base64 字符串的互相转换,比较麻烦;Qt 还有更简单的方法读写图片数据,并和 base64 字符串互相转换,如下所示:
读取图片数据,并转换成 base64 字符串:
// 读文件
void Widget::on_btnReadFile_clicked()
{
// 获取文件路径
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
// 声明字节数组
QByteArray array;
// 声明缓冲区,指向字节数组(当向缓冲区写入数据时,就是在向字节数组中写入数据)
QBuffer buf(&array);
// 将图片数据保存到缓冲区
QPixmap pixmap(filePath);
pixmap.save(&buf, "jpg");
// 将数据转换成 base64 字符串
QString str = QString(array.toBase64());
ui->textEdit->setText(str);
qDebug() << str;
}
}
将 base64 字符串数据保存为图片:
// 写文件
void Widget::on_btnWriteFile_clicked()
{
// 获取保存文件路径
QString filePath = QFileDialog::getSaveFileName();
if (!filePath.isEmpty())
{
// 获取 textEdit 上的数据
QString str = ui->textEdit->toPlainText();
// 从给定字节数组加载一个图片对象
QPixmap pixmap;
pixmap.loadFromData(QByteArray::fromBase64(str.toLocal8Bit()));
// 保存图片
pixmap.save(filePath);
}
}
五、QTextStream 操作文件:
QTextStream 类为读写文本提供了一个方便的接口。
QTextStream 可以在 QIODevice、QByteArray 或 QString 上操作。使用 QTextStream 的流操作符,可以方便地读写单词、行和数字。对于生成文本,QTextStream 支持字段填充和对齐的格式化选项,以及数字的格式化。
使用 QTextStream 读取控制台输入和写入控制台输出也是常见的。QTextStream 可识别区域设置,并将使用正确的编解码器自动解码标准输入。
写入文件:
// 写文件
void Widget::on_btnWriteFile_clicked()
{
// 创建文件对象
QFile file("file.txt");
// 以只写方式打开文件
if (file.open(QFile::WriteOnly))
{
// 创建文本流对象
QTextStream stream(&file);
// 设置写入文件的编码方式(默认情况下是根据平台默认的编码)
stream.setCodec("UTF-8");
// 向流中写数据
stream << QString("主要看气质") << 250;
// 关闭文件
file.close();
}
}
写入的文件为:
可以发现,写入的两个数据没有分隔,而是紧挨在一起的;这种情况下如果使用下面的方法读取数据,就无法读出正确的数据:
// 读文件
void Widget::on_btnReadFile_clicked()
{
// 创建文件对象
QFile file("file.txt");
// 以只读方式打开文件
if (file.open(QFile::ReadOnly))
{
// 创建文本流对象
QTextStream stream(&file);
// 设置写入文件的编码方式(默认情况下是根据平台默认的编码)
stream.setCodec("UTF-8");
QString str;
int a;
// 读取数据
stream >> str >> a;
qDebug() << str.toUtf8().data() << a;
// 关闭文件
file.close();
}
}
输出结果如下:这是因为读取的数据全部给了变量 str,而整型变量 a 没有接收到数据,只有初始值 0.
六、QBuffer:表示一个内存缓冲区
QBuffer 类为 QByteArray 提供一个 QIODevice 接口。
QBuffer 允许我们使用 QIODevice 接口访问 QByteArray。QByteArray 被视为一个标准的随机访问文件。
QBuffer 相当于一个内存文件,也可以向其中读写数据:
// 声明一个内存缓冲区(相当于一个内存文件,也可以读写数据)
QBuffer buf;
// 以只写方式打开内存缓冲区
if (buf.open(QFile::WriteOnly))
{
// 向内存缓冲区写入数据
buf.write("hello");
buf.write("nihao");
buf.write("how are you");
buf.close(); // 关闭缓冲区
}
// 从缓冲区中读取数据
// 可以看到,向内存缓冲区中写的多条数据,是紧挨在一起的
qDebug() << buf.buffer();
QBuffer 可以和 QByteArray 一起使用,用 QByteArray 来存储 QBuffer 缓冲区中的数据:
// 声明一个字节数组
QByteArray array;
// 声明一个内存缓冲区,指向一个字节数组;
// 当向缓冲区中写入数据时,就是在向字节数组中写入数据;
QBuffer buf(&array);
// 以只写方式打开内存缓冲区
if (buf.open(QFile::WriteOnly))
{
// 向内存缓冲区写入数据
buf.write("hello");
buf.write("nihao");
buf.write("how are you");
buf.close(); // 关闭缓冲区
}
// 从缓冲区中读取数据
// 可以看到,向内存缓冲区中写的多条数据,是紧挨在一起的
qDebug() << buf.buffer();
qDebug() << "array:" << array;