11、QT基础——文件系统

简介: 11、QT基础——文件系统

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):

tt.png




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是随机访问设备,


11.1 基本文件操作

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。在所有的 I/O 设备中,文件 I/O 是最重要的部分之一。因为我们大多数的程序依旧需要首先访问本地文件(当然,在云计算大行其道的将来,这一观点可能改变)。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"


11.2 二进制文件读写

QDataStream提供了基于QIODevice的二进制数据的序列化。数据流是一种二进制流,这种流完全不依赖于底层操作系统、CPU 或者字节顺序(大端或小端)。例如,在安装了 Windows 平台的 PC 上面写入的一个数据流,可以不经过任何处理,直接拿到运行了 Solaris 的 SPARC 机器上读取。由于数据流就是二进制流,因此我们也可以直接读写没有编码的二进制数据,例如图像、视频、音频等。


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对象的指针传递给一个QDataStream实例out。类似于std::cout标准输出流,QDataStream也重载了输出重定向<<运算符。后面的代码就很简单了:将“the answer is”和数字 42 输出到数据流。由于我们的 out 对象建立在file之上,因此相当于将问题和答案写入file。

需要指出一点:最好使用 Qt 整型来进行读写,比如程序中的qint32。这保证了在任意平台和任意编译器都能够有相同的行为。

如果你直接运行这段代码,你会得到一个空白的 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;


唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。


那么,既然QIODevice提供了read()、readLine()之类的函数,为什么还要有QDataStream呢?QDataStream同QIODevice有什么区别?区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API 更好一些。我们通过下面一段代码看看什么是流的形式:


QFile file("file.dat");


file.open(QIODevice::ReadWrite);



QDataStream stream(&file);


QString str = "the answer is 42";



stream << str;


11.3 文本文件读写

上一节我们介绍了有关二进制文件的读写。二进制文件比较小巧,却不是人可读的格式。而文本文件是一种人可读的文件。为了操作这种文件,我们需要使用QTextStream类。QTextStream和QDataStream的使用类似,只不过它是操作纯文本文件的。


QTextStream会自动将 Unicode 编码同操作系统的编码进行转换,这一操作对开发人员是透明的。它也会将换行符进行转换,同样不需要自己处理。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打开方式。我们可以从下表中看到这些打开方式的区别:


枚举值                描述


QIODevice::NotOpen       未打开

QIODevice::ReadOnly      以只读方式打开

QIODevice::WriteOnly     以只写方式打开

QIODevice::ReadWrite     以读写方式打开

QIODevice::Append        以追加的方式打开,

新增加的内容将被追加到文件末尾


QIODevice::Truncate      以重写的方式打开,在写入新的数据时会将原有

                        数据全部清除,游标设置在文件开头。


QIODevice::Text          在读取时,将行结束符转换成 \n;在写入时,

将行结束符转换成本地格式,例如 Win32 平台


上是 \r\n


QIODevice::Unbuffered    忽略缓存

我们在这里使用了QFile::WriteOnly | QIODevice::Truncate,也就是以只写并且覆盖已有内容的形式操作文件。注意,QIODevice::Truncate会直接将文件内容清空。



虽然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");


这样的函数进行设置。


目录
相关文章
|
开发者 Python
Python Qt GUI设计:信号与槽的使用方法(基础篇—7)
Python Qt GUI设计:信号与槽的使用方法(基础篇—7)
Python Qt GUI设计:信号与槽的使用方法(基础篇—7)
|
JavaScript 大数据
Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo
上一篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口。   本篇的demo实现了自动排序的柱状图,实现了一个自动排序柱状图的基本交互方式,即Qt调用js脚本操作html。   本篇demo使用Qt定时器方式,实现数据定时刷新自增,并预留出了定时器间隔参数。   像大数据网页常看的人口增长时间图,收入年度增长时间图等都是这一类。
Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo
|
JavaScript
Qt+ECharts开发笔记(四):ECharts的饼图介绍、基础使用和Qt封装百分比图Demo
前一篇介绍了横向柱图图。本篇将介绍基础饼图使用,并将其封装一层Qt。本篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口。
Qt+ECharts开发笔记(四):ECharts的饼图介绍、基础使用和Qt封装百分比图Demo
|
缓存 JavaScript 编译器
Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo
上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍、基础使用,并将其封装一层Qt。   本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt调用js脚本操作html。
Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo
|
Web App开发 数据可视化 JavaScript
Qt+ECharts开发笔记(一):ECharts介绍、下载和Qt调用ECharts基础柱状图Demo
使用Qt开发大数据可视化看板,基于Qt使用QWidget或者QML是很难达到ECharts大数据看板的效果,所以使用Qt来制作。
Qt+ECharts开发笔记(一):ECharts介绍、下载和Qt调用ECharts基础柱状图Demo
|
算法 数据安全/隐私保护
Qt中base64基础的应用
Qt中base64基础的应用
290 0
Qt中base64基础的应用
|
C# C++ 计算机视觉
Qt开发Activex笔记(一):环境搭建、基础开发流程和演示Demo
Qt开发Activex笔记(一):环境搭建、基础开发流程和演示Demo
|
JavaScript 定位技术 API
地图开发笔记(二):Qt与百度地图js交互的基础-Qt向Js发送指令定位各大省份与城市
地图开发笔记(二):Qt与百度地图js交互的基础-Qt向Js发送指令定位各大省份与城市
地图开发笔记(二):Qt与百度地图js交互的基础-Qt向Js发送指令定位各大省份与城市
|
Python
Qt for Python的4种基础布局管理
Qt for Python的4种基础布局管理
231 0
Qt for Python的4种基础布局管理
Python Qt GUI设计:QCalendar日历类和QDateTimeEdit时间类(基础篇—20)
Python Qt GUI设计:QCalendar日历类和QDateTimeEdit时间类(基础篇—20)
Python Qt GUI设计:QCalendar日历类和QDateTimeEdit时间类(基础篇—20)