QT學習:10 IO類
章節描述:
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()
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()
函式來檢視當前裝置的錯誤資訊。