文件系统
文件操作是应用程序必不可少的部分。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 传输数据;
1 基本文件操作
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"
2 二进制文件读写
我们可以直接读写没有编码的二进制数据,例如图像、视频、音频等。 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没有正常关闭 。为性能起见,数据只有在文件关闭时才会真正写入。因此,我们必须在最后添加一行代码 :
file. close ( ) ; // 如果不想关闭文件,可以使用 file.flush();
接下来我们将存储到文件中的答案取出来
QFile file ( "file.dat" ) ;
file. open ( QIODevice:: ReadOnly) ;
QDataStream in ( & file) ;
QString str;
qint32 a;
in >> str >> a;
唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。
QDataStream同QIODevice有什么区别?
区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API 更好一些。
3 文本文件读写
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打开方式。我们可以从下表中看到这些打开方式的区别:
QTextStream与QDataStream注意
虽然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" ) ;
这样的函数进行设置。
Qt 工程案例
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QFile>
#include <QTextCodec>
#include <QFileInfo>
#include <QDebug>
#include <QDateTime>
#include <QTextStream>
Widget:: Widget ( QWidget * parent) :
QWidget ( parent) ,
ui ( new Ui:: Widget)
{
ui- > setupUi ( this ) ;
//点击按钮 选取文件
connect ( ui- > pushButton, & QPushButton:: clicked, [ = ] ( ) {
QString path = QFileDialog:: getOpenFileName ( this , "打开文件" , "C:\\Users\\zhangtao\\Desktop" ) ;
if ( path. isEmpty ( ) )
{
QMessageBox:: warning ( this , "警告" , "打开失败" ) ;
}
else
{
//将路径 放入到lineEdit
ui- > lineEdit- > setText ( path) ;
//读取文件
//QFile默认支持UTF-8格式
//QTextCodec * codec = QTextCodec::codecForName("gbk");
QFile file ( path) ; //参数路径名称
//指定打开方式(只读)
//file.open(QIODevice::ReadOnly);
file. open ( QFileDevice:: ReadOnly) ;
QByteArray array;
array = file. readAll ( ) ;
// while( !file.atEnd())
// {
// array += file.readLine();
// }
//设置到 文本编辑框中
ui- > textEdit- > setText ( array) ;
file. close ( ) ;
//读gbk
// ui->textEdit->setText( codec->toUnicode(array));
//写文件
//重新指定打开方式
// file.open(QFileDevice::Append);
// file.write("哦哦哦哦哦哦");
// file.close();
//通过QFileInfo读取文件信息
QFileInfo info ( path) ;
qDebug ( ) << "路径: " << info. filePath ( ) << " 名称: "
<< info. fileName ( ) << " 文件大小" << info. size ( ) << "后缀名:" << info. suffix ( ) ;
qDebug ( ) << "创建日期:" << info. created ( ) . toString ( "yyyy-MM-dd hh:mm:ss" ) ;
qDebug ( ) << "修改日期:" << info. lastModified ( ) . toString ( "yyyy/MM/dd hh:mm:ss" ) ;
}
} ) ;
//文件流读写文件
//分类:文本流(基础数据类型) 和 数据流 (大型QImage)
//文本流
// QFile file("aaa.txt");
// file.open(QFileDevice::WriteOnly);
// QTextStream stream(&file);
// stream<< QString("hello World") << 123456 ;
// file.close();
// //读取
// file.open(QFileDevice::ReadOnly);
// QString str;
// //stream >>str; //读取空格 结束
// str = stream.readAll();
// qDebug() << str;
//数据流 二进制
QFile file ( "bbb.txt" ) ;
file. open ( QFileDevice:: WriteOnly) ;
QDataStream stream ( & file) ;
stream << QString ( "hello World" ) << 123456 ;
file. close ( ) ;
//读数据
file. open ( QFileDevice:: ReadOnly) ;
QString str;
int num;
stream >> str >> num;
qDebug ( ) << str << num;
}
Widget:: ~ Widget ( )
{
delete ui;
}