以普通文件为例(文本文件和二进制文件在这里统称为普通文件),它们是存放在计算机的硬盘上的,在Qt中它被视为一种特殊的IO设备,操作IO设备实质上是对一段存储空间(以块为单位)的读写。
QIODevice是一个抽象类(类内含纯虚函数,不可生成对象),为IO设备提供了共同的实现和接口,支持读写QFile、QBuffer和QTCPSocket等以块为单位的数据。IO操作的常用接口有:
() 打开设备:
bool open(OpenMode mode)
() 读取数据:
qint64 read(char * data, qint64 maxSize)
QByteArray read(qint64 maxSize)
QByteArray readAll()
qint64 readLine(char * data, qint64 maxSize)
QByteArray readLine(qint64 maxSize = )
() 写入数据:
qint64 write(const char * data, qint64 maxSize)
qint64 write(const char * data)
qint64 write(const QByteArray &byteArray)
() 关闭设备
void close()
QIODevice中区分了两种类型的设备:随机读写设备和顺序读写设备,对于随机读取设备还可以调用seek()函数定位任意位置,调用pos()函数获取当前位置。QFile类和QBuffer就是随机读取设备的例子。
1. QFile文件类
QFile是Qt中用于文件操作的类,生成的对象需要对应到计算机上的某个文件。建立Qt控制台程序:
#include <QtCore/QCoreApplication>
#include <QFile>
#include <QDebug>
#include <QFileInfo>
#include <QDateTime>
#include <QByteArray>
void write_file(QString file_path)
{
QFile file(file_path);
//以只读文本的方式打开(默认打开方式是数据文件)
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
file.write("QFile test\n");
file.write("2017.09.14");
file.close();
}
}
void read_file(QString file_path)
{
QFile file(file_path);
//以只写文本的方式打开
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
//读取文件全部内容
QByteArray dat = file.readAll();
QString str(dat);
qDebug() << str;
file.close();
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
write_file("./tmp.txt");
read_file("./tmp.txt");
return a.exec();
}
运行:
另外通过类QFileInfo可读取文件属性信息:
#include <QFileInfo>
#include <QDateTime>
#include <QByteArray>
void file_info(QString file_path)
{
QFile file(file_path);
QFileInfo info(file);
qDebug() << info.exists();
qDebug() << info.isFile();
qDebug() << info.isReadable();
qDebug() << info.isWritable();
qDebug() << info.created();
qDebug() << info.lastRead();
qDebug() << info.lastModified();
qDebug() << info.path();
qDebug() << info.fileName();
qDebug() << info.suffix();
qDebug() << info.size();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
file_info("./tmp.txt");
return a.exec();
}
运行:
2. QTempporaryFile临时文件类
QTempporaryFile是Qt提供的临时文件操作类,它能安全地创建一个系统唯一的临时文件夹,当QTempporaryFile对象呗销毁时,临时文件也会随之被删除。临时文件的打开操作被QTempporaryFile封装,方式固定为QIODevice::ReadWrite。临时文件主要运用于进程间大批量数据的传输或者进程间通讯。
#include <QTemporaryFile>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTemporaryFile tmp_file;
if (tmp_file.open())
{
tmp_file.write("tmp file test!\n");
tmp_file.write("2017.09.14\n");
//一定要close,不然通过cat命令并不可看到文件的内容
tmp_file.close();
}
QFileInfo info(tmp_file);
qDebug() << info.path();
qDebug() << info.fileName();
return a.exec();
}
运行:
3. 文本文件和数据文件
(1) 文本文件是指文件中的内容是由文本构成的,文本指的是ASCII码字符。在计算机看来,文件的内容本质上都是数字0/1。那为什么我们用编辑器打开文本文件显示的是ASCII的文本字符?这是因为编辑器默认对文件的编码都是ASCII码。文本文件的好处就是可以轻易被人读写。
(2) 二进制文件本质上还是数字,只不过这些数字并非文本字符的编码,而是真正意义上的数据,常见的.bin文件,文件内都是机器码(0/1),Linux上的elf可执行文件(elf文件是除了机器码外还包含其他额外信息,如段的加载地址、运行地址、重定位表等)。
从本质上看,文本文件和二进制文件并没有任何差别,都是一个文件中存放了数字。区别在于对文件内容的理解方式,若将文件上的数字当做数字处理则是二进制文件,若将文件上的数字按照某种编码方式去解码就成为文本文件。
从操作系统层面同样不区分这两种文件。比如,
利用系统调用或者标准c库函数的文件操作函数去实现将结构体变量写入文本文件:
typedef struct {
char name[];
int age;
float score;
}stu;
int main(void)
{
stu stu1 = {
.name = "tom",
.age = ,
.score = ,
};
stu ret = {};
FILE* fp = fopen("./tmp.file", "a+");
fwrite(&stu1, sizeof(stu1), , fp);
fseek(fp, , SEEK_SET);
fread(&ret, sizeof(ret), , fp);
printf("ret.name = %s, ret.age = %d, ret.score = %.2f\n", ret.name, ret.age, ret.score);
fclose(fp);
return ;
}
编译运行:
通过file命令知该文件的类型为data,也就是二进制文件;
再编程将字符串变量写入文本文件:
int main(void)
{
FILE* fp = fopen("./tmp.file", "a+");
fwrite("hello\n", , , fp);
fseek(fp, , SEEK_SET);
fread(ret, , , fp);
printf("%s\n", ret);
fclose(fp);
return ;
}
编译运行:
通过file命令查看该文件的类型为ASCII text。
然而不管是文本文件类型还是数据文件类型,只要在调用fread()函数时候使用与之对应的数据类型来接收,就可以得到程序员所预知的结果而非乱码。
4. Qt中的文本流和数据流
在了解完文本文件和数据文件之后,回到Qt。假设现在要通过Qt的文件操作写入double类型的变量,利用QFile类的read()/write()函数该如何实现?要写入的是double类型的变量而非字符串,显然被写入的文件是个二进制文件。
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QFile file("./tmp.file");
if (file.open(QIODevice::WriteOnly))
{
double dat = ;
//需要强制转换为char*类型
file.write((char*)&dat, sizeof(dat));
file.write(reinterpret_cast<char*>(&dat), sizeof(dat));
file.close();
}
if (file.open(QIODevice::ReadOnly))
{
double dat = ;
//需要强制转换为char*类型
//file.read((char*)(&dat), sizeof(dat));
file.read(reinterpret_cast<char*>(&dat), sizeof(dat));
file.close();
qDebug() << dat;
}
return a.exec();
}
QFile的read()/write()函数在写入非字符串变量需要将非字符串变量强制转换为char*类型,也就是将变量强制为一个字节一个字节的写。跟前面的讲解文本文件和数据文件的程序一样,程序员需要很清楚的写入的变量的类型,然后读出的变量的类型需要以相同类型取解析,在Qt实现中需要涉及到了诸多强制类型转换。于是,Qt提供了两个普通文件操作的辅助类,简化了文本文件/数据文件的读写。
(1) QTextStream:写入的数据全部转换为可读文本
(2) QDataStream: 写入的数据根据类型转化为二进制数据
//以文本流的方式读写文件
void text_stream(QString path)
{
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream out(&file);
out << QString("hello") << endl;
out << << '*' << << '=' << * << endl;
file.close();
}
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&file);
while (!in.atEnd())
{
QString l = in.readLine();
qDebug() << l;
}
file.close();
}
}
//以数据流的方式读写文件
void data_stream(QString path)
{
QFile file(path);
if (file.open(QIODevice::WriteOnly))
{
QDataStream out(&file);
//设置Qt版本,以使得数据流为版本的数据流
out.setVersion(QDataStream::Qt_4_7);
out << QString("hello");
out << ;
file.close();
}
if (file.open(QIODevice::ReadOnly))
{
QDataStream in(&file);
QString str = "";
double dat = ;
//设置Qt版本
in.setVersion(QDataStream::Qt_4_7);
in >> str;
in >> dat;
file.close();
qDebug() << str;
qDebug() << dat;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
text_stream("./tmp.txt");
data_stream("./tmp.txt");
return a.exec();
}
运行:
需要注意,不同的Qt版本的数据流文件格式可能被不同,当数据流文件可能在不同版本的Qt程序中传递数据时,需要在代码中设置版本信息。需要注意,不同的Qt版本的数据流文件格式可能被不同,当数据流文件可能在不同版本的Qt程序中传递数据时,需要在代码中设置版本信息。
5. 缓冲区类QBuffer
缓冲区是什么,干什么用的?做嵌入式软件程序员再熟悉不过了。在Qt中定义了缓冲区类QBuffer,并将缓冲区看作是一种特殊的IO设备。那就简单了,前面讲到的文件操作函数和文件流辅助类就可以直接用于操作缓冲区了。
#include <QBuffer>
void write_buffer(QBuffer& buffer)
{
if( buffer.open(QIODevice::WriteOnly) )
{
QDataStream out(&buffer);
out << QString("QBuffer test");
out << ;
buffer.close();
}
}
void read_buffer(QBuffer& buffer)
{
if( buffer.open(QIODevice::ReadOnly) )
{
QDataStream in(&buffer);
QString str = "";
double i;
in >> str;
in >> i;
qDebug() << str;
qDebug() << i;
buffer.close();
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray array;
QBuffer buffer(&array);
write_buffer(buffer);
read_buffer(buffer);
return a.exec();
}
运行: