---
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()函数来查看当前设备的错误信息。

QT学习:10 IO类的更多相关文章

  1. QT学习之常用类的总结

    QApplication 应用程序类 管理图形用户界面应用程序的控制流和主要设置       QPalate   QLabel 标签类 提供文本或者图像的显示   QPushButton 按钮类 提供 ...

  2. QT学习之QPair类

    #QPair类 QPair是一个用来存储一对对象的容器模板.其有两个值,first和second. QPair() QPair(const T1 & value1, const T2 & ...

  3. Qt 学习之路 2(10):对象模型

    Home / Qt 学习之路 2 / Qt 学习之路 2(10):对象模型 Qt 学习之路 2(10):对象模型  豆子  2012年9月2日  Qt 学习之路 2  45条评论 标准 C++ 对象模 ...

  4. Java程序猿的JavaScript学习笔记(10—— jQuery-在“类”层面扩展)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  5. Qt 学习之路 2(73):Qt 线程相关类

    Home / Qt 学习之路 2 / Qt 学习之路 2(73):Qt 线程相关类 Qt 学习之路 2(73):Qt 线程相关类  豆子  2013年11月26日  Qt 学习之路 2  7条评论 希 ...

  6. QT学习之文件系统读写类

    #QT学习之文件系统读写类 QIODevice QFileDevice QBuffer QProcess 和 QProcessEnvironment QFileDevice QFile QFileIn ...

  7. 系统学习 Java IO (十六)----这么多类,应该用哪个?

    目录:系统学习 Java IO---- 目录,概览 Java IO目的和功能 Java IO 包含 InputStream,OutputStream,Reader 和 Writer 类的许多子类. 原 ...

  8. iOS学习10之OC类和对象

    本次是OC的第一节课,主要是学习和理解类与对象 1.面向对象 1> OOP(Object Oriented Programming)面向对象编程. 面向对象以事物为中心,完成某件事情都需要哪些事 ...

  9. Qt 学习之路 :Qt 线程相关类

    希望上一章有关事件循环的内容还没有把你绕晕.本章将重新回到有关线程的相关内容上面来.在前面的章节我们了解了有关QThread类的简单使用.不过,Qt 提供的有关线程的类可不那么简单,否则的话我们也没必 ...

  10. QT学习之第一个程序

    QT学习之第一个程序 目录 手动创建主窗口 居中显示 添加窗口图标 显示提示文本 Message Box的应用 手动连接信号与槽 手动创建主窗口 窗口类型 QMainWindow: 可以包含菜单栏.工 ...

随机推荐

  1. python生成随机汉字

    python 随机生成汉字 第一种方法:Unicode码 在unicode码中,汉字的范围是(0x4E00, 9FBF) 这个方法比较简单,但是有个小问题,unicode码中收录了2万多个汉字,包含很 ...

  2. Solution Set - DP

    CF101E Candies and Stones Link&Submission. DP 的状态设计和转移都是显然的,唯一的问题在于需要输出方案,而这题卡空间.会发现如果用 bitset 存 ...

  3. vim 使用clang-format 格化C/C++/Java/JavaScript

    vim 使用clang-format 格化C/C++/Java/JavaScript 参考信息 官方参考https://clang.llvm.org/docs/ClangFormat.html 安装 ...

  4. 【GUI软件】小红书指定博主批量采集笔记,支持多博主同时采集!

    目录 一.背景介绍 1.1 爬取目标 1.2 演示视频 1.3 软件说明 二.代码讲解 2.1 爬虫采集模块 2.2 软件界面模块 2.3 日志模块 三.获取源码及软件 一.背景介绍 1.1 爬取目标 ...

  5. java netty 实现 websocket 服务端和客户端双向通信 实现心跳和断线重连

    java netty 实现 websocket 服务端和客户端双向通信 实现心跳和断线重连 maven依赖 <dependency> <groupId>io.netty< ...

  6. salesforce零基础学习(一百三十七)零碎知识点小总结(九)

    本篇参考: https://help.salesforce.com/s/articleView?id=release-notes.rn_lab_conditional_visibiliy_tab.ht ...

  7. C# dynamic动态对象赋值

    dynamic 如果接收的是匿名对象,是无法为属性赋值的,而如果是接收的定义对象,又无法扩展字段. 解决办法序列化为json字符串,然后用Dictionary反序列化,就能赋值了.也能扩展新的字段. ...

  8. 【U8】 生产订单下bom 提示 “遇到以0做除数错误”错误

    一个虚拟件子件的子件为无换算率存货,bom中对应的换算率.辅助基本用量为0,修改为null后正常. 对应 bom_opcomponent表的 ChangeRate 换算率 AuxBaseQtyN 辅助 ...

  9. iOS11 ReplayKit2 问题总结

    一.苹果自6月30日发布iOS11系统之后,其中的Airplay的协议发生变更,导致市场上的苹果直播助手(录屏)大部分变得不可用,因此在iOS11之后需要寻找新的技术方案来录屏 1)采用系统提供的Re ...

  10. 7.28考试总结(NOIP模拟26)[神炎皇·降雷皇·幻魔皇]

    或许只需一滴露水,便能守护这绽放的花朵. 前言 疯狂挂分,本来T2是想用树状数组优化一下的不知道为啥后来看了一下就少看了一层循环, 然后就想,我都 n 的复杂度了,足以搞过第一问了,还优化啥呀.... ...