输入输出是一个应用程序重要的组成部分,任何一个应用程序如果没有任何的输入也没有任何的输出,都将毫无意义。一个应用程序兼容的输入方式和输入格式越多,它的适用也就越广泛;一个Qt应用程序的输出越严谨越简洁,就越容易与别的系统相兼容。

介绍

QIODevice是一个重要的Qt框架核心类,主要负责与输入/输出设备进行交互,从而实现数据的读取和写入。在Qt中,几乎所有输入/输出操作都依赖于QIODevice。

QIODevice是一个直接继承自QObject的类,其主要作用是为输入输出类提供一个共同的基类,大部分具体的功能其本身并不直接实现,而是提供统一的虚函数接口,将具体实现留给相应的子类。

简单来说,QIODevice的主要作用是抽象了各种类型的输入/输出设备(文件、网络流等),使得开发者可以采用一致的方式进行数据处理,无需关注底层细节。另外,QIODevice还提供了丰富的读写接口以及数据流管理功能,方便开发人员灵活地读入或写出数据。

由于QIODevice 的稳定、灵活、可靠等优点,在Qt中有着非常广泛的应用场景,不管是网络通信的底层实现、文件读写的操作还是硬件设备的管理,都离不开 QIODevice 类。以下是QIODevice的重要作用:

  1. 抽象I/O设备:使用QIODevice,开发人员可以仅使用一套代码和算法对不同类型的设备进行访问,例如:文件、串口、套接字、标准输入/输出等等,并且是可扩展的。

  2. 读写任何形式数据:QIODevice 提供高效性能的同时还支持通用格式对数据进行读写操作。比如数字数据,比特位,文本,Json,Xml,图片等等。

  3. 灵活读写:支持分段读取,异步IO等多种读写模式。

  4. 高效传输:针对网络连接,QIODevice工具箱保证成千上万数据量的快速可靠的传输。

  5. 易于集成:与Qt的其他类一样,QIODevice是对外公开的API,使用起来简单方便,而且可以和 Qt 其他的模块无缝集成。

子类及其作用

Qt中继承了QIODevice的类有QAbstractSocket、QBuffer、QFileDevice、QLocalSocket、QNetWorkReplay和QProcess。

继承自QIODevice的各种类都为Qt应用程序提供了灵活而强大的I/O支持,使得开发人员在处理数据方面更加容易和高效,使得Qt程序开发者能够更加方便地进行网络通信、文件数据操作和支持多进程。

  1. QAbstractSocket:QAbstractSocket是QTcpSocket和QUdpSocket的基类,并提供了所有套接字通信所需的共性。它是实现网络通信中I/O操作的重要类。

  2. QBuffer:在内存中缓冲数据,实现数据读写。

  3. QFileDevice:QFileDevice是实现QIODevice子类之一的抽象基类,定义了许多用于文件数据操作的虚函数。其主要作用是为实现Unix式和Windows式文件系统提供统一API。

  4. QLocalSocket:实现了对本地套接字(Unix domain socket)进行读写操作;

  5. QNetworkReply:QNetworkReply是在Qt网络库中用于处理服务器响应的类,负责从网络或缓存读入响应数据。QNetworkReply被发送给客户端的signal保证你收到响应时的预期结果,同时也支持各种数据格式。

  6. 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
#endif

QT_BEGIN_NAMESPACE


class QByteArray;
class QIODevicePrivate;

class Q_CORE_EXPORT QIODevice
#ifndef QT_NO_QOBJECT
    : public QObject
#endif
{
#ifndef QT_NO_QOBJECT
    Q_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_QOBJECT
    explicit QIODevice(QObject *parent);
#endif
    virtual ~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();
#endif

protected:
#ifdef QT_NO_QOBJECT
    QIODevice(QIODevicePrivate &dd);
#else
    QIODevice(QIODevicePrivate &dd, QObject *parent = nullptr);
#endif
    virtual 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_QOBJECT
    QScopedPointer<QIODevicePrivate> d_ptr;
#endif

private:
    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);
#endif

QT_END_NAMESPACE

#endif // QIODEVICE_H
06-05 09:23