QIODevice类是Qt中所有输入/输出设备的基本接口类。
QIODevice为支持读取和写入数据块的设备提供了一个通用的实现和一个抽象接口,例如QFile、QBuffer和QTcpSocket。QIODevice是抽象的,不能被实例化,但是使用它定义的接口来提供设备独立的输入输出特性是很常见的。例如,Qt的XML类就相当于一个QIODevice指针操作,允许它们与各种设备(如文件和缓冲区)一起使用。
在访问设备之前,必须调用open()来设置正确的OpenMode(例如ReadOnly或ReadWrite)。然后,您可以使用write()或putChar()来写入设备,并通过调用read()、readLine()或readAll()来读取。当您使用该设备时,请调用close()。
QIODevice区分了两种类型的设备:随机访问设备和顺序设备。
随机访问设备支持通过查找()查找任意位置。文件中的当前位置可以通过调用pos()来获得。QFile和QBuffer是随机访问设备的例子。
顺序设备不支持寻找任意位置。数据必须在一次传递中读取。函数pos()和size()不适用于顺序设备。QTcpSocket和QProcess是顺序设备的示例。
您可以使用 isSequential()来确定设备的类型。
当新数据可用时,QIODevice会发出readyRead(),例如,如果新数据已经到达网络,或者附加的数据被附加到您正在读取的文件中。您可以调用bytesAvailable()来确定当前可供阅读的字节数。在使用诸如QTcpSocket之类的异步设备编程时,使用bytesAvailable()和readyRead()信号是很方便的,在这些设备中,数据的片段可以及时到达。QIODevice每次向设备写入数据时都会发出 bytesWritten()信号。使用bytesToWrite()来确定等待写入的当前数据大小。
QIODevice的某些子类,如QTcpSocket和QProcess,是异步的。这意味着,诸如write()或read()之类的输入/输出函数总是立即返回,而当控件返回到事件循环时,可能已经和设备本身的通信了。QIODevice提供了一些功能,允许您立即执行这些操作,同时阻塞调用线程,而不需要进入事件循环。这使QIODevice子类可以在没有事件循环的情况下使用,或者在单独的线程中使用:
从主GUI线程调用这些函数,可能会导致您的用户界面卡死。例如:
QProcess gzip;
gzip.start("gzip", QStringList() << "-c");
if (!gzip.waitForStarted())
return false;
gzip.write("uncompressed data");
QByteArray compressed;
while (gzip.waitForReadyRead())
compressed += gzip.readAll();
通过子类化QIODevice,您可以为自己的输入/输出设备提供相同的接口。QIODevice的子类只需要实现保护方法readData()和writeData()函数。QIODevice使用这些函数来实现所有的便利函数,比如getChar()、readLine()和write()。QIODevice还为您处理访问控制,如果调用writeData(),那么该设备将以写模式打开。
一些子类,例如QFile和QTcpSocket,是使用一个内存缓冲区来实现数据的中间存储的。这减少了访问调用所需的设备的数量,但是通常是非常慢的。缓冲可以使getChar()和putChar()这样的函数快速运行,因为它们可以在内存缓冲区上操作,而不是直接在设备本身上运行。然而,某些输入/输出操作不能很好地处理缓冲区。例如,如果几个用户打开相同的设备并按字符读取字符,那么当他们打算读取一个单独的块时,他们可能会读取相同的数据。出于这个原因,QIODevice允许您通过将未缓冲的标志传递给open()来绕过任何缓冲。在子类化QIODevice时,请记住,当设备在非缓冲模式下打开时,可以使用任何缓冲区。
通常,来自异步设备的传入数据流是碎片化的,数据块可以在任意时间点到达任意点。要处理数据结构的不完整读取,请使用QIODevice实现的事务机制。有关更多细节,请参阅startTransaction()和相关函数。
一些顺序设备支持通过多个通道进行通信。这些通道表示单独的数据流,这些数据流具有独立的顺序交付的属性。打开设备之后,您可以通过调用readChannelCount()和writeChannelCount()函数来确定通道的数量。要在通道之间切换,请分别调用setCurrentReadChannel()和setCurrentWriteChannel()。QIODevice还提供了额外的信号,以在每个通道的基础上处理异步通信。
以上是文档中QIODevice的解释,内容非常多,还是先看一下它的子类QFile的使用
详细内容还是查看文档吧,这里写几个常用的,忘了可以看一下:
bool QFile::copy(const QString &newName) //文件复制
bool exists() const //文件是否存在
bool link(const QString &linkName) //创建连接(快捷方式)
bool remove() //删除一个文件
bool rename(const QString &newName) //重命名
void setFileName(const QString &name) //设置文件名
//静态成员
bool copy(const QString &fileName, const QString &newName)
bool setPermissions(const QString &fileName, Permissions permissions)
Permissions permissions(const QString &fileName) //分别是查看和设置权限
QFile file("E:/test.txt");
if(!file.open(QFile::ReadOnly))
{
qDebug() << file.errorString();
}
else
{
while(!file.atEnd())
{
qDebug() << file.readLine();
}
}
//或者按数据大小读取
char buf[1024] = {0};
file.read(buf, 1024);
//小文件可以选择readAll
file.close();
查看QTextStream构造函数,其中有一个重载就是可以选择一个IO设备作为输出
QTextStream(QIODevice *device)
读示例
QTextStream in(&file);
while(!in.atEnd())
{
qDebug() << in.readLine();
}
//QString data =in.read(1024);
写示例
QTextStream out(&file);
out << data; //从文本开始追加,使用seek偏移文件读写位置指针
QDataStream类向QIODevice提供二进制数据的序列化。
数据流是一种二进制的编码信息流,它100%独立于主机计算机的操作系统、CPU或字节顺序。例如,由Windows下的PC所编写的数据流可以通过运行Solaris的Sun SPARC来读取。
您还可以使用数据流来读/写原始未编码的二进制数据。如果您想要一个可以“解析”输入流,请参阅QTextStream。
QDataStream类实现了C++基本数据类型的序列化,如char、short、int、char* 等。通过将数据分解成原始单元,可以实现更复杂的数据的序列化。
数据流与QIODevice配合。一个QIODevice代表一个输入/输出介质,它可以读取数据并写入数据。QFile类是一个输入/输出设备的例子。
示例(将二进制数据写入流):
QFile file("file.dat");
file.open(QIODevice::WriteOnly);
QDataStream out(&file); // we will serialize the data into the file
out << QString("the answer is"); // serialize a string
out << (qint32)42; // serialize an integer
写入的数据当然是无法直接查看的乱码。
示例(从流中读取二进制数据):
QFile file("file.dat");
file.open(QIODevice::ReadOnly);
QDataStream in(&file); // read the data serialized from the file
QString str;
qint32 a;
in >> str >> a; // extract "the answer is" and 42
写入到流的每一项都是用一种预定义的二进制格式编写的,这种格式根据类型而变化。支持的Qt类型包括QBrush, QColor, QDateTime, QFont, QPixmap, QString, QVariant和许多其他类型。对于支持数据流的所有Qt类型的完整列表,请参阅序列化Qt数据类型(Serializing Qt Data Types)。
对于整数,最好总是将其转换为Qt整数类型,并将其重新读取到相同的Qt整数类型中。这可以确保数据的准确,以及支持跨平台。
举个例子,一个char字符串被写成一个32位的整数,等于字符串的长度,包括’0’字节,后面是字符串的所有字符,包括’0’字节。读取char字符串时,读取4个字节以创建32位的长度值,然后读取字符字符串的许多字符,包括“0”结束符。
初始的输入/输出设备通常设置在构造函数中,但是可以使用setDevice()进行更改。如果您已经到达了数据的末尾(或者如果没有输入/输出设备),atEnd()将返回true。
stream.setVersion(QDataStream::Qt_4_0);
如果您正在生成一种新的二进制数据格式,例如您的应用程序创建的文档的文件格式,那么您可以使用QDataStream来编写可移植的数据。通常,您将编写一个包含神奇数字(魔术字)和版本号的简短标题,以便为将来的扩展留出空间。例如:
QFile file("file.xxx");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
// Write a header with a "magic number" and a version
out << (quint32)0xA0B0C0D0;
out << (qint32)123;
out.setVersion(QDataStream::Qt_4_0);
// Write the data
out << lots_of_interesting_data;
对应的读取方法:
QFile file("file.xxx");
file.open(QIODevice::ReadOnly);
QDataStream in(&file);
// Read and check the header
quint32 magic;
in >> magic;
if (magic != 0xA0B0C0D0)
return XXX_BAD_FILE_FORMAT;
// Read the version
qint32 version;
in >> version;
if (version < 100)
return XXX_BAD_FILE_TOO_OLD;
if (version > 123)
return XXX_BAD_FILE_TOO_NEW;
if (version <= 110)
in.setVersion(QDataStream::Qt_3_2);
else
in.setVersion(QDataStream::Qt_4_0);
// Read the data
in >> lots_of_interesting_data;
if (version >= 120)
in >> data_new_in_XXX_version_1_2;
in >> other_interesting_data;
在序列化数据时,可以选择要使用的字节顺序。默认设置是大端存储(MSB优先)。将其更改为小端存储会破坏可移植性(除非读者也更改为小端)。除非您有特殊要求,否则我们建议您保留此设置。
读取
您可能希望从数据流直接读/写您自己的原始二进制数据。可以使用readRawData()将数据从流中读取到预先分配的char *中。类似的数据也可以通过writeRawData()写入到流中。请注意,数据的任何编码/解码都必须由您完成。
类似的两个函数是readBytes()和writeBytes()。这些不同于原始同行如下:readBytes()读取一个quint32被读取的数据的长度,那么被读入的字节数预先分配char *;writeBytes()包含写入的数据的长度类型为quint32(第二个参数),紧跟着数据。请注意,任何编码/解码数据(除了长度quint32)必须由您来完成。
Qt容器类也可以序列化为QDataStream存储。这些包括QList、QLinkedList、QVector、QSet、QHash和QMap。流操作符被声明为类的非成员。
QDataStream &operator<<(QDataStream &, const QXxx &);
QDataStream &operator>>(QDataStream &, QXxx &);
例如,以下是QImage的流操作声明:
QDataStream & operator<< (QDataStream& stream, const QImage& image);
QDataStream & operator>> (QDataStream& stream, QImage& image);
只需要查看帮助文档即可知道是否有相应的流操作即可。