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() orread()的结果总是立即返回, 然而, 当控件返回到事件循环时, 可能会发生与设备本身的通信.
因此 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):
原子性(atomicity): 事务是数据库的逻辑工作单位, 而且是必须是原子工作单位, 对于其数据修改, 要么全部执行, 要么全部不执行.
一致性(consistency): 事务在完成时, 必须是所有的数据都保持一致状态. 在相关数据库中, 所有规则都必须应用于事务的修改, 以保持所有数据的完整性.(实例: 转账, 两个账户余额相加, 值不变.)
隔离性(isolation): 一个事务的执行不能被其他事务所影响.
持久性(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()函数来查看当前设备的错误信息.
来源: http://www.bubuko.com/infodetail-3651863.html