输入输出是一个应用程序重要的组成部分,任何一个应用程序如果没有任何的输入也没有任何的输出,都将毫无意义。一个应用程序兼容的输入方式和输入格式越多,它的适用也就越广泛;一个Qt应用程序的输出越严谨越简洁,就越容易与别的系统相兼容。
介绍
QIODevice是一个重要的Qt框架核心类,主要负责与输入/输出设备进行交互,从而实现数据的读取和写入。在Qt中,几乎所有输入/输出操作都依赖于QIODevice。
QIODevice是一个直接继承自QObject的类,其主要作用是为输入输出类提供一个共同的基类,大部分具体的功能其本身并不直接实现,而是提供统一的虚函数接口,将具体实现留给相应的子类。
简单来说,QIODevice的主要作用是抽象了各种类型的输入/输出设备(文件、网络流等),使得开发者可以采用一致的方式进行数据处理,无需关注底层细节。另外,QIODevice还提供了丰富的读写接口以及数据流管理功能,方便开发人员灵活地读入或写出数据。
由于QIODevice 的稳定、灵活、可靠等优点,在Qt中有着非常广泛的应用场景,不管是网络通信的底层实现、文件读写的操作还是硬件设备的管理,都离不开 QIODevice 类。以下是QIODevice的重要作用:
-
抽象I/O设备:使用QIODevice,开发人员可以仅使用一套代码和算法对不同类型的设备进行访问,例如:文件、串口、套接字、标准输入/输出等等,并且是可扩展的。
-
读写任何形式数据:QIODevice 提供高效性能的同时还支持通用格式对数据进行读写操作。比如数字数据,比特位,文本,Json,Xml,图片等等。
-
灵活读写:支持分段读取,异步IO等多种读写模式。
-
高效传输:针对网络连接,QIODevice工具箱保证成千上万数据量的快速可靠的传输。
-
易于集成:与Qt的其他类一样,QIODevice是对外公开的API,使用起来简单方便,而且可以和 Qt 其他的模块无缝集成。
子类及其作用
Qt中继承了QIODevice的类有QAbstractSocket、QBuffer、QFileDevice、QLocalSocket、QNetWorkReplay和QProcess。
继承自QIODevice的各种类都为Qt应用程序提供了灵活而强大的I/O支持,使得开发人员在处理数据方面更加容易和高效,使得Qt程序开发者能够更加方便地进行网络通信、文件数据操作和支持多进程。
-
QAbstractSocket:QAbstractSocket是QTcpSocket和QUdpSocket的基类,并提供了所有套接字通信所需的共性。它是实现网络通信中I/O操作的重要类。
-
QBuffer:在内存中缓冲数据,实现数据读写。
-
QFileDevice:QFileDevice是实现QIODevice子类之一的抽象基类,定义了许多用于文件数据操作的虚函数。其主要作用是为实现Unix式和Windows式文件系统提供统一API。
-
QLocalSocket:实现了对本地套接字(Unix domain socket)进行读写操作;
-
QNetworkReply:QNetworkReply是在Qt网络库中用于处理服务器响应的类,负责从网络或缓存读入响应数据。QNetworkReply被发送给客户端的signal保证你收到响应时的预期结果,同时也支持各种数据格式。
-
QProcess:QProcess类在后台启动进程执行可执行文件,程序可以通过start()函数运行进程。它可以侦听进程发出的readyRead()信号,在标准输出上就绪时从进程读取数据。默认情况下,从错误输出(standardError)读取返回值(即exitCode)。因此,QProcess是实现多进程应用程序的常见方式之一。
常用的接口
QIODevice是Qt中实现I/O操作的基础类,提供了许多接口函数。常用的接口函数主要包括以下几个方面:
1.打开和关闭设备。open(mode)函数用于打开设备并设置读写模式。其中mode参数可以取值为QIODevice::ReadOnly(只读模式)、QIODevice::WriteOnly(只写模式)以及QIODevice::ReadWrite(读写模式)。对应地,close()函数用于关闭设备。
2.设备状态检查。isOpen()函数可以检查设备是否已经打开,isReadable()和isWritable()函数则分别用于检查设备是否可读和可写。
3.读/写数据。read(data, maxSize)函数用于从设备读取数据,最大读取maxSize字节到data缓冲区;write(data, maxSize)函数则是将数据写入设备。
4.设备指针位置和数据大小。pos()函数返回当前设备读写指针的位置,而seek(pos)函数则定位到指定位置。size()函数返回当前设备上的数据大小(字节数),bytesAvailable()函数则返回从设备接收到但未读取的数据数量。
5.判断设备是否在结尾和重置设备状态。atEnd()函数如果设备当前位置是数据结尾,则返回true。reset()函数可用于重置设备状态。
6.等待数据就绪、可写。waitForReadyRead(msecs)和waitForBytesWritten(msecs)函数都会使调用线程等待为msecs毫秒或直到有数据和/或可写数据可用。它们均是异步的,当设备中有足够数据被读完了,或者设备还有足够空间来写数据时,都会触发信号。
总之,QIODevice提供了一套通用的I/O接口,使得用户可以方便地进行文件操作、网络通信和其他各种数据I / O操作。然而,在使用它的所有接口时需要注意,不能随意调用读写函数,这会导致奇怪的Bug。因此,在使用QIODevice时,请始终遵循文件、网络等设理的备处基本原则,以确保程序的正确性和高效性。
头文件
对于这种基础框架的基类,我们还是要关注其所有的接口,为后续子类的学习和使用打好基础。
#ifndef QIODEVICE_H
#define QIODEVICE_H#include <QtCore/qglobal.h>
#ifndef QT_NO_QOBJECT
#include <QtCore/qobject.h>
#else
#include <QtCore/qobjectdefs.h>
#include <QtCore/qscopedpointer.h>
#endif
#include <QtCore/qstring.h>#ifdef open
#error qiodevice.h must be included before any header file that defines open
#endifQT_BEGIN_NAMESPACEclass QByteArray;
class QIODevicePrivate;class Q_CORE_EXPORT QIODevice
#ifndef QT_NO_QOBJECT: public QObject
#endif
{
#ifndef QT_NO_QOBJECTQ_OBJECT
#endif
public:enum OpenModeFlag {NotOpen = 0x0000,ReadOnly = 0x0001,WriteOnly = 0x0002,ReadWrite = ReadOnly | WriteOnly,Append = 0x0004,Truncate = 0x0008,Text = 0x0010,Unbuffered = 0x0020,NewOnly = 0x0040,ExistingOnly = 0x0080};Q_DECLARE_FLAGS(OpenMode, OpenModeFlag)QIODevice();
#ifndef QT_NO_QOBJECTexplicit QIODevice(QObject *parent);
#endifvirtual ~QIODevice();OpenMode openMode() const;void setTextModeEnabled(bool enabled);bool isTextModeEnabled() const;bool isOpen() const;bool isReadable() const;bool isWritable() const;virtual bool isSequential() const;int readChannelCount() const;int writeChannelCount() const;int currentReadChannel() const;void setCurrentReadChannel(int channel);int currentWriteChannel() const;void setCurrentWriteChannel(int channel);virtual bool open(OpenMode mode);virtual void close();// ### Qt 6: pos() and seek() should not be virtual, and// ### seek() should call a virtual seekData() function.virtual qint64 pos() const;virtual qint64 size() const;virtual bool seek(qint64 pos);virtual bool atEnd() const;virtual bool reset();virtual qint64 bytesAvailable() const;virtual qint64 bytesToWrite() const;qint64 read(char *data, qint64 maxlen);QByteArray read(qint64 maxlen);QByteArray readAll();qint64 readLine(char *data, qint64 maxlen);QByteArray readLine(qint64 maxlen = 0);virtual bool canReadLine() const;void startTransaction();void commitTransaction();void rollbackTransaction();bool isTransactionStarted() const;qint64 write(const char *data, qint64 len);qint64 write(const char *data);inline qint64 write(const QByteArray &data){ return write(data.constData(), data.size()); }qint64 peek(char *data, qint64 maxlen);QByteArray peek(qint64 maxlen);qint64 skip(qint64 maxSize);virtual bool waitForReadyRead(int msecs);virtual bool waitForBytesWritten(int msecs);void ungetChar(char c);bool putChar(char c);bool getChar(char *c);QString errorString() const;#ifndef QT_NO_QOBJECT
Q_SIGNALS:void readyRead();void channelReadyRead(int channel);void bytesWritten(qint64 bytes);void channelBytesWritten(int channel, qint64 bytes);void aboutToClose();void readChannelFinished();
#endifprotected:
#ifdef QT_NO_QOBJECTQIODevice(QIODevicePrivate &dd);
#elseQIODevice(QIODevicePrivate &dd, QObject *parent = nullptr);
#endifvirtual qint64 readData(char *data, qint64 maxlen) = 0;virtual qint64 readLineData(char *data, qint64 maxlen);virtual qint64 writeData(const char *data, qint64 len) = 0;void setOpenMode(OpenMode openMode);void setErrorString(const QString &errorString);#ifdef QT_NO_QOBJECTQScopedPointer<QIODevicePrivate> d_ptr;
#endifprivate:Q_DECLARE_PRIVATE(QIODevice)Q_DISABLE_COPY(QIODevice)
};Q_DECLARE_OPERATORS_FOR_FLAGS(QIODevice::OpenMode)#if !defined(QT_NO_DEBUG_STREAM)
class QDebug;
Q_CORE_EXPORT QDebug operator<<(QDebug debug, QIODevice::OpenMode modes);
#endifQT_END_NAMESPACE#endif // QIODEVICE_H