zoukankan      html  css  js  c++  java
  • QT学习:10 IO类

    --- title: framework-cpp-qt-10-IO类 EntryName: framework-cpp-qt-10-QIODevice date: 2020-04-17 10:24:06 categories: tags: - qt - c/c++ ---

    章节描述:
    QIODevice类是所有输入输出IO类的基础类,为IO类提供了统一的调用接口(因此我们称QIODevice类以及其派生类为IO类)。
    QIODevice 是一个抽象类,所以不能被实例化,但通常会用到它定义的接口,这些接口提供设备无关的I/O特性。例如Qt的XML类通过操作一个QIODevice 的指针,可以使用各种各样的设备(files,buffers等)。

    IO设备类型

    IO类支持随机存储,和顺序储存设备。

    可以使用 isSequential()判断设备是否是顺序设备。

    其中顺序设备不支持pos(),size()方法。

    调用size()函数时,如果是随机设备则返回当前设备的大小,如果是顺序设备则返回bytesAvailable()。在设备关闭的情况下调用size()函数将不会反映设备的实际大小,因此调用该函数前保证设备已打开。

    顺序存取设备:只能从头开始顺序读写数据,不能指定数据的读写位置。顺序设备没有位置概念,也不支持搜索,只能在数据可用时一次性读取所有数据。典型的顺序设备是Socket

    随机存取设备:随机设备就是文件,它们具有大小和当前位置,支持在数据流中向前向后搜索,可以定位到任意位置进行数据的读写。

    QT中IO设备的继承类图:

    QIODevice
    │	随机设备
    ├── QBuffer
    ├── QFileDevice
    │   ├── QFile
    │   │   └── QTemporaryFile
    │   └── QSaveFile
    │
    │	顺序设备
    ├── QAbstractSocket
    │   ├── QTcpSocket
    │   │   ├── QSctpSocket
    │   │   └── QSslSocket
    │   └── QUdpSocket
    └── QProcess
    

    IO类继承于QIODevice类,只需要实现自己的writeData()readData()方法。其他读写方法QIODevice都是调用writeData()readData()实现的。

    操作流程

    在访问设备之前,必须先调用open(),并设置正确的OpenMode(such as ReadOnly or ReadWrite)。你可以用write()putChar()来写入设备。也可以用read(),readLine()来读设备。使用完毕后调用close()

    在访问IO类,必须先调用open()方法打开设备,之后才能调用读写方法对类进行操作。结束操作后需要调用close()方法关闭设备。

    调度 与 同步

    IO类发射readyRead()信号表示有数据可以读取,对应的可以调用bytesAvailable()方法了解可以读取多少字节的数据。
    同理,发射bytesWritten()信号表示数据写入完成,对应的可以调用bytesToWrite()方法了解写入了多少字节的数据。

    IO类的读写函数是非阻塞的,调用方法后不会等待数据读写完成方法,而是立即返回。

    QTcpSocket and QProcess是QIODevice的子类,是异步的,这意味着 I/O 函数 write() or read()的结果总是立即返回,然而,当控件返回到事件循环时,可能会发生与设备本身的通信。

    因此QIODevice提供函数在阻塞调用线程和不输入事件循环的同时,允许程序立即执行,这使得QIODevice的子类可以被使用,在没有循环事件或者是单线程的条件下,提供了waitForReadyRead()waitForBytesWriten()方法实现阻塞(在调用读写方法后调用对应的wait…方法实现阻塞)

    waitFor...():子类会实现相应的函数为了特殊的操作。

    比如QProcess 有个叫waitForStarted()的函数。它将会延迟调用的线程,直到那个process已经启动。

    QProcess gzip;
    gzip.start("gzip", QStringList() << "-c");
    if (!gzip.waitForStarted())
        return false;
     
    gzip.write("uncompressed data");
     
    QByteArray compressed;
    while (gzip.waitForReadyRead())
        compressed += gzip.readAll();
    

    IO类例如QFile,QTcpSocket提供了buffer机制,用于减少底层驱动系统调用,提高访问速度。特别是提高了getChar,putChar方法的速度 。但是在多对象需要读取同一个设备的大批量数据时,buffer会导致内存中存在多个同样的数据副本,内存开销巨大。这个情况,可以 在调用open()方法时设置Unbuffered模式关闭buffer机制。

    常用操作

    open(OpenMode mode):打开设备。mode参数用于设置读写模式,buffer机制,读写机制等。
        
    close():关闭设备
    isOpen():判断设备是否被打开。
        
    isWriteable:判断设备是否支持写入模式。(Open方法设置的)
    isReadable:判断设备是否支持读取。
        
    isSequential():判断设备是否是顺序设备
        
    isTextModeEnable():Text模式getChar方法将忽略’/r’换行符,返回下个字符。
    setTextModeEnable():设置text模式 
    

    打开文件

    bool QIODevice::open(QIODevice::OpenMode mode)
    

    描述:以指定的方式打开一个文件。

    参数解析:

    mode:打开方式

    • QIODevice::NotOpen 不打开
    • QIODevice::ReadOnly 以只读的方式打开.
    • QIODevice::WriteOnly 以只写的方式打开,该模式意味着Truncate,除非与ReadOnly,Append或NewOnly结合使用。
    • QIODevice::ReadWrite 设备以读写的方式打开,写入文件会覆盖之前的内容(打开文件期间多次写入不会覆盖)。
    • QIODevice::Append 以追加模式打开,以便将所有数据写入文件末尾,此模式下不能读文件。
    • QIODevice::Truncate 如果可能,删除文件原有内容。
    • QIODevice::Text 读取时,行尾终止符被转换为' n'。 写入时,行尾终止符将转换为本地编码,例如Win32的“ r n”。(常用于文本文件以行为单位的读取)
    • QIODevice::Unbuffered 无缓冲的形式打开,设备中的任何缓冲都会被跳过。
    • NewOnly:只允许打开一个不存在的文件
    • ExistingOnly:只允许打开一个已经存在的文件

    设置

    调用openMode()函数可以获取当前设备的打开模式。如果在设备打开的情况下改变设备模式,可以调用setOpenMode()函数来更改。

    调用setTextModelEnabled()函数可以设置设备模式为Text,这对于自定义处理终止符非常有帮助。调用isTextmodeEnabled()函数可以确定设备模式是否有Text。

    读取

    读取数据前必须先判断是否有数据可读,有两种方法来确定是否可以读取数据:

    • 调用isReadable()函数;

    • 接收到readyRead()信号。(当有新数据可被读取时会发出该信号)

    通常采用信号的方式,然后调用bytesAvailable()函数来确定可读取数据的大小,通常与顺序设备一起使用,来确定缓冲区中分配的字节数。

    调用read()函数来读取数据,无法读取时返回-1。

    便捷函数有readAll()readLine()/canReadLine()getChar()/ungetChar()

    写入

    调用isWritable()函数可以判断设备是否设置打开模式中包含了WriteOnly标志,这是一个便捷函数。

    写入数据时,一般会先写入缓冲区,然后再从缓冲区写入设备。调用bytesToWrite()函数返回即将写入设备的数据大小,而对于无缓冲的设备则返回0。每次写入设备数据时都会发出bytesWritten()信号。

    写入数据到设备中,调用write()函数:

    qint64 QIODevice::write(const char *data)
    qint64 QIODevice::write(const QByteArray &byteArray)
    qint64 QIODevice::write(const char *data, qint64 maxSize)
    

    便捷函数有putChar()函数。

    读写位置

    对于随机设备来说,每次读写都会导致内部的文件指针偏移;只有随机设备才可以设置/读取读写位置。

    bool QIODevice::seek(qint64 pos) //定位文件指针。
        
    qint64 QIODevice::pos() const // 获取文件指针位置
    

    事务机制

    事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据项的一个程序执行单元(unit)。事务一般由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。

    为什么要事务?

    事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。

    用一个简单例子说明:银行转帐业务,账户A要将自己账户上的1000元转到B账户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束,B因为网络中断而操作失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操走就需要事务,将A账户资金减少和B账户资金增加放到同一个事务里,要么全部执行成功,要么全部撤销,这样就保证了数据的安全性。

    事务的4个特性(ACID):

    1. 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

    2. 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。(实例:转账,两个账户余额相加,值不变。)

    3. 隔离性(isolation):一个事务的执行不能被其他事务所影响。

    4. 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

    一般来说,异步设备的输入流是分段的,数据块可以在任意的时间内到达。要在这种情况下读到完整数据,使用QIODevice的事务机制。

    读取设备中的数据时,我们可以调用startTransaction()函数来在读取操作序列中设置一个恢复点。接下来的代码进行一般的读取操作,然后调用commitTransaction()函数来提交所有的操作。例如:

    in.startTransaction();
     
    QString nextFortune;
    in >> nextFortune;
     
    if (!in.commitTransaction())
     return;
    

    调用rollbackTransaction()函数来回滚事务,这会将来自源的输入流恢复到startTransaction()时的点。

    读写通道

    一些顺序设备支持多通道通信,这些通道代表了具有独立排序传递特性的独立数据流。打开设备后,调用readChannelCount()writeChannelCount()函数来确定通道数。调用setCurrentReadChannel()setCurrentWriteChannel()函数来切换通道。

    此外,QIODevice还提供额外的信号来处理通道的异步通信。

    • 如果通道有新数据可被设备读取时,发出channelReadyRead()信号。

    • 如果通道有数据写入设备时,发出channelBytesWritten()信号。

    • 关闭读取通道禁止输入流时,会发出readChannelFinished()信号。

    错误信息

    当设备发生故障时,我们可以调用setErrorString()函数来给设备设置一个错误信息,任何时候都可以调用errorString()函数来查看当前设备的错误信息。

  • 相关阅读:
    HashMap于Hashtable的区别
    redis分布式锁
    mybatis基本认识
    怎么获取硬件线程数,Future,创建线程
    查看端口号有什么在用
    javaScript 中的字符操作
    获取类里面的所有属性
    给Date赋值
    实现多人聊天
    客户端与服务器端执行报重置问题
  • 原文地址:https://www.cnblogs.com/schips/p/framework-cpp-qt-10-QIODevice.html
Copyright © 2011-2022 走看看