C++Qt开发——文件操作

简介

QT中的IO操作通过统一的接口简化了文件与外部设备的操作方式,QT中文件被当作一种特殊的外部设备,文件操作与外部设备操作相同。IO操作的本质是连续存储空间的数据读写。

1. IO设备的类型

顺序存取设备:只能从头开始顺序读写数据,不能指定数据的读写位置

随机存取设备:可以定位到任意位置进行数据的读写

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

2. QFile

在 Qt 帮助文档里面,如果直接查询 QFile 帮助文档,看不到几个关于文件读写的函数,因为 Qt 将读写操作都封装在基类 QIODevice 里面:

QIODevice 类是对输入输入设备的抽象建模,涉及到读写的文件类 QFile 、网络收发QTcpSocket/QUdpSocket、进程输入输出 QProcess,都是从 QIODevice 类派生的。QIODevice 是非常重要的基类,以后讲到网络收发和进程类时还会再讲,本节主要关注与文件读写相关的接口函数。
QFileDevice 是对文件设备的抽象,其实在 Unix 和 Linux 系统中所有东西都是文件设备,就连硬件设备也抽象成文件设备,比如 /dev/usb0 是代表 USB 设备的文件,QFileDevice 就是描述文件设备的类,QFileDevice 这一层基类的接口函数比较少,可以不 用管的。
QFile 类就是本节的学习重点,随后慢慢讲解。一同从 QFileDevice 派生的还有个 QSaveFile ,这个保存文件类,就为了安全地保存文件 而设计的,因为程序运行时可能有 bug 导致崩溃,如果崩溃时正在写入文件,那么文件被改写了一部分,但又没修改完全,会导致原始文件的损坏。QSaveFile 就是为了解决文件的不安全读写,避免出现半吊子问题,QSaveFile 有两个重要函数:cancelWriting() 函数取消写入操作,commit() 提交所有写入操作,知道 QSaveFile 有这两个函数就差不多了,因为也没其他重要功能。QSaveFile 类不单独讲了,因为就那两个重要函数而已。下面开始学习 QFile 类。

2.1 QFile功能函数

在多数情况下,QFile 都是配合 QTextStream 和 QDataStream 使用,当然也可以使用 QFile 自带的读写函数处理文件。QFile 不仅适合于普通的文件系统,而且对 Qt 程序内嵌的资源文件也是通用的,区别只是内嵌资源文件全是只读的。下面大致分几块 来介绍 QFile 的功能函数:

(1)构造函数和打开函数

QFile 通常在构造函数里指定需要打开的文件名:

QFile(const QString & name)
QFile(QObject * parent)
QFile(const QString & name, QObject * parent)

参数 name 就是需要打开的文件名,注意必须是实际的文件路径,不能是只有文件夹路径。parent 是父对象指针。对于第二个构造函数,没有指定文件名,必须在之后的代码里用如下函数设置文件名:

void QFile::​setFileName(const QString & name)

设置了文件名之后才能打开文件进行读写。获取文件名就用 fileName() 函数,不单独列了。

可以使用多种模式打开文件,比如只读模式、只写模式、读写模式等,最常用的打开函数为:

bool QFile::​open(OpenMode mode)

OpenMode 枚举类型是在基类  QIODevice 定义的,有如下打开模式:
 

OpenMode 枚举常量数值描述
QIODevice::NotOpen0x0000用于表示设备或文件尚未打开。
QIODevice::ReadOnly0x0001按读取模式打开。
QIODevice::WriteOnly0x0002按写入模式打开,注意单独用这个模式会暗含Truncate。
QIODevice::ReadWriteReadOnly | WriteOnly按读取和写入模式打开,既能读也能写。
QIODevice::Append0x0004按追加模式打开,文件以前存在的内容不会被覆盖,新数据从文件末尾开始写入。
QIODevice::Truncate0x0008强制清空文件里以前存的旧数据,新数据从零开始写入。
QIODevice::Text0x0010在读取时,把行尾结束符修改为 '\n'; 在写入时,把行尾结束符修改为本地系统换行风格,比如Windows文本换行是 "\r\n"
QIODevice::Unbuffered0x0020忽略缓冲区,直接读写设备或文件。除非是实时性很强的程序,否则用不到。


文件读取时,常见组合如下面两句:

file.open(QIODevice::ReadOnly);    //以只读方式打开文件
file.open(QIODevice::ReadOnly | QIODevice::Text);    //确定是读取文本文件,并且自动把换行符修改为 '\n'

注意以 QIODevice::Text 模式打开文件时,读写的数据不一定是原始数据,因为 QFile 自动把换行符做了转换,读取得到的缓冲区数据与原始文件是可能不一样的,比如 Windows 文本,换行符是 "\r\n" 两个字符,用 QIODevice::Text 读取时只会看到 '\n' 一个字符;写入时就反过来,代码写入一个 '\n',实际文件就是两个连续字符 "\r\n",所以要注意 QIODevice::Text 模式读写的不一定是原始数据。

对于文件写入时,其常用打开模式如下:

file.open(QIODevice::WriteOnly);    //以只写模式打开,这个模式暗含 Truncate,会清空旧数据
file.open(QIODevice::WriteOnly | QIODevice::Truncate);    //只写模式,清空旧数据
file.open(QIODevice::WriteOnly | QIODevice::Append);     //只写和追加模式,不会清空旧数据


如果文件打开时既要读,又要写,那么建议用如下模式:

file.open(QIODevice::ReadWrite);    //读写模式,旧数据不会清空,可以读出来


文件打开之后,可以用从 QIODevice 继承来的读写函数操作文件,或者用 QFile 对象指针构造 QTextStream 或 QDataStream 来读写文件。

除了上面最常用的打开函数,另外还有两个不太常用的打开函数:

bool QFile::​open(FILE * fh, OpenMode mode, FileHandleFlags handleFlags = DontCloseHandle)
bool QFile::​open(int fd, OpenMode mode, FileHandleFlags handleFlags = DontCloseHandle)

上面第一个不常用打开函数可以打开标准输入流 stdin、标准输出流 stdout、标准错误流 stderr ,或者其他文件句柄(Windows系统中参数里的 fh 句柄必须以二进制模式打开,打开句柄时要带 'b' 模式)。最后的参数 handleFlags 一般就用不关闭的 DontCloseHandle 就可以了,如果希望 QFile 对象析构时自动关闭文件或流,那么可以用 QFileDevice::AutoCloseHandle 。

对于 Windows 平台,如果希望在图形界面程序里面用标准的输入输出和错误流,那么必须在项目文件加一句:

CONFIG += console


上面第二个不常用 open() 函数是以 fd 文件描述符参数,与 C 语言里面的文件读写用到的文件描述符类似,如果 fd 为 0 是标准输入流,为 1 是标准输出流,为 2 是标准错误流。

open() 函数打开正确就返回 true,否则返回 fasle,注意判断该函数的返回值,然后再进行文件读写操作!

(2)读写函数

本小节主要介绍从 QIODevice 继承而来的读写函数,这些函数要在 QIODevice 类帮助文档才能找到。
首先是简单的字节读写函数:

bool QIODevice::​getChar(char * c)

参数指针 c 就是读取的一个字节将要存到的变量指针,程序员需要自己先定义一个 char 变量,把这个变量地址传递给 ​getChar() 函数,如果读取一字节成功就返回 true;如果之前已经到了文件末尾,没有字节可以读了,就返回 false。 ​getChar() 函数有一个逆操作函数:

void QIODevice::​ungetChar(char c)

这个函数就是把之前读取的字节 c (这次是变量,不是指针)放回去,并且当前读取游标减一,还原到读取之前状态。注意,如果 c 字节不等于之前读取的字节数值,那么 ​ungetChar() 函数操作结果无法预知,所 以不要使用 ​ungetChar() 函数修改文件!

写入一个字节到文件中,应该使用函数:

bool QIODevice::​putChar(char c)

这个函数会把字节 c 写入文件,并将文件游标加一。

这里我们专门讲一下文件游标,文件在读写时,共同使用一个唯一的游标(QFile内部有),我们这里随便取个名字叫 pos(Position),这个 pos 在文件刚打开时一般处于文件开头位置:

说明一下,文件的大小是用 size() 函数获取的:

qint64 QFile::​size() const

文件大小使用 qint64 类型变量保存的,也就是说 QFile 最大支持 2^63 - 1 == 9,223,372,036,854,775,807 字节的文件,所以不用操心 QFile 对大文件的支持特性。

QFile 当前游标可以用函数获取:

qint64 QFileDevice::​pos() const


对于按字节读取的函数 ​getChar() ,每调用一次,文件游标就加 1,如果连续调用了 N 次,游标 pos 就会移动到下图所示位置:

之前介绍的 ​getChar()、​putChar() ,如果读写正确都会使 pos 自动加 1,ungetChar() 函数如果操作正确,那么会使 pos 自动减一,这个游标都是 QFile 自动控制,一般不需要手动移动游标。

我们对文件读取到一定程度,就会读到文件末尾位置,到达末尾之后就无法再读数据了,因为游标已经超出范围:

QFile 基类有快捷函数判断文件游标是否已到达文件末尾:

bool QFileDevice::​atEnd() const


对于字节读取函数,文件游标是按一字节移动的,如果要读取大段数据块,那么可以使用下面的函数:

qint64 QIODevice::​read(char * data, qint64 maxSize)

data 通常是程序员手动分配的缓冲区,比如 char *buff =  new char[256];
maxSize 就是最多读取的字节数目,一般是手动分配的缓冲区大小,比如 256。
该函数返回值一般就是正确读取的字节数目,因为如果文件后面如果没有 256 字节,那么有几个字节读几个字节。
如果 read() 函数在读取之前就到了文件末尾或者读取错误,那么返回值是 -1 。对于使用 QIODevice::WriteOnly 只写模式打开的文件,通常文件游标总是指向文件末尾,这时候调用 read() 没意义,所以 read() 返回值就是 -1。

手动分配缓冲区其实是比较麻烦的事情,我们 Qt 原生态的读取函数应该用下面这个:

QByteArray QIODevice::​read(qint64 maxSize)

这里的 read() 函数会把读取的字节数组存到 QByteArray 对象并返回,参数里的 maxSize 就是最多读取的字节数目。返回的 QByteArray 对象里面,可以用 QByteArray 自己的 QByteArray::​size() 函数判断读了多少字节,如果文件后面没字节可读或读取错误,那么 QByteArray 尺寸就是 0 。

QByteArray QIODevice::​readAll()

readAll() 函数看名字就知道,把文件的全部内容直接读取到 QByteArray 对象然后返回。
另外还有两个更实用的读取行的函数:

qint64 QIODevice::​readLine(char * data, qint64 maxSize)
QByteArray QIODevice::​readLine(qint64 maxSize = 0)

第一个 ​readLine() 是程序员手动分配缓冲区,第二个不需要手动分配缓冲区。
readLine()  函数工作特性比较特殊,它是从文件或设备里面读取一行 ASCII 字符,最多读取 maxSize-1 字节,因为最后一个字节预留给字符串结尾NULL字符 。
该函数返回值是真实读取的字节数目,如果读取出错或无数据可读就返回 -1。
​readLine() 总会在实际读取的字符串末尾会自动添加一个字符串终结符 0 。

​readLine() 会一直读取数据直到如下三个条件之一满足:
① 第一个 '\n' 字符读取到缓冲区。
② maxSize - 1 字节数已读取,最后一个字节预留给 0 。
③ 文件或设备读取已经到末尾。
对于第一个终止条件,真实读取到了 '\n' 字符,那么这个换行字符会填充到缓冲区里面;
对于第二个和第三个终止条件,是没有读到 '\n' 字符,那么该函数不会自动添加换行符到末尾。
还有一个特殊情况要注意,readLine() 函数会把 Windows 的文件换行风格 "\r\n" 自动替换改为 '\n' 。

​read() 和 readAll() 、​readLine() 函数都会移动文件游标,具体是看真实读了多少字节。

以上主要是读操作,写操作除了 putChar() ,还有如下三个写函数:

qint64 QIODevice::​write(const char * data, qint64 maxSize)

data 就是缓冲区数据指针,maxSize 是最多写入的字节数。 返回值是真实写入的字节数,因为可能出现磁盘不够的情况。 如果返回值是 -1,那么可能是写入出错或者无写入权限。这个写函数不区分 data 缓冲区里面的 '\0' 字符和普通字符串字符,都一股脑写进去。

qint64 QIODevice::​write(const char * data)

这第二个函数参数没指定缓冲区大小,会将参数里的 data 当作 '\0' 结尾的普通字符串,写入该字符串。这个函数等价于下面这句代码:

QIODevice::write(data, qstrlen(data));

第三个写函数其实更常用:

qint64 QIODevice::​write(const QByteArray & byteArray)

byteArray 里面有多少字节就写入多少,这个也是不区分 '\0' 字符和普通字符串字符,都一股脑写进去。
写操作函数也都会移动文件游标 pos,具体是看实际写入了多少字节。

一般我们都不需要手动控制文件游标 pos,但是如果有特殊情况,需要手动移游标,那么通过下面函数:

bool QFileDevice::​seek(qint64 pos)

​seek 函数如果成功移动游标,那么会返回 true,否则返回 false。最好不要用 seek 函数移动游标到超出文件尺寸的位置,这样会导致无法预料 的结果。

如果希望设置文件尺寸,提前在磁盘上分配空间,可以用如下函数:

bool QFile::​resize(qint64 sz)

参数 sz 就是新的文件大小,如果新大小比旧的大,那么新增空间内容是随机的,需要程序员以后手动填充数据。重置大小成功就返回 true,否则返回 false。

文件打开和读写操作结束之后,就可以关闭文件:

void QFileDevice::​close()

在写操作过程中,如果需要立即把 Qt 内部写缓冲区的数据写入磁盘,可以调用:

bool QFileDevice::​flush()    //这个函数很少用到,文件关闭时自动会执行 flush

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

(3)文件属性和权限等函数

QFile 有部分函数其实与 QFileInfo 类功能差不多,这里大致讲解一下,对于这部分操作,其实更建议用 QFileInfo 或 QDir 类来实现。

bool QFile::​copy(const QString & newName)

把当前文件复制到新文件 newName,复制成功就返回 true,否咋返回 false。
注意如果 newName 新文件之前已经存在,那么 copy() 函数返回 false,它不会覆盖旧文件。
当复制文件时,源文件自动会被关闭,以后使用源文件最好再重新打开。

bool QFile::​exists() const

判断当前文件是否存在。

bool QFile::​link(const QString & linkName)

为当前文件创建一个新的快捷方式 linkName ,创建成功返回 true,创建失败返回 false。​link() 函数也不会覆盖之前已存在的快捷方式。对于 Windows 系统,快捷方式名必须以 .lnk 结尾,否则会出错。

bool QFile::​remove()

删除当前文件,删除之前文件会自动被关闭,然后删除。

bool QFile::​rename(const QString & newName)

把当前文件重命名为新名字 newName,如果成功返回 true,失败返回 false。如果 newName 文件之前已存在,那么重命名会失败,旧文件不会被覆盖。文件重命名之前,该文件也会自动关闭。

QString QFile::​symLinkTarget() const

如果当前文件是快捷方式文件,那么​ symLinkTarget() 返回原始文件的完整路径文件名,否则返回空字符串。
 

Permissions QFile::​permissions() const
bool QFile::​setPermissions(Permissions permissions)

获取和设置文件权限的函数,Permissions 枚举变量与 7.1.3 QFileInfo 类的权限枚举是一样的。

(4)QFile 类静态函数

有好几个静态函数与上面(3)里的函数重名,只是参数通常比上面同名函数多一个,多的参数是源文件名,这里就不列举了。
静态函数里面,有三个与上面内容不重复的:

QString QFile::​decodeName(const QByteArray & localFileName)
QString QFile::​decodeName(const char * localFileName)

这两个文件名解码函数把操作系统本地化的路径文件名转为 Qt 标准的 QString 路径文件名(路径分隔符是 '/')。
当然也有文件名编码函数:

QByteArray QFile::​encodeName(const QString & fileName)

这个函数把 Qt 标准的 QString 文件名编码称为操作系统本地化的路径文件名。
关于 QFile 类的功能函数介绍到这,下面来看看示例。

读文件

QFile file("./student.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
    qDebug() << "文件打开失败";
    return;
}
//file.seek(0);//移动文件指针到开头
while (!file.atEnd())
{
    qDebug() << QString(file.readLine());  //读取文件中一行
    //qDebug() << QString(file.readAll()); //读取文件中所有内容
}

QIODevice::Text标志告诉Qt将windows风格的行终止符("\r\n")转换为c++风格的终止符("\n")。 默认情况下,QFile假设为二进制,即它不会对存储在文件中的字节执行任何转换。

写文件

QFile file("./what.txt");
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
{
    qDebug() << "文件打开失败";
    return;
}
file.write("大家好,我是xxx");

修改.txt文件中内容(读文件与写文件)

QString strAll;
    QStringList strList;
    QFile readFile("C:/Users/Administrator/Desktop/r.txt");//自定义读取文件的路径
    if(readFile.open((QIODevice::ReadOnly|QIODevice::Text)))
    {
        QTextStream stream(&readFile);
        strAll = stream.readAll();//把文件所有信息读出来保存到strAll中
    }
    readFile.close();

    QFile writeFile("C:/Users/Administrator/Desktop/w.txt");//是自定义写文件的路径
    if(writeFile.open(QIODevice::WriteOnly|QIODevice::Text))
    {
        QTextStream stream(&writeFile);
        strList = strAll.split("\n");//以换行符为基准分割文本,保存到strList中
        for(int i=0;i<strList.count();i++)//遍历每一行
        {
            if(strList.at(i).contains("flag"))//flag是要修改的内容
            {
                QString tempStr=strList.at(i);
                tempStr.replace(0,tempStr.length(),"Hello");//把flag替换成Hello
                stream<<tempStr<<'\n';
            }
            //如果没有找到要替换的内容,照常写入
            else
            {
                if(i == strList.count()-1)//如果到了文件最后一行,不用加"\n"
                {
                    stream<<strList.at(i);
                }
                else//否则每一行都加一个"\n"
                {
                    stream<<strList.at(i)<<'\n';
                }
            }
        }
    }
    writeFile.close();

临时文件

QTemporaryFile用于安全地创建唯一的临时文件

void tempFile()
{
    qDebug()<<QDir::tempPath();
    QTemporaryFile tempfile;
    if (!tempfile.open())
    {
        qDebug() << "打开失败"<<tempfile.errorString();
        return;
    }
    QTextStream stream(&tempfile);
    stream << "hello";
    qDebug() << tempfile.fileName();
}

3. 流操作

在上面的读写文件中,我们发现操作还是太过复杂,为了简化文本文件和数据文件的读写操作,QT提供了QTextStream和QDataStream辅助类。

QTextStream: 可将写入的数据全部转换为可读文本,QDataStream: 可将写入的数据根据类型转换为二进制数据。

3.1 QTextStream(文本流)

标准 C++ 有三个常见的输入输出流:iostream 处理命令行输入输出、fstream 处理文件输出输出、sstream 处理内存字符串流的输入输出。通常将文本字符串转换成各种 C++ 变量的过程是输入,把内存中 C++ 变量表示成文本字符串的过程是输出。
Qt 提供了强大的文本流 QTextStream ,同时实现了三种 C++ 输入输出流的功能,并且还有功能增强,支持各种文本字符编码。QTextStream 一般用于操作各种编码格式的文本文件(QFile 对象)或字符串(QString、QByteArray),也可以打开 stdin、stdout 和 stderr 命令行的输入输出,并自动处理本地化编码和 Unicode 编码。

QTextStream可以在QIODevice, QByteArray或QString上操作。 使用QTextStream的流操作符,可以方便地读和写单词,行和数字。 对于生成文本,QTextStream支持字段填充和对齐的格式化选项,以及数字的格式化。

3.1.1 QTextStream 构造函数和普通读写函数

通常情况下 QTextStream 自己不会主动去打开一个文件,所以它的构造函数不接收文件名,而是接收 QFile 基类 QIODevice 的指针,可以用 QFile 对象的指针构造 QTextStream ,但需要注意在构造 QTextStream 之前要调用 QFile 对象的 open() 函数按需要的模式打开文件并检查是否正确打开。

用于读写文件的 QTextStream 构造函数为:

 QTextStream(QIODevice * device)

设备指针 device 需要提前用 open() 函数打开。

对于文件设备,还有个不常用的构造函数:

 QTextStream(FILE * fileHandle, QIODevice::OpenMode openMode = QIODevice::ReadWrite)

fileHandle 是文件句柄,因为 Qt 里面的文件一般都用 QFile 处理,所以几乎不会用这个第二个函数处理文件。第二个构造函数的用途主要是和命令行的 stdin、stdout、stderr 三个文件句柄协作,处理命令行的输入输出。

以上两个构造函数第一个用于文件读写(类似 fstream),第二个一般用于命令行输入输出(类似 iostream),而 QTextStream 第三个用途就是对内存字符串的格式化输入和输出(类似 sstream)。内存字符串输入输出,使用的构造函数为:

QTextStream(QString * string, QIODevice::OpenMode openMode = QIODevice::ReadWrite)
QTextStream(QByteArray * array, QIODevice::OpenMode openMode = QIODevice::ReadWrite)
QTextStream(const QByteArray & array, QIODevice::OpenMode openMode = QIODevice::ReadOnly)

注意后两个构造函数,如果传递 QByteArray 对象给构造函数,那么这个 QByteArray 对象是只读的。
如果传递 QByteArray 指针给构造函数,那么默认是可读可写的。

除了可以在构造函数指定文件设备或内存字符串,还可以在运行时修改:

void QTextStream::​setDevice(QIODevice * device)
void QTextStream::​setString(QString * string, QIODevice::OpenMode openMode = QIODevice::ReadWrite)

目前没有重新设置 QByteArray 的函数,只有修改文件设备和 QString 的函数。

我们这里约定一下概念,无论 QTextStream 构造函数里是文件设备或内存字符串,本节后面都把 QTextStream 对象称为 文本流。
如果我们需要从文本流提取各种 C++ 基本数值类型,如 short、int、long、double ,这些读操作一般用 7.3.2 节的操作子和运算符 <<、>>来实现,写入文本流时也一样。流操作子和运算符之外的读写函数就是本小节说的普通读写函数。
读取一整行文本的函数:

QString QTextStream::​readLine(qint64 maxlen = 0)

QTextStream::​readLine() 读取一行文本的时候,返回字符串末尾剔除了换行符("\n"  或 "\r\n"),而上一节 QIODevice 和 QFile 的 readLine() 函数返回字符串末尾带有一个 "\n" ,要注意区分。
如果要读取文本流中所有内容,可以用快捷函数:

QString QTextStream::​readAll()

如果希望从文本流中读取指定长度的文本,可以用函数:

QString QTextStream::​read(qint64 maxlen)

参数里是最多读取的字符数目,如果文本流里的数据不够,返回的字符串就是剩余的字符数。

在使用 QFile 对象指针构造 QTextStream 的时候,一旦把 QFile 对象指针交给 QTextStream 之后,就不要再调用 QFile 对象自己的读写函数了,因为 QTextStream 使用自己缓存的文件游标,底层 QFile 对象自己又进行读写之后,二者的文件游标很可能不 一致,会造成读写的混乱。既然使用 QTextStream 读写文件,那就只用 QTextStream 的读写函数或输入输出运算符。

QTextStream 的文件游标(其实应该叫文本流游标)可以用函数获取、移动,以及判断是否到达文件末尾:

qint64 QTextStream::​pos() const    //获取游标位置
bool QTextStream::​seek(qint64 pos) //移动游标到 pos
bool QTextStream::​atEnd() const    //是否到达文 本流末尾


对于文本流的写入操作,QTextStream 压根没有 write() 函数,写入操作全部是通过输出运算符 << 和流操作子实现的。
我们下面看看流操作子和 <<、>> 运算符。

3.1.1 QTextStream 流操作子和运算符

绝大多数情况下,QTextStream 都是利用 >> 运算符进行输入,利用 << 运算符进行输出,与这两个运算符配合的有一大堆操作子,这些操作子和 iostream 的操作子很类似,而且名字也几乎一样。流操作子一般用于控制输入输出的格式,大部分流操作子不带参数,Qt 提供额外提供了三个带参数的全局操作子,等会都列出 来。

QTextStream 支持所有 C++ 基本类型变量输入输出,并支持 Qt 自家的 QChar、QString、QByteArray,举例来说:

QTextStream & QTextStream::​operator<<(signed int i)
QTextStream & QTextStream::​operator>>(signed int & i)

第一个是整型变量的输出运算符(或叫插入运算符)函数,第二个是整型变量的输入运算符(或叫提取运算符)函数。
QTextStream 在代码里面进行输入输出,就与标准 C++ 的 cin 和 cout 一样用就可以了。例如:

QString strIn = tr("100  29.88");
QTextStream tsIn(&strIn);
int nNum;
double dblVal;
tsIn>>nNum>>dblVal;//输入
qDebug()<<nNum<<dblVal;//打印

这段代码执行后 nNum 就是数值 100, dblVal 就是浮点数 29.88 。qDebug() 其实就是获取调试输出流的对象,然后打印了两个数值。

QTextStream 里面重载的 << 和 >> 运算符就不一个个列举了,讲一下关于字符串读取的三个:

QTextStream & QTextStream::​operator>>(QString & str)
QTextStream & QTextStream::​operator>>(QByteArray & array)
QTextStream & QTextStream::​operator>>(char * c)

QTextStream 对于字符串的读取,其实只读取一个单词,而不是一整行。比如如果文本流原始为 "Hello world, aha !",如果用上面三个函数从头读取,那么读取的都只是第一个单词 "Hello" ,在源头的文件或内存字符串中,文本流中间都没有字符串终结符 '\0',所以 QTextStream 在输入时按照空白字符切割单词,每个单词算作一个小字符串返回。空白字符包括空格、制表符、换行符等等。

另外当文本流用 >> 读取逐个字符到 QChar 或 char 变量里面时,可以用 

void QTextStream::​skipWhiteSpace()

跳过空白字符,如果不跳过空白字符,那么 QChar 和 char 也会接收到空白字符,如空格、制表符、换行符等等。

对于字符串的输出就没有上面那些问题了,因为输出时可以明确知道参数里的 QString 、QByteArray 、char * 是以 '\0' 结尾的,在 '\0' 前面的字符串都会被写入到输出文本流中。

输出文本流需要注意的是 QTextStream 有较大的输出缓冲区,如果用 stdout 或 stderr 构造 QTextStream 对象打印输出,要常用 flush  或 endl 操作子函数清空缓存,及时显示输出:

tsOut<<flush;
tsOut<<endl;

flush 和 endl 的区别是 endl 会换行,flush 不换行。

流的输入输出运算符用起来比较简单,下面重点看看控制格式的操作子,操作子本质都是函数,比如常用的换行 endl:

QTextStream & endl(QTextStream & stream)


我们这里把 QTextStream 的操作子大致列举并解释一下:
① 数值的进制和符号、小数点
 

操作子描述
bin读写二进制整数,等同于 setIntegerBase(2).
oct读写八进制整数,等同于 setIntegerBase(8).
dec读写十进制整数,等同于 setIntegerBase(10).
hex读写十六进制整数,等同于 setIntegerBase(16).
showbase输出时显示数值进制前缀,比如 "0x" 、"0"、"0b",等同于 setNumberFlags(numberFlags() | ShowBase).
forcesign强制显示正号(正数和0前面),等同于 setNumberFlags(numberFlags() | ForceSign).
forcepoint强制显示整数末尾的小数点,等同于 setNumberFlags(numberFlags() | ForcePoint).
noshowbase不显示进制的前缀,等同于 setNumberFlags(numberFlags() & ~ShowBase).
noforcesign不强制显示正号,等同于 setNumberFlags(numberFlags() & ~ForceSign).
noforcepoint不强制显示小数点,等同于 setNumberFlags(numberFlags() & ~ForcePoint).
uppercasebase数值进制的前缀为大写,如 "0X" 、"0B",等同于 setNumberFlags(numberFlags() | UppercaseBase).
uppercasedigits基数大于10进制的数值里面的字母大写,比如16进制的 "FE00", 等同于setNumberFlags(numberFlags() | UppercaseDigits).
lowercasebase数值进制的前缀小写,如 "0x" 、"0b",等同于 setNumberFlags(numberFlags() & ~UppercaseBase).
lowercasedigits基数大于10进制的数值里面的字母小写,比如16进制的 "fe00", 等同于 setNumberFlags(numberFlags() & ~UppercaseDigits).


② 浮点数格式
 

操作子描述
fixed以定点数显示实数(比如 float、double 类型),比如 1024.1234,等同于 setRealNumberNotation(FixedNotation).
scientific以科学计数法显示实数,比如 1.0241234e3,等同于 setRealNumberNotation(ScientificNotation).
qSetRealNumberPrecision(int precision)这是全局操作子,带一个整型参数,指定实数显示的精度位数,等同于 QTextStream::setRealNumberPrecision(precision).


qSetRealNumberPrecision(int) 会对后续所有实数变量输出都生效,直到被改变为止,实数精度默认是 6 位,如果希望还原就设置精度为 6 。注意使用操作子 qSetRealNumberPrecision(int) 时最好一块明确指定使用 fixed 或者 scientific,这样显示结果才是符合预期的小数位精度。

③ 域宽和对齐方式、填充字符
 

操作子描述
qSetFieldWidth(int width)全局操作子,带一个整型参数,设定变量输出后的显示宽度,不足的用填充字符补齐(默认用空格补),等同于 QTextStream::setFieldWidth(width).
qSetPadChar(QChar ch)全局操作子,带一个字符参数,如果设置的域宽比变量显示需要的宽度更宽,就用这个填充字符填补(默认是空格 ' '),等同于 QTextStream::setPadChar(ch).
left设定域宽比变量显示需要的更宽时,变量显示时左对齐,右边空位补 PadChar, 等同于 setFieldAlignment(AlignLeft).
right设定域宽比变量显示需要的更宽时,变量显示时右对齐,左边空位补 PadChar, 等同于 setFieldAlignment(AlignRight).
center设定域宽比变量显示需要的更宽时,变量显示时居中对齐,两边空位补 PadChar, 等同于 setFieldAlignment(AlignCenter).


注意:qSetFieldWidth(int) 设置域宽之后,如果不重新设置,它就一直生效, 对后续所有变量输出都管用,包括 endl 都会补齐变为 指定宽 度。 如果要还原成不设置域宽的状态,就调用操作子 qSetFieldWidth(0) 。 
qSetPadChar(QChar) 设置填充字符之后也是一直持续生效,直到被重新设置,如果还原成空格,就调用操作子 qSetPadChar(QChar(0x20)) 。

④ 其他操作子
 

操作子描述
endl添加换行符并清空输出缓存,把缓冲数据都写到输出文本流,等同于 stream << '\n' << flush;
flush清空输出缓冲区,把缓冲数据都写到输出文本流,等同于 QTextStream::flush()
reset把之前操作子或格式函数设定的格式全部重置,还原到流对象构建时的默认格式, 等同于 QTextStream::​reset()
ws跳过输入文本流里的空白字符(包括空格、制表符、换行符等),直至遇到非空白字符, 等同于 QTextStream::​skipWhiteSpace()
bom除非确定是写入 Unicode 文本文件否则不要设置,如果要设置,必须在所有写入操作之前调用该操作子, 等同于 setGenerateByteOrderMark(true).


bom(Byte Order Mark)只用于 Unicode 编码的文本文件,如果不是写入 Unicode 的字符编码格式(UTF-8、UTF-16、UTF-32)文本,就不要用 bom,后面的小节会讲到文本流和字符编码格式。
 

3.1.1QTextStream 工作状态

本小节说明一下文本流的工作状态,以输入文本流为例,源头文本为 "Hello world, aha !" ,我们如果执行:

//文本源
QString strSrc = "Hello world, aha !";
QTextStream tsIn( & strSrc );
//变量
QString strTemp;
tsIn>>strTemp;
qDebug()<<strTemp;

这段代码会从文本流提取一个单词赋值给 strTemp ,这样的输入操作当然是对的。但如果强行从非数值字符串提取数值到整型或浮点数类型,那么会发生什么呢?

//文本源
QString strSrc = "Hello world, aha !";
QTextStream tsIn( & strSrc );
//变量
int nNum;
tsIn>>nNum;
qDebug()<<nNum;
qDebug()<<tsIn.status();

strSrc 里面压根没有整型数值对应的字符串,上面代码执行之后 nNum 会被设置成默认的数值  0,而这时候 tsIn 应该报错才对,输入流发生了错误,无法为整型数提供 nNum 提供数值。
文本流是通过状态变化来反映刚才的输入或输出操作有无错误发生,获取当前文本流的状态就是用函数:

Status QTextStream::​status() const

枚举类型 Status 目前共有 4 个不同的枚举值:
 

Status 枚举常量数值描述
QTextStream::Ok0正常操作状态,没出错。
QTextStream::ReadPastEnd1底层设备已到达末尾,比如文件末尾,无数据可读了。
QTextStream::ReadCorruptData2读入了腐化数据,典型的就是文本源内容与目标变量类型不匹配。
QTextStream::WriteFailed3无法向底层设备写入数据,比如文件是只读的。


对于上面示例的代码,文本源没有整型变量对应的字符串,当 tsIn 向 nNum 输入数据时,就会发生 QTextStream::ReadCorruptData 错误,tsIn.status() 返回的数值就是 2 。
在进行文本流的输入输出时,如果出现莫名奇妙的错误,一定要注意检查文本流 QTextStream 的状态,很可能发生内容字符串与变量类型不匹配,或者无法写入底层设备等。

编程时需要仔细考虑用户的输入和程序输出是否异常!

对于错误的输入或输出应该如何处理呢?简单方法就是跳过错误的部分或用消息框提示,然后可以重新设置流的状态:

void QTextStream::​setStatus(Status status) //把文本流设为指定状态
void QTextStream::​resetStatus()            //把状态重置为初始的 Ok 状态

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

示例:

class Student
{
public:
    qint64 number;
    QString name;
    QString grade;
    qreal math;
    qreal chinese;
    qreal english;
    friend QDebug& operator<<(QDebug& out, const Student& other)
    {
        out.noquote() << other.number << other.name << other.grade << other.math << other.chinese << other.english;
        return out;
    }
};
void readStudentInfo()
{
     QFile file("./student.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        return -1;
    }
​
    Student stu;
    QTextStream stream(&file);
    qDebug().noquote() <<stream.readLine();
    while (!stream.atEnd())
    {
        stream >> stu.number >> stu.name >> stu.grade >> stu.math >> stu.chinese >> stu.english;
        qDebug() << stu;
    }
}
void test()
{
    QString res;
    QTextStream textstream(&res);
    textstream << "maye" << QString("冷寂") << "young";   //注意:中文要用Qstirng包起来,否则会乱码
    qDebug() <<res;
}

3.2 QDataStream(串行化数据流)

上节 QTextStream 是针对文本流的处理,QTextStream 中都是人类直接可读的文本形式,但是 QTextStream 有它的局限性,对于 Qt 自己的类对象,QTextStream 仅支持字符相关的 QChar、QString、QByteArray,Qt 还有很多其他类,比如矩形 QRect、像素图 QPixmap、点 QPoint、颜色 QColor 等等,这些都没有规定的字符串形式。
实际程序中会大量用到人类不可读的二进制数据,这些二进制数据的封装和存储、传输,就需要用串行化数据流 QDataStream 实现,对于 Qt 知道的所有数据类型,包括 C++ 基本类型变量和 Qt 自己类对象,都可以使用 QDataStream 自动打包成整体的数据块(将各种类型变量和对象按顺序打包就是串行化,按顺序解包就是反串行化),可以用于网络传输和本地文件读写。

3.2.1 QDataStream 构造函数和普通函数

QDataStream 可以支持对输入输出设备的串行化,也可以支持对 QByteArray 字节数组的输入输出,它的构造函数如下:

QDataStream(QIODevice * d)
QDataStream(QByteArray * a, QIODevice::OpenMode mode)
QDataStream(const QByteArray & a)

第一个构造函数是用于支持 I/O 设备,需要在传递指针给 QDataStream 之前把设备打开,比如提前打开文件,然后把文件对象指针传给 QDataStream。
第二个构造函数一般用于支持 QByteArray 的读写,传递字节数组的指针,并且需要指定读写模式,一般是用 QIODevice::ReadWrite。
第三个构造函数参数为字节数组的常量引用,字节数组是只读的,仅作为输入来使用。
除了构造函数,还有如下设置函数改变当前 QDataStream 对象的设备:

void QDataStream::​setDevice(QIODevice * d)


QDataStream 使用的是 Qt 自定义的数据打包格式,这种数据打包格式与操作系统、硬件是 100% 无关的,用 QDataStream 打包的数据包,无论是在 Intel + Windows 、还是 Sun SPARC + Solaris、ARM + Andorid 等等,都可以通用,不用管 CPU 字节序。
对于所有 C++ 基本类型变量,QDataStream 提供了一系列的 << 和 >>  运算符重载函数,比如整型的:

QDataStream & QDataStream::​operator<<(qint32 i)
QDataStream & QDataStream::​operator>>(qint32 & i)

这些都是 QDataStream 的成员函数,还有其他的比如输入输出 bool、qint64、float、double 等等。

对于 C++ 整型数的读写,需要特别注意 long 类型,在 32 位系统,long 类型是 32 bit 长度,而在 64 位系统,long 是 64 bit 长度,因为这种不确定性,所以整型变量都应该转为 Qt 预定义的类型:qint8、quint8、qint16、quint16、qint32、quint32、qint64、quint64,这样能保证变量的长度是类型名字 里指定的位数,无论在什么操作系统都不会出错。

对于所有 Qt 数据封装类的对象,在 QDataStream 类里面查不到对应的运算符重载函数,不是没有,而是太多。在 Qt 帮助文档 Serializing Qt Data Types 页面可以看到所有可以串行化的类型,Qt 自己类对象的串行化,是通过相关的非成员函数(Related Non-Members),这些非成员函数可以在各个数据封装类自己的文档页面找到,比如 QColor 类的非成员函数:

QDataStream & QDataStream::operator<<(QDataStream & stream, const QColor & color)
QDataStream & QDataStream::operator>>(QDataStream & stream, QColor & color)


对于 QDataStream 流的输入,可以判断当前位置是否到达设备的末尾:

bool QDataStream::​atEnd() const

但是 QDataStream 没有 seek() 函数移动游标,也没有游标获取函数 pos() ,这与文本流 QTextStream、文件类 QFile 有很大的区别。在 QDataStream 流里看不到游标,因为它不是按字节或字符读写的,它用 Qt 自家的打包格式(或叫编码方式),只能按变量或类对象顺 序读写,不能随机读写。
串行化,就如它的名字一样,一条道走到黑,输入输出的顺序固定好之后,就不能进行随机位置读写,只能从头按顺序读或从头按顺序写。

除了 << 和 >> 运算符重载函数,QDataStream 提供了另外几个读写函数,可以用于读写自定义的数据块,首先是一对 writeBytes() 和 readBytes() 函数:

QDataStream & QDataStream::​writeBytes(const char * s, uint len) //变量 len 也会写入数据流
QDataStream & QDataStream::​readBytes(char *& s, uint & l)      //l数值是从数据流里读出来的,就是上面的 len

对于字节写入函数,参数里的 s 是要输出的字节缓冲区,len 是写入数据的长度,这里 s 里面针对纯字节数据,不管里面有没有 '\0' ,都写入 数据流。
对于字节读取函数,s 是指针变量的引用,l 是读取的字节数,s 指针不需要程序员分配空间,由 ​readBytes() 函数自己 new [] 一段缓冲区,然后把缓冲区指针赋值给 s;参数 l 是函数的返回变量,是真实读取到的字节数。函数返回之后 s 指针变量指向的缓冲区需程序员手动 delete [] 。
​writeBytes() 函数在做串行化时,会先写 quint32 类型的数据长度,然后写入真实的缓冲区数据到数据流。​readBytes() 也是先读取字节数组长度,该函数自己 new [] 一片空间,把后面的真实数据读到缓冲区。

writeBytes() 和 ​readBytes() 函数与字符串 读写运算符重载函数有类似的地方:

QDataStream & QDataStream::​operator<<(const char * s)
QDataStream & QDataStream::​operator>>(char *& s)

在做串行化时,都是先写一个 quint32 类型的长度到数据流,然后写真实数据,读的时候就是先读长度,然后根据长度 new []  缓冲区,把数据读到缓冲区。这两对函数的区别就是 writeBytes() 和 ​readBytes() 针对字节数组,不管是不是 '\0',都当作普通字 节读写;<< 和 >> 读写字符串时遇到 '\0'  就截止了,并且 '\0' 不会写入到数据流。

QDataStream 还提供了更裸的读写函数,下面这对读写函数是不把字节数组长度变量写入到数据流的,仅仅写入原始的缓冲区数据:

int QDataStream::​writeRawData(const char * s, int len)
int QDataStream::​readRawData(char * s, int len)

这对函数与之前一对 writeBytes() 和 ​readBytes() 函数区别有两点:第一,不写数据长度变量到数据流,只读写最原始的数据块;第二,​readRawData() 自己不会分配缓冲区,必须由程序员提前分配缓冲区给 s,然后传递给 readRawData() 函数。

QDataStream 与 QTextStream 也有类似的地方,就是流的状态,如果数据流的输入顺序与输出顺序不匹配,那么会出现状态错误,获取流的状态函数为:

Status QDataStream::​status() const

状态枚举类型也有四个:
 

Status 枚举常量数值描述
QDataStream::Ok0正常操作状态,没出错。
QDataStream::ReadPastEnd1底层设备已到达末尾,比如文件末尾,无数据可读了。
QDataStream::ReadCorruptData2读入了腐化数据,典型的就是输入流读取顺序与输出流顺序不一样。
QDataStream::WriteFailed3无法向底层设备写入数据,比如文件是只读的。


在 QDataStream 数据流出现错误状态之后,可以重新设置流的状态:

void QDataStream::​setStatus(Status status) //设置流为参数里执行的状态
void QDataStream::​resetStatus()         //重置为原始的 Ok 状态

QDataStream 流的排除处理就比较麻烦了,虽然可以跳过一定的裸字节数:

int QDataStream::​skipRawData(int len)

这个函数跳过最原始的裸数据 len 长度的字节,返回值是真实跳过的字节数。这个函数可以与 ​readRawData() 配合使用,但是在处理流错误时未必有用,因为跳过指定的字节数目之后,流的数据到哪种类型的变量或对象了,难以确定。QDataStream 读取错误时,最好的做法是直接提示出错,后面的不读取,因为串行化数据流默认是不能随机化读写的。

QDataStream 是使用 Qt 自己独有的串行化数据打包方式,它没有任何的格式操作子,因为输入输出格式全是内置的,但 QDataStream 有自己兼容的 Qt 版本号。随着 Qt 版本的更新,QDataStream 打包数据的方式也会更新,因此 QDataStream 有一大堆兼容的版本号,从最早的 Qt_1_0 到最新的 Qt_5_4,版本号枚举常量的命名规则就是  Qt_ 大版本号_小版本号 。
如果负责输出的程序与负责输入的程序,使用的 Qt 版本是一致的,就不需要设置版本号。但是如果输出和输入程序的 Qt  版本不一致,比如输出程序 使用的是 Qt 4.8.* ,输入程序使用的是 Qt 5.4.* ,那么就应该向旧版本兼容,在做读取变量或对象之前,设置版本号:

void QDataStream::​setVersion(int v)

要兼容 Qt 4.8.* ,那么就把参数设为 QDataStream::Qt_4_8  。

随着 Qt 版本号的更新,C++ 基本类型的变量读写规则是固定的,比如 qint32 就是读写 4 个字节,所以 QDataStream 版本号对 C++ 基本类型变量读写其实没影响。QDataStream 版本号影响的是 Qt 自己类对象的读写规则,比如以后如果新增了一个类 QXXXXData,那么只有新版本的 Qt 中 QDataStream 才能读写这个新类 QXXXXData 对象。

示例:

QByteArray data;
QDataStream stream(&data,QIODevice::ReadWrite);
stream << "image" << 1024 << "ABC";
//"[\x00\x00\x00\x06image\x00]   [\x00\x00\x04\x00]  [\x00\x00\x00\x04" "ABC\x00"] 22
qDebug() << data << data.size();

使用QDataStream串行化数据,如果数据是字符串则会在前面用4个字节表明字符串长度,如果是整数则直接存储。

4. QFileInfo

4.1 简介

QFileInfo类提供与系统无关的文件信息,QFileInfo提供了关于文件的名称和在文件系统中的位置(路径)、它的访问权限以及它是目录还是符号链接等信息。文件的大小和最后修改/读取时间也可用。

4.2 常用方法

bool isDir() const               //.如果该对象指向目录或指向目录的符号链接,则返回true;否则返回false。
bool isFile() const              //.如果该对象指向文件或指向文件的符号链接,则返回true。如果对象指向的不是文件,比如目录,则返回false。
bool isHidden() const            //.如果这是一个“隐藏”文件,则返回true;否则返回false。
bool isSymLink() const           //.如果该对象指向符号链接,则返回true;否则返回false。
QString symLinkTarget() const    //.返回符号链接指向的文件或目录的绝对路径,如果对象不是符号链接,则返回空字符串。
QString suffix() const           //.返回文件的后缀(扩展名)。
QString completeSuffix() const   //.返回文件的完整后缀(扩展名)。 
QString fileName() const         //.返回文件的名称(不包括路径)。
QString baseName() const         //.返回文件的基本名称,不包含路径。
QString filePath() const         //.返回文件名,包括路径(可以是绝对的或相对的)。
QString absoluteFilePath() const //.返回包含文件名的绝对路径。
QString path() const             //.返回文件的路径。这不包括文件名。
qint64 size() const              //.以字节为单位返回文件大小。如果文件不存在或无法获取,则返回0。
QDateTime birthTime() const      //.返回文件创建/生成的日期和时间。
QDateTime lastModified() const   //.返回文件最后修改的日期和本地时间。

示例:

QFileInfo info(file);
QFileInfo info("../QFile-test/what.txt");
qDebug() << info.size();         //文件大小
qDebug() << info.absoluteFilePath(); //文件绝对路径(包括文件名)
qDebug() << info.absolutePath();     //绝对路径(不包括文件名)
qDebug() << info.absoluteDir();      //绝对路径 QDir
qDebug() << info.path();             //文件路径
qDebug() << info.filePath();         //返回文件名,包括路径(可以是绝对路径也可以是相对路径)。  
if(info.is)
if (info.isFile()) //如果是文件
{
    qDebug() << info.fileName(); //带后缀的文件名
    qDebug() << info.baseName(); //不带后缀的文件名
    qDebug() << info.suffix();   //获取文件后缀
}

5. QDir

5.1 简介

QDir类提供对目录结构及其内容的访问。

QDir用于操作路径名、访问有关路径和文件的信息以及操作底层文件系统。它也可以用来访问Qt的资源系统。

Qt使用“/”作为通用目录分隔符,就像在url中使用“/”作为路径分隔符一样。如果您总是使用“/”作为目录分隔符,Qt将转换您的路径以符合底层操作系统。

QDir可以使用相对路径或绝对路径指向文件。绝对路径以目录分隔符开始(在Windows下,可以在前面加上驱动器规范)。相对文件名以目录名或文件名开头,并指定相对于当前目录的路径。

5.2 QDir功能

(1)、目录分隔符统一使用’/’

(2)、能够对目录进行任意操作(创建、删除、重命名)

bool mkdir(const QString &dirName) const
bool mkpath(const QString &dirPath) const
        
bool rmdir(const QString &dirName) const    //删除子目录(必须为空才能删除)
bool rmpath(const QString &dirPath) const   //删除路径(必须为空才能删除)
bool remove(const QString &fileName)        //删除指定文件
bool removeRecursively()                    //删除目录,包括它的所有内容 
​
bool rename(const QString &oldName, const QString &newName) //重命名

(3)、能够获取指定目录中的所有条目(文件和文件夹)

QFileInfoList entryInfoList(const QStringList &nameFilters, QDir::Filters filters = NoFilter, QDir::SortFlags sort = NoSort) const
QFileInfoList entryInfoList(QDir::Filters filters = NoFilter, QDir::SortFlags sort = NoSort) const
QStringList entryList(const QStringList &nameFilters, QDir::Filters filters = NoFilter, QDir::SortFlags sort = NoSort) const
QStringList entryList(QDir::Filters filters = NoFilter, QDir::SortFlags sort = NoSort) const

(4)、获取常用路径

//返回应用程序当前目录的绝对路径
QDir current()
QString currentPath()
​
//返回用户主目录  C:/Users/Maye
QDir home()
QString homePath()
 
//返回系统的临时目录。   
QDir temp()
QString tempPath()
    
//返回根目录列表 C:/  D:/ ...
QFileInfoList drives()

(5)、计算目录的大小

quint32 dirSize(const QString& dirName)
{
    QDir dir(dirName);
    if (!dir.exists())
        return ~0;
    quint32 size = 0;
    for (QFileInfo& info : dir.entryInfoList(QDir::Filter::NoDotAndDotDot | QDir::Files | QDir::Dirs))
    {
        qDebug() << info.fileName();
        if (info.isFile())
        {
            size += info.size();
        }
        else
        {
            size += dirSize(info.filePath());
        }
    }
    return size;
}

6. QFileSystemWatcher

6.1 简介

QFileSystemWatcher类用于提供监视文件和目录修改的接口。

QFileSystemWatcher通过监控指定路径的列表,监视文件系统中文件和目录的变更。

调用addPath()函数可以监控一个特定的文件或目录。如果需要监控多个路径,可以使用addPaths()。通过使用removePath()和removePaths()函数来移除现有路径。

QFileSystemWatcher检查添加到它的每个路径,已添加到QFileSystemWatcher的文件可以使用的files()函数进行访问,目录则使用directories()函数进行访问。

当一个文件被修改、重命名或从磁盘上删除时,会发出fileChanged()信号。同样,当一个目录或它的内容被修改或删除时,会发射directoryChanged()信号。需要注意:文件一旦被重命名或从硬盘删除,目录一旦从磁盘上删除,QFileSystemWatcher将停止监控。

注:监控文件和目录进行修改的行为会消耗系统资源。这意味着,你的进程同时监控会有文件数量的限制。一些系统限制打开的文件描述符的数量默认为256。也就是说,如果你的进程试使用addPath()和addPaths()函数添加超过256个文件或目录到文件系统将会失败。

6.2 公有函数

  • 构造函数

QFileSystemWatcher(const QStringList &paths, QObject *parent = nullptr)
QFileSystemWatcher(QObject *parent = nullptr)
  • 添加目录或文件

bool  addPath(const QString &path)
QStringList addPaths(const QStringList &paths)
  • 获取在监控的目录或文件

QStringList directories() const
QStringList files() const
  • 从监控中移除目录或文件

bool removePath(const QString &path)
QStringList removePaths(const QStringList &paths)

6.3 信号

  • 目录或文件发生改变

void directoryChanged(const QString &path)
void fileChanged(const QString &path)

示例:

来实现一个文件/目录监控类型。

#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QDir>
#include <QFileInfo>
#include <QMap>
#include <QDebug>
class FileWatcher : public QObject
{
    Q_OBJECT
public:
    FileWatcher():mFileWatcher(new QFileSystemWatcher(this))
    {
        connect(mFileWatcher, &QFileSystemWatcher::directoryChanged, this, &FileWatcher::onDirChanged);
        connect(mFileWatcher, &QFileSystemWatcher::fileChanged, this, &FileWatcher::onFileChanged);
    };
    void addWatcherPath(const QString&path)
    {
        //把每个目录和文件都添加监视
        mFileWatcher->addPath(path);
        //如果监视的是目录,则把目录中的项保存起来
        if(QFileInfo(path).isDir())
            mCurContentsMap.insert(path,QDir(path).entryList(QDir::Filter::NoDotAndDotDot | QDir::Dirs | QDir::Files));
​
        //遍历子项
        QDir dir(path);
        for(QString ph : dir.entryList(QDir::Filter::NoDotAndDotDot | QDir::Dirs | QDir::Files))
        {
            if(QFileInfo(ph).isFile())
            {
                mFileWatcher->addPath(ph);
            }
            else //目录
            {
               addWatcherPath(ph);
            }
        }
    }
​
public slots:
    void onDirChanged(const QString& path)
    {
        qDebug() << path << "dirChanged"<<mFileWatcher->directories();
        //获取目录下所有项列表
        auto curEntryList = QDir(path).entryList(QDir::Filter::NoDotAndDotDot | QDir::Dirs | QDir::Files);
​
        //获取目录下原来项的列表
        auto oldEntryList = mCurContentsMap[path];
​
        //qDebug()<<"curEntryList"<<curEntryList;
        //qDebug()<<"oldEntryList"<<oldEntryList;
​
​
        //把curEntryList和oldEntryList转成集合set
        QSet<QString> curDirSet(curEntryList.begin(),curEntryList.end());
        QSet<QString> oldDirSet(oldEntryList.begin(),oldEntryList.end());
​
​
        //更新设置
        mCurContentsMap[path] = curEntryList;
​
        //判断是否修改了文件:如果新老集合大小相等,说明只是文件名改变了
        if(curDirSet.size() == oldDirSet.size())
        {
            auto modifyDir = curDirSet - oldDirSet;
            if(!modifyDir.isEmpty())
            {
                //对修改的文件进行操作
                qDebug()<<"修改"<<modifyDir;
            }
        }
        //判断是否添加了文件
        else if(curDirSet.size() > oldDirSet.size())
        {
             auto addDir = curDirSet - oldDirSet;
             if(!addDir.isEmpty())
             {
                 //对添加的文件进行操作
                 qDebug()<<"添加"<<addDir;
             }
​
        }
        //判断是否删除了文件
        else if(curDirSet.size() < oldDirSet.size())
        {
             auto delDir = curDirSet - oldDirSet;
             if(!delDir.isEmpty())
             {
                 //对删除的文件进行操作
                 qDebug()<<"删除"<<delDir;
             }
        }
    }
    void onFileChanged(const QString& file)
    {
        qDebug() << file << "fileChanged";
​
        QFileInfo info(file);
        QString strPath = info.absolutePath();
        QString strName = info.fileName();
​
        qDebug() << QString("The file %1 at path %2 is updated").arg(strName).arg(strPath);
    }
private:
    QFileSystemWatcher *mFileWatcher;
    QMap<QString,QStringList> mCurContentsMap;  //每个监控路径对应的列表
};
​
​
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
​
    FileWatcher watcher;
    watcher.addWatcherPath("./");
​
    return a.exec();
}
​
#include"main.moc"

7. QStandardPaths

7.1 简介

QStandardPaths类提供了访问标准路径的方法。 所谓系统标准路径指的是本地文件系统中,用户的特定目录或系统的配置目录。比如在Windows系统中的“我的文档”,“视频”,“图片”等目录位置。

对于一个大型项目,系统的标准目录是保存数据,配置信息的一个非常有用的地方。例如,一个应用程序需要将下载好的文档保存在本地文件系统的某个地方,而它不能假设某个定好的路径是存在于磁盘上的。有的人可能会将这个文档保存在应用程序所在的工作目录中,当然这是个好方法,但有时应用程序并不希望将数据放在工作目录中,这是因为:

  • 这会使得程序工作目录变得复杂,让用户来干预工作目录,这无疑是一件危险的事情,很可能会破坏程序。

  • 有的程序会希望工作目录是只读的,所以禁止其它用户来对其进行修改。

  • 如果某些配置文件是和其它程序是共享的,如若放置这些文件在某个程序的工作目录中,显然是不明智的做法,而最好的做法就是放在这些标准目录中啦。

对于系统标准目录,我们可以认定它是必然存在的(即使不存在,也可自动创建),但是不同的操作系统,可能有不一样的系统标准目录。例如“我的文档”目录位置

  • Windows:C:/Users/$username$/Documents

  • MacOs :~/Documents

  • Linux : ~/Documents

  • Android :/Documents,//Documents

  • IOS :/Documents

如果想要做跨平台的系统,像这些路径,你都得想办法获取,这只是一个我的文档,如果再加上“下载”,“图片”等标准路径,想想是不是都很麻烦。

然而,Qt却给我们提供了非常方便的类来获取这些标准目录路径,它就是马上要学习的QStandardPaths类。所有函数均为静态函数

7.2 静态公有函数

(1) 返回给定位置类型的本地化显示名称,如果找不到相关位置,则返回空QString。

参数列表中StandardLocation type是一个枚举类型下面会解释所有类型

[static] QString displayName(QStandardPaths::StandardLocation type)

(2) 在指定路径中查找名为executableName的可执行文件,如果路径为空则查找系统路径。

[static] QString findExecutable(const QString &executableName, const QStringList &paths = QStringList())

(3) 在type的标准位置中查找名为fileName的文件或目录。

[static] QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = LocateFile)

(4) 根据文件名fileName在type的标准位置查找所有文件或目录。

[static] QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = LocateFile)

(5) 如果testMode为true,这将在QStandardPaths中启用一个特殊的“测试模式”,它将更改可写的位置以指向测试目录。 这将阻止自动测试读取或写入当前用户的配置。

[static] void setTestModeEnabled(bool testMode)

(6) 返回该类型文件所属的所有目录。

[static] QStringList standardLocations(QStandardPaths::StandardLocation type)

(7) 返回写入类型为文件的目录,如果无法确定位置,则返回空字符串。

[static] QString writableLocation(QStandardPaths::StandardLocation type)

7.3 enum QStandardPaths::StandardLocation

枚举常量

描述

QStandardPaths::DesktopLocation

0返回用户的桌面目录。这是一个泛型值。在没有桌面概念的系统上。

QStandardPaths::DocumentsLocation

1返回包含用户文档文件的目录。这是一个泛型值。返回的路径从来不是空的。

QStandardPaths::FontsLocation

2返回包含用户字体的目录。这是一个泛型值。注意,安装字体可能需要额外的、特定于平台的操作。

QStandardPaths::ApplicationsLocation

3返回包含用户应用程序(可执行程序、应用程序包或它们的快捷方式)的目录。这是一个泛型值。注意,安装应用程序可能需要额外的、特定于平台的操作。该目录中的文件、文件夹或快捷方式是特定于平台的。

QStandardPaths::MusicLocation

4返回包含用户音乐或其他音频文件的目录。这是一个泛型值。如果不存在专门用于音乐文件的目录,则返回一个用于存储用户文档的合理后备方案。

QStandardPaths::MoviesLocation

5返回包含用户电影和视频的目录。这是一个泛型值。如果不存在特定于电影文件的目录,则返回用于存储用户文档的合理的备用方案。

QStandardPaths::PicturesLocation

6返回包含用户图片或照片的目录。这是一个泛型值。如果没有特定的目录对于已经存在的图片文件,将返回存储用户文档的合理退步。

QStandardPaths::TempLocation

7返回可以存储临时文件的目录。返回值可能是特定于应用程序的,在此用户的其他应用程序之间共享,甚至在系统范围内共享。返回的路径从来不是空的。

QStandardPaths::HomeLocation

8返回用户的主目录(与QDir::homePath()相同)。在Unix系统上,这等于HOME环境变量。这个值可以是通用的,也可以是特定于应用程序的,但是返回的路径从来不是空的。

QStandardPaths::DataLocation

9返回与AppLocalDataLocation相同的值。此枚举值已弃用。使用AppDataLocation更可取,因为在Windows上,推荐使用漫游路径。

QStandardPaths::CacheLocation

10返回应该写入用户特定的非必要(缓存)数据的目录位置。这是一个特定于应用程序的目录。返回的路径从来不是空的。

QStandardPaths::GenericCacheLocation

15返回应写入跨应用程序共享的特定于用户的非必要(缓存)数据的目录位置。这是一个泛型值。注意,如果系统没有共享缓存的概念,则返回的路径可能为空。

QStandardPaths::GenericDataLocation

11返回可存储跨应用程序共享的持久数据的目录位置。这是一个泛型值。返回的路径从来不是空的。

QStandardPaths::RuntimeLocation

12返回应该写入运行时通信文件的目录位置,如Unix本地套接字。这是一个泛型值。在某些系统上,返回的路径可能为空。

QStandardPaths::ConfigLocation

13返回应该写入用户特定配置文件的目录位置。这可能是一个通用值或特定于应用程序的值,并且返回的路径永远不会为空。

QStandardPaths::DownloadLocation

14返回用户下载文件的目录。这是一个泛型值。如果不存在专门用于下载的目录,则返回用于存储用户文档的合理后备方案。

QStandardPaths::GenericConfigLocation

16返回应该写入多个应用程序之间共享的用户特定配置文件的目录位置。这是一个泛型值,返回的路径从不为空。

QStandardPaths::AppDataLocation

17返回可存储持久应用程序数据的目录位置。这是一个特定于应用程序的目录。要获取存储要与其他应用程序共享的数据的路径,请使用QStandardPaths::GenericDataLocation。返回的路径从来不是空的。在Windows操作系统上,这将返回漫游路径。这个enum值是在Qt 5.4中添加的。

QStandardPaths::AppLocalDataLocation

DataLocation

返回Windows操作系统的本地设置路径。在所有其他平台上,它返回与AppDataLocation相同的值。这个enum值是在Qt 5.4中添加的。

QStandardPaths::AppConfigLocation

18返回应该写入用户特定配置文件的目录位置。这是一个特定于应用程序的目录,返回的路径永远不会为空。这个enum值是在Qt 5.5中添加的。

7.4 使用方法

  • QString displayName(QStandardPaths::StandardLocation type)的使用
qDebug() << "DesktopLocation: " << QStandardPaths::displayName(QStandardPaths::DesktopLocation);
qDebug() << "DocumentsLocation: " << QStandardPaths::displayName(QStandardPaths::DocumentsLocation);
qDebug() << "FontsLocation: " << QStandardPaths::displayName(QStandardPaths::FontsLocation);
qDebug() << "ApplicationsLocation: " << QStandardPaths::displayName(QStandardPaths::ApplicationsLocation);
qDebug() << "MusicLocation: " << QStandardPaths::displayName(QStandardPaths::MusicLocation);
qDebug() << "MoviesLocation: " << QStandardPaths::displayName(QStandardPaths::MoviesLocation);
qDebug() << "PicturesLocation: " << QStandardPaths::displayName(QStandardPaths::PicturesLocation);
qDebug() << "TempLocation: " << QStandardPaths::displayName(QStandardPaths::TempLocation);
qDebug() << "HomeLocation: " << QStandardPaths::displayName(QStandardPaths::HomeLocation);
qDebug() << "DataLocation: " << QStandardPaths::displayName(QStandardPaths::DataLocation);
qDebug() << "CacheLocation: " << QStandardPaths::displayName(QStandardPaths::CacheLocation);
qDebug() << "GenericCacheLocation: " << QStandardPaths::displayName(QStandardPaths::GenericCacheLocation);
qDebug() << "GenericDataLocation: " << QStandardPaths::displayName(QStandardPaths::GenericDataLocation);
qDebug() << "RuntimeLocation: " << QStandardPaths::displayName(QStandardPaths::RuntimeLocation);
qDebug() << "ConfigLocation: " << QStandardPaths::displayName(QStandardPaths::ConfigLocation);
qDebug() << "DownloadLocation: " << QStandardPaths::displayName(QStandardPaths::DownloadLocation);
qDebug() << "GenericConfigLocation: " << QStandardPaths::displayName(QStandardPaths::GenericConfigLocation);
qDebug() << "AppDataLocation: " << QStandardPaths::displayName(QStandardPaths::AppDataLocation);
qDebug() << "AppLocalDataLocation: " << QStandardPaths::displayName(QStandardPaths::AppLocalDataLocation);
qDebug() << "AppConfigLocation: " << QStandardPaths::displayName(QStandardPaths::AppConfigLocation);

  • QString writableLocation(QStandardPaths::StandardLocation type)的使用
qDebug() << QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
qDebug() << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);

7.5 在指定位置查找文件

  • 在指定的路径中查找名为executableName的可执行文件,如果paths为空,则在系统路径中查找。 系统路径指PATH环境变量的值。 如果存在,返回可执行文件的绝对文件路径,如果没有找到,则返回空字符串。
  • QString findExecutable(const QString &executableName, const QStringList &paths = QStringList())的使用
qDebug()<<QStandardPaths::findExecutable("calc.exe");	//只要设置了path环境变量,都可以找到
qDebug()<<QStandardPaths::findExecutable("7z.exe",QStringList()<<"D:\\MySoftWare\\7-Zip");	//如果没有设置path环境变量,可以自己指定多个路径

  • 根据标准目录类型,在该目录中查找名为fileName的文件或目录。 返回找到的文件或目录的完整路径(取决于options)。 如果找不到这样的文件或目录,则返回一个空字符串
  • QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = LocateFile)的使用
    QStringList locateAll(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options = LocateFile)
qDebug()<< QStandardPaths::locate(QStandardPaths::StandardLocation::DownloadLocation,"下拉.png");

8. QSettings

8.1 简介

用户通常希望应用程序在会话中记住它的设置(窗口大小和位置,选项等)。 这些信息通常存储在Windows上的系统注册表中(HKEY_CURRENT_USERSoftware/MySoft ),以及macOS和iOS上的属性列表文件中。 在Unix系统上,在缺乏标准的情况下,许多应用程序(包括KDE应用程序)使用INI文本文件。

QSettings是对这些技术的抽象,使您能够以可移植的方式保存和恢复应用程序设置。 它还支持自定义存储格式。

QSettings的API基于QVariant,因此我们可以保存很多的类型,如QString、QRect和QImage。

如果您所需要的只是一个非持久的基于内存的结构,那么可以考虑使用QMap<QString,QVariant>替代。

8.2 基本用法

创建QSettings对象时,必须传递公司或组织的名称以及应用程序的名称。 例如,如果你的程序名为QQ,而你的公司名为NiuBi,你将构造QSettings对象如下:

QSettings setting("NiuBi","QQ");

QSettings对象既可以在堆栈上创建,也可以在堆上创建(即使用new)。 构造和销毁QSettings对象非常快。

如果你在应用程序的很多地方使用QSettings,你可能想要使用QCoreApplication::setOrganizationName()和QCoreApplication::setApplicationName()指定组织名称和应用程序名称,然后使用默认的QSettings构造函数:

QCoreApplication::setOrganizationName("NiuBi");
QCoreApplication::setOrganizationDomain("NiuBi.com");
QCoreApplication::setApplicationName("QQ");
...
QSettings settings;

QSettings存储设置。 每个设置由一对(key,value)键值对(key为QStirng类型,value为QVariant)组成。 要写入设置,可以使用setValue()。 例如:

setting.setValue("size",QSize(640,480));
qDebug()<< setting.fileName();  //获取配置文件保存位置

如果已经存在具有相同键的设置,则现有值将被新值覆盖。 为了提高效率,更改可能不会立即保存到永久存储中。 (可以调用sync()来立即提交更改。)

你可以使用value()获取设置的值:

QSize size =  setting.value("size",QSize(250,250)).value<QSize>();

如果没有指定名称的设置,QSettings返回一个空的QVariant(无效的)。你可以通过传递第二个参数给value()来指定另一个默认值(这里传了QSize(250,250),如果配置文件中没有size,就返回自己传的这个数据)。

配置文件格式

在windows下,默认为写入注册表,如果想要写入到.ini文件并保存到exe所在目录,该怎么设置呢?

QApplication::setOrganizationName("NiuBi");
QApplication::setApplicationName("QQ");
​
QSettings setting(QApplication::applicationDirPath()+"/qfile.ini" ,QSettings::Format::IniFormat);

8.3 公有函数

常用

  • 设置将key设置为value的值。 如果键已经存在,则覆盖前面的值。

void setValue(const QString &key, const QVariant &value)
  • 返回设置键的值。 如果该设置不存在,则返回defaultValue。

    如果没有指定默认值,则返回一个默认的QVariant。

QVariant QSettings::value(const QString &key, const QVariant &defaultValue = QVariant()) const
  • 将任何未保存的更改写入永久存储,并重新加载与此同时被另一个应用程序更改的任何设置。

    这个函数会定期从QSettings的析构函数和事件循环中自动调用,所以您通常不需要自己调用它。

void QSettings::sync()
  • 返回一个状态码,指示QSettings遇到的第一个错误,如果没有错误发生,则返回QSettings::NoError。

QSettings::Status status() const
  • 删除设置键和键的任何子设置。

void remove(const QString &key)
  • 返回使用该QSettings对象写入的设置存储的路径。

    在Windows上,如果格式为QSettings::NativeFormat,则返回值为系统注册表路径,而不是文件路径。

QString QSettings::fileName() const

分组

  • 向当前组添加前缀。

void beginGroup(const QString &prefix)
  • 将组重置为相应beginGroup()调用之前的值。

void endGroup()
  • 返回当前组。

QString QSettings::group() const
  • 向当前组添加前缀并开始从数组读取。 返回数组的大小。[有案例]

int beginReadArray(const QString &prefix)
  • 向当前组添加前缀,并开始写入大小为size的数组。 如果size为-1(默认值),则根据写入的条目的索引自动确定size。 [有案例]

void beginWriteArray(const QString &prefix, int size = -1)
  • 关闭数组

void endArray()
  • 将当前数组的索引设置为i。调用setValue()、value()、remove()和contains()等函数将对该索引处的数组项进行操作。

void setArrayIndex(int i)

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

  • 2
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一个跨平台的应用开发框架,它提供了丰富的工具和库,用于快速开发应用程序。使用Qt 5.1编写一个窗口调试助手可以实现以下功能: 1. 用户界面设计:使用Qt提供的UI设计器工具创建窗口界面,包括按钮、输入框、文本框等,用于与用户交互。 2. 日志记录:在窗口调试助手中添加一个可写入的文本框,用于输出调试信息。通过调用Qt提供的日志记录功能,可以在程序运行时将关键信息输出到文本框中,方便用户查看和分析。 3. 调试命令执行:在窗口调试助手中添加一个按钮或菜单,用于执行特定的调试命令。例如,用户可以点击按钮来执行一段代码、调试一个函数,或者执行一些特定的调试操作。 4. 变量监视:在窗口调试助手中添加一个区域显示当前的变量状态。当用户执行调试命令时,程序可以捕捉并显示变量的值,以便用户快速了解程序的执行情况。 5. 断点设置:在窗口调试助手中添加一个断点设置功能,用户可以在某一行代码上设置断点。当程序执行到该行代码时,将暂停执行,用户可以通过调试助手查看变量状态、查找问题。 6. 单步执行:在窗口调试助手中添加一个单步执行功能,方便用户逐行调试程序。用户可以通过点击按钮或菜单进行单步执行,观察每个语句的执行结果,以便排查程序中的错误。 总之,使用Qt 5.1编写一个窗口调试助手可以提供方便的调试功能,帮助用户更快速地定位和解决程序中的问题。通过清晰的界面设计和丰富的调试工具,用户可以轻松地进行代码的调试和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值