Qt 输入输出设备和文件操作

Posted houxian1103

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt 输入输出设备和文件操作相关的知识,希望对你有一定的参考价值。

一、输入输出设备

QIODevice类是Qt中所有I/O设备的基础接口类,为诸如QFile、QBuffer和 QTcpSocket等支持读/写数据块的设备提供了一个抽象接口。QIODevice类是抽象的,无法被实例化,一般是使用它所定义的接口来提供设备无关的I/O功能。
QIODevice类的继承关系图
QIODevice会区别两种类型的设备:随机存取设备和顺序存储设备。(可以在程序中使用isSequentiaU)函数来判断设备的类型。)

  • 随机存取设备支持使用seek()函数来定位到任意的位置。文件中的当前位置可以使用pos()函数来获取。这样的设备有QFile、QBuffer等。
  • 顺序存储设备不支持定位到任意的位置,数据必须一次性读取。Pos()和size()
    等函数无法在操作顺序设备时使用。这样的设备有QTcpSocket、QUdpSocket 和QProcess等。

文件打开模式

访问一个设备以前,需要使用open()函数打开该设备,而且必须指定正确的打开模式。QIODevice中所有的打开模式由QIODevice::OPenMode枚举变量定义,其取值如下表所列,其中的一些值可以使用按位或符号“丨”来同时使用。打开设备后可以使用write()或者putChar()来进行写人,使用read()、readLine()或者readAll()进行读取,最后使用close()关闭设备。
在这里插入图片描述

二、文件 QFile

QFile类提供了一个用于读/写文件的接口,它是一个可以用来读/写文本文件、二进制文件和Qt资源的I/O设备。QFile可以单独使用,也可以和QTextStream或者QDataStream—起使用,这样会更方便。
一般在构建QFile对象时便指定文件名,当然也可以使用setFileName()在其他任何时间进行设置。可以使用exists()来检査文件是否存在,remove()来删除一个文件。更多与文件系统相关的高级操作在QFilelnfo和QDir类中提供。
一个文件可以使用open()打开,使用close()关闭,使用flush()刷新。文件的数据读写一般使用QTextStream或者QDataStream来完成,不过也可以使用继承自QIODevice类的一些函数,比如read()、readLine()、readAll()和write(),还有一次只操作一个字符的getChar()、putChar()和ungetChar()等函数。可以使用size()函数来获取文件的大小,使用seek()来定位到文件的任意位置,使用pos()来获取当前的位置,使用atEnd()来判断是否到达了文件的末尾。
(1)直接使用QFile读写文件
示例程序如下:

//直接使用QFile写文件
QFile fileW("test.txt");
if (!fileW.open(QIODevice::WriteOnly | QIODevice::Text))
	return;
fileW.write("first line!\\nsecond line");
fileW.close();

//直接使用QFile读文件
QFile fileR("test.txt");
if (!fileR.open(QIODevice::ReadOnly | QIODevice::Text))
	return;
while (!fileR.atEnd())

	QByteArray line = fileR.readLine();
	qDebug() <<line;

fileR.close();

(2)使用文本流读/写文本文件
QTextStream类提供了一个方便的接口来读/写文本,可以在QIODevice、QByteArray和QString上进行操作。使用QTextStream的流操作符,可以方便地读/写单词、行和数字。示例程序如下:

//使用QTextStream文本流读文件
QFile fileW("test.txt");
if (!fileW.open(QIODevice::WriteOnly | QIODevice::Text))
	return -1;
QTextStream out(&fileW);
out << "first line" << "\\nsecond line";
fileW.close();

//使用QTextStream文本流写文件
QFile fileR("test.txt");
if (!fileR.open(QIODevice::ReadOnly | QIODevice::Text))
	return -1;
QTextStream in(&fileR);
while (!in.atEnd())

	QString line = in.readLine();
	qDebug() <<line;


可以使用seek()来定位到一个指定位置,使用atEnd()判断是否还有可以读取的数据。如果调用了flush()函数,QText­Stream会清空写缓冲中的所有数据,并且调用设备的flush()函数。

在内部,QTextStream使用了一个基于Unicode的缓冲区,QTextStream使用QTextCodec来自动支持不同的字符集。默认的,使用QTextCodec ::codecForLocale()返回的编码来进行读写,也可以使用setCodec()函数来设置编码。
(3)使用数据流读/写二进制数据
QDataStream类实现了将QIODevice的二进制数据串行化。一个数据流就是一个二进制编码信息流,完全独立于主机的操作系统、CPU和字节顺序。数据流也可以读/写未编码的原始二进制数据。QDataStream类可以实现C+ +基本数据类型的串行化,比如char、Sh〇rt、im和char*等。而串行化更复杂的数据,是通过将数据分解为基本的数据类型来完成的。示例程序如下:

//使用QTextStream文本流读文件
QFile fileW("test.txt");
if (!fileW.open(QIODevice::WriteOnly))
    return -1;
QDataStream out(&fileW);
//串行化字符串(写入时,要求强制转换为QString类型,否则写入的是乱码)
out << QString("the answer is");
//串行化整数
out << static_cast<qint32>(42);
fileW.close();

//使用QTextStream文本流写文件
QFile fileR("test.txt");
if (!fileR.open(QIODevice::ReadOnly))
    return -1;
QDataStream in(&fileR);
QString str;
qint32 num;
in >> str >> num;
qDebug() << str << num; //输出:"the answer is" 42
fileR.close();

写到数据流中的每一个条目都是使用一个预定义的格式写入的,这个格式依赖于条目的类型。(否则写入的会是乱码)支持的 Qt 类型包括 QBrush、QColor、QDateTime、QFom、QPixmap、 QString、QVariant和很多其他格式。

三、文件信息 QFilelnfo

QFilelnfo类提供了与系统无关的文件信息,包括文件的名称、在文件系统中的位置(路径)、文件的访问权限以及是否是一个目录或者符号链接等 。QFilelnfo也可以获取文件的大小和最近一次修改/读取的时间,还可以获取Qt资源的相关信息 。 QFilelnfo指向的文件可以在QFileinfo对象构建时设置,或者以后使用setFile()来设置。

  • QFileInfo可以使用相对(relative)路径或者绝对(absolute)路径来指向一个文件,还可以使用makeAbsolute()来将一个相对路径转换为绝对路径。
  • 文件的类型可以使用isFile()、isDir()和isSymLink()来获取。
    可以分别使用path()和fileName()来获取文件的路径和文件名,还可以使用baseName()来获取文件名中的基本名称,使用suffix()来获取文件名的后缀,使用completeSuffix()来获取复合后缀。
  • 文件的日期可以使用created()、lastModified()和lastRead()来返回;
  • 访问权限可以使用isReadable()、isWritable()和isExecutable()来获取;
  • 文件的所有权可以使用owner()、ownerId()、group()和groupId()来获取。

新建Qt5控制台应用,名称为myFile,创建完成后将main.cpp文件的内容更改为:

#include <QtCore/QCoreApplication>
#include <QFileInfo>
#include <QTextCodec>
#include <QStringList>
#include <QDateTime>
#include <QDebug>

int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);

    // 以只写方式打开,如果文件不存在,那么会创建该文件
    QFile file("myfile.txt");
    if (!file.open(QIODevice::WriteOnly  | QIODevice::Text))
        qDebug() << file.errorString();
    file.write("helloQt!\\nyafeilinux");
    file.close();

    // 获取文件信息
    QFileInfo info(file);
    qDebug() << QObject::tr("绝对路径:") << info.absoluteFilePath() << endl
             << QObject::tr("文件名:") << info.fileName() << endl
             << QObject::tr("基本名称:") << info.baseName() << endl
             << QObject::tr("后缀:") << info.suffix() << endl
             << QObject::tr("创建时间:") << info.created() << endl
             << QObject::tr("大小:") << info.size();

    // 以只读方式打开
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        qDebug() << file.errorString();
    qDebug() << QObject::tr("文件内容:") << endl << file.readAll();
    qDebug() << QObject::tr("当前位置:") << file.pos();
    //移动文件开始读取位置为0
    file.seek(0);
    QByteArray array;
    array = file.read(5);
    qDebug() << QObject::tr("前5个字符:") << array
             << QObject::tr("当前位置:") << file.pos();
    //移动文件开始读取位置为15
    file.seek(15);
    array = file.read(5);
    qDebug() << QObject::tr("第16-20个字符:") << array;
    file.close();

    return a.exec();


执行程序,控制台输出如下:

"绝对路径:" "D:/Project/C++/Qt/myFile/build-myFile-Desktop_Qt_5_9_7_MinGW_32bit-Debug/myfile.txt"
"文件名:" "myfile.txt"
"基本名称:" "myfile"
"后缀:" "txt"
"创建时间:" QDateTime(2019-06-17 16:58:01.230 中国标准时间 Qt::TimeSpec(LocalTime))
"大小:" 20
"文件内容:"
"helloQt!\\nyafeilinux"
"当前位置:" 20
"前5个字符:" "hello" "当前位置:" 5
"第16-20个字符:" "linux"

四、临时文件 QTemporaryFile

QTemporaryFile类是一个用来操作临时文件的I/O设备,它可以安全地创建一个唯一的临时文件。当调用open()函数时便会创建一个临时文件,临时文件的文件名可以保证是唯一的,当销毁QTemporaryFile对象时,该文件会被自动删除掉。
在调用open()函数时,默认会使用QIODevice::ReadWrite模式,可以像下面的代码这样来使用QTemporaryFile类:

QTemporaryFile file;
if (file.open()) 

    // 在这里对临时文件进行操作,file.fileName()可以返回唯一的文件名


在调用了close()函数后重新打开QTemporaryFile是安全的,只要QTemporaryFile的对象没有被销毁,那么唯一的临时文件就会一直存在而且由QTemporaryFile内部保持打开。临时文件默认会生成在系统的临时目录里,这个目录的路径可以使用QDir::tempPath()来获取。

Qt_IO系统_文件

主要参考: devbean.net 豆子的博客
参考书:《QtCreator 快速入门》第三版

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。
从本章开始,我们来了解下 Qt 的文件以及输入输出的功能,也就是 I/O 系统。

Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图:

Qt4 图片用的豆子的
技术图片

Qt5 图片用的豆子的

技术图片

从上面的类图可以看出,Qt4 和 Qt5 在 I/O 设备部分大同小异。只不过 Qt5 新增加了一个QFileDevice类。图中所涉及的类及其用途简要说明如下:

  • QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;
  • QFlie:访问本地文件或者访问Qt资源系统中的文件;
  • QTemporaryFile:创建和访问本地文件系统的临时文件;
  • QBuffer:读写QByteArray
  • QProcess:运行外部程序,处理进程间通讯;
  • QAbstractSocket:所有套接字类的父类;
  • QTcpSocket:TCP协议网络数据传输;
  • QUdpSocket:传输 UDP 报文;
  • QSslSocket:使用 SSL/TLS 传输数据;
  • QFileDevice: Qt5新增加的类,提供了有关文件操作的通用实现。

顺序访问设备: 其中,QProcessQTcpSocketQUdpSoctetQSslSocket是顺序访问设备。所谓“顺序访问”,是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节.

随机访问设备QFileQTemporaryFileQBuffer是随机访问设备,可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针。

QFile

这里主要介绍QFile及其相关类,后面则开始介绍有关输入输出的流。

在所有的 I/O 设备中,文件 I/O 是最重要的部分之一。因为我们大多数的程序依旧需要首先访问本地文件(当然,在云计算大行其道的将来,这一观点可能改变)。QFile提供了从文件中读取和写入数据的能力。Qt5 新加入的QFileDevice类,则将这部分公共操作放到了这个单独的类中。显然,这部分代码在 Qt4 中位于QFile类。这样看来,Qt5 的代码结构更为清晰,层次更好。

如何使用QFile

通常会将文件路径作为参数传给QFile的构造函数。不过也可以在创建好对象最后,使用setFileName()来修改。
QFile需要使用 / 作为文件分隔符,不过,它会自动将其转换成操作系统所需要的形式。例如 C:/windows 这样的路径在 Windows 平台下同样是可以的。

QFile主要提供了有关文件的各种操作,比如打开文件(open(...))、关闭文件(close()、刷新文件(flush())等。
我们可以使用QDataStreamQTextStream类来读写文件,也可以使用QIODevice类提供的read()readLine()readAll()以及write()这样的函数。

值得注意的是,有关文件本身的信息,比如文件名、文件所在目录的名字等,则是通过QFileInfo获取,而不是自己分析文件路径字符串。

QFile 和QFileInfo Demo

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    // QFile 类的使用
    QFile file("in.txt"); // 当前路径 QDir::currentPath(),所以需要在此目录下创建in.txt 文件。


    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "open file failed." ;
        return -1;
    }else {
        while (!file.atEnd()) {
            qDebug() << "文件内容:";
            qDebug() << file.readLine(); // 尽量一行一行的读。
        }
    }
    file.close(); // 及时的关闭,虽然也可以不写等退出app时候Qt帮忙关闭

    //=======
    // QFileInfo 文件的相关信息 详细看QFileInfo doc
    //=======
    QFileInfo file_infomation(file);
    qDebug() << "file_infomation.fileName(): "<<file_infomation.fileName();
    qDebug() <<"file_infomation.isDir() :" <<  file_infomation.isDir();
    qDebug() << "file_infomation.isExecutable(): " << file_infomation.isExecutable();
    qDebug() << "file_infomation.baseName() :" << file_infomation.baseName();
    qDebug() << "file_infomation.completeBaseName():" << file_infomation.completeBaseName();
    qDebug() << "file_infomation.completeBaseName():"<<file_infomation.suffix();
    qDebug() << "file_infomation.completeBaseName():"<< file_infomation.completeSuffix();

    //    return a.exec();
    return 0;
}

在这段代码中,我们首先使用QFile创建了一个文件对象。这个文件名字是 in.txt。(没有指定文件路径的文件就是在可执行程序的当前目录)
如果你不知道应该把它放在哪里,可以使用QDir::currentPath()来获得应用程序执行时的当前路径。只要将这个文件放在与当前路径一致的目录下即可。
然后,我们使用open()函数打开这个文件,打开形式是只读方式,文本格式。这个类似于fopen()的 r 这样的参数。open()函数返回一个 bool 类型,如果打开失败,我们在控制台输出一段提示然后程序退出。否则,我们利用 while 循环,将每一行读到的内容输出。

程序的第二部分,我们使用QFileInfo获取有关该文件的信息。QFileInfo有很多类型的函数,我们只举出一些例子。比如这里,isDir()检查该文件是否是目录;isExecutable()检查该文件是否是可执行文件等。baseName()可以直接获得文件名;suffix()则直接获取文件后缀名。我们可以由下面的示例看到,baseName()completeBaseName(),以及suffix()completeSuffix()的区别:

QFileInfo fi("/tmp/archive.tar.gz");
QString base  = fi.baseName();  // base = "archive"
QString cbase = fi.completeBaseName();  // base = "archive.tar"
QString ext   = fi.suffix();  // ext = "gz"
QString ext   = fi.completeSuffix();  // ext = "tar.gz"

以上是关于Qt 输入输出设备和文件操作的主要内容,如果未能解决你的问题,请参考以下文章

QT开发(二十四)——QT文件操作

第15章 基于QT5打印机设备开发

QT软件开发: QProcess启动进程完成交互并获取输出

QT可以和python混合编程吗

C 语言文件操作 ( 写文本文件 )

如何使用 Qt 在 Linux 中读取文件设备?