一、文件系統:
文件操作是應用程序必不可少的部分。Qt 作爲一個通用開發庫,提供了跨平臺的文件操作能力。Qt 通過QIODevice提供了對 I/O 設備的抽象,這些設備具有讀寫字節塊的能力。下面是 I/O 設備的類圖(Qt5):
-
QIODevice:所有 I/O 設備類的父類,提供了字節塊讀寫的通用操作以及基本接口;
-
QFileDevice:Qt5新增加的類,提供了有關文件操作的通用實現。
-
QFlie:訪問本地文件或者嵌入資源;
-
QTemporaryFile:創建和訪問本地文件系統的臨時文件;
-
QBuffer:讀寫QbyteArray, 內存文件;
-
QProcess:運行外部程序,處理進程間通訊;
-
QAbstractSocket:所有套接字類的父類;
-
QTcpSocket:TCP協議網絡數據傳輸;
-
QUdpSocket:傳輸 UDP 報文;
-
QSslSocket:使用 SSL/TLS 傳輸數據;
文件系統分類:
-
順序訪問設備:
是指它們的數據只能訪問一遍:從頭走到尾,從第一個字節開始訪問,直到最後一個字節,中途不能返回去讀取上一個字節,這其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是順序訪問設備。
-
隨機訪問設備:
可以訪問任意位置任意次數,還可以使用 QIODevice::seek() 函數來重新定位文件訪問位置指針,QFile、QTemporaryFile 和 QBuffer 是隨機訪問設備。
二、QFile 讀寫文件:
在 ui 界面上拖兩個按鈕和一個文本編輯控件,如下:
代碼如下:
// 讀文件
void Widget::on_btnReadFile_clicked()
{
// 獲取文件路徑
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
// 根據文件路徑實例化一個文件對象
QFile file(filePath);
// 以只讀方式打開文件
if (file.open(QFile::ReadOnly))
{
// 讀取文件:一次讀取文件中所有內容
// QByteArray buf = file.readAll();
// 讀取文件:一行一行讀取
QByteArray buf;
while (!file.atEnd())
{
buf += file.readLine();
}
// 注意:默認情況下,如果文件中含有中文時,只有 UTF8 格式的文件能正常讀取,
// 其他格式的文件讀取出的中文數據都是亂碼。
// 顯示到 textEdit 上
ui->textEdit->setText(buf);
}
// 關閉文件
file.close();
}
}
// 寫文件
void Widget::on_btnWriteFile_clicked()
{
// 保存文件路徑
QString filePath = QFileDialog::getSaveFileName();
if (!filePath.isEmpty())
{
// 根據文件路徑實例化一個文件對象
QFile file(filePath);
// 以只寫方式打開文件
if (file.open(QFile::WriteOnly))
{
// 獲取 textEdit 中內容;toPlainText 表示獲取 textEdit 中內容的純文本數據
QString text = ui->textEdit->toPlainText();
// 寫入文件:並將文件格式設置爲 utf8
// file.write(text.toUtf8());
// 寫入文件:將數據轉換成標準 C++ 的 char* 格式;
// 生成的文件還是 utf8 格式,即默認寫入的數據格式就是 utf8;
// file.write(text.toStdString().data());
// 寫入文件:文件格式爲本地平臺8位編碼(windows 下默認爲 ANSI)
file.write(text.toLocal8Bit());
}
// 關閉文件
file.close();
}
}
QFileDialog 的使用參考 Qt學習筆記(十二):標準文件對話框。
三、QFileInfo 獲取文件信息:
// 獲取文件路徑
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
QFileInfo info(filePath);
qDebug() << info.absoluteFilePath(); // 獲取包含文件名的絕對路徑
qDebug() << info.absolutePath(); // 獲取不包含文件名的絕對路徑
qDebug() << info.fileName(); // 獲取文件名(不包含路徑)
qDebug() << info.filePath(); // 獲取文件名(包括路徑,絕對的或相對的)
qDebug() << info.path(); // 獲取文件路徑(不包含文件名)
// 獲取文件的創建日期和事件("年-月-日 時:分:秒:毫秒")
qDebug() << info.created().toString("yyyy-MM-dd HH:mm:ss:zzz");
qDebug() << info.exists(); // 判斷文件是否存在
qDebug() << info.suffix(); // 獲取文件後綴
qDebug() << info.isExecutable(); // 是否是可執行文件
qDebug() << info.isDir(); // 是否是目錄
qDebug() << info.isFile(); // 是否是文件
qDebug() << info.isHidden(); // 是否是隱藏文件
}
四、QDataStream 讀寫文件:
QDataStream 類提供二進制數據的序列化,到一個 IO 設備。
數據流是編碼信息的二進制流,它完全獨立於主機的操作系統、CPU 或字節順序。例如,通過 windows 下 PC 編寫的數據流,可以被運行在 Solaris 上的 Sun SPARC 讀取。
還可以使用數據流來讀取/寫入未經編碼的原始二進制數據。如果想要“解析”輸入流,請參閱 QTextStream。
QDataStream 類實現了 c++ 基本數據類型的序列化,如 char、short、int、char * 等。更復雜數據的序列化是通過將數據分解爲基本單元來實現的。
QDataStream 可以用來操作圖片、音頻、視頻等非文本數據。
寫文件:
// 寫文件
void Widget::on_btnWriteFile_clicked()
{
QFile file("file.txt");
// 以只寫方式打開文件
if (file.open(QIODevice::WriteOnly))
{
// 根據文件對象實例化一個數據流,向數據流裏寫數據,就是向文件裏寫數據
QDataStream stream(&file);
// 使用輸出字符寫數據,可以寫字符串,也可以整型,或者其他類型的數據;
// 注意:因爲寫入的是二進制數據,所以即使寫入的是 .txt 文件,文件中也是亂碼;
stream << QString("主要看氣質") << 22;
file.close(); // 關閉文件
}
qDebug() << "寫文件成功";
}
讀文件:
// 讀文件
void Widget::on_btnReadFile_clicked()
{
QFile file("file.txt");
if (!file.exists())
{
qDebug() << "文件不存在";
}
else
{
// 以只讀方式打開文件
if (file.open(QFile::ReadOnly))
{
// 創建數據流對象
QDataStream stream(&file);
QString str;
int a;
// 讀取數據。
// 注意:讀取數據的類型和順序,需要和寫入數據的類型和順序 保持一致.
stream >> str >> a;
// Qt 默認寫入的數據是 utf8 格式的
qDebug() << str.toUtf8().data() << a;
file.close(); // 關閉文件
}
}
}
使用 QDataStream 讀取圖片,並轉換成 base64 字符串:
// 讀文件
void Widget::on_btnReadFile_clicked()
{
// 獲取文件路徑
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
// 創建文件對象
QFile file(filePath);
// 以只讀方式打開文件
if (file.open(QFile::ReadOnly))
{
// 創建數據流
QDataStream stream(&file);
char *buf = new char[1024];
QByteArray array;
// 循環讀取數據
while (!stream.atEnd())
{
// 讀取數據到緩衝區 buf 中,最多讀取 1024 個字節,返回實際讀取的字節數;
int len = stream.readRawData(buf, 1024);
// 將讀取的二進制數據轉換成 字節數組
array.append(QByteArray(buf, len));
}
delete buf;
// 將讀取的數據轉換成 base64 字符串
QString str = QString(array.toBase64());
ui->textEdit->setText(str);
// 不知道爲什麼此處的 qDebug() 方法不能輸出!!!
qDebug() << str;
file.close(); // 關閉文件
}
}
}
讀取結果:
使用 QDataStream 將 base64 字符串轉換成圖片:將上圖 textEdit 中的數據寫入圖片;
// 寫文件
void Widget::on_btnWriteFile_clicked()
{
// 獲取保存文件路徑
QString filePath = QFileDialog::getSaveFileName();
if (!filePath.isEmpty())
{
// 創建文件對象
QFile file(filePath);
// 以只寫方式打開文件
if (file.open(QFile::WriteOnly))
{
// 獲取 textEdit 上的文本信息
QString text = ui->textEdit->toPlainText();
// 將 base64 數據轉換成 字節數組
QByteArray array = QByteArray::fromBase64(text.toUtf8());
// 創建數據流
QDataStream stream(&file);
// 寫入數據:參數1表示緩存數據,參數2表示緩存數據的字節長度;返回實際寫入的字節數。
int len = stream.writeRawData(array.data(), array.length());
qDebug() << QString::number(len);
file.close(); // 關閉文件
}
}
}
上面是使用 QDataStream 讀寫圖片,並和 base64 字符串的互相轉換,比較麻煩;Qt 還有更簡單的方法讀寫圖片數據,並和 base64 字符串互相轉換,如下所示:
讀取圖片數據,並轉換成 base64 字符串:
// 讀文件
void Widget::on_btnReadFile_clicked()
{
// 獲取文件路徑
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
// 聲明字節數組
QByteArray array;
// 聲明緩衝區,指向字節數組(當向緩衝區寫入數據時,就是在向字節數組中寫入數據)
QBuffer buf(&array);
// 將圖片數據保存到緩衝區
QPixmap pixmap(filePath);
pixmap.save(&buf, "jpg");
// 將數據轉換成 base64 字符串
QString str = QString(array.toBase64());
ui->textEdit->setText(str);
qDebug() << str;
}
}
將 base64 字符串數據保存爲圖片:
// 寫文件
void Widget::on_btnWriteFile_clicked()
{
// 獲取保存文件路徑
QString filePath = QFileDialog::getSaveFileName();
if (!filePath.isEmpty())
{
// 獲取 textEdit 上的數據
QString str = ui->textEdit->toPlainText();
// 從給定字節數組加載一個圖片對象
QPixmap pixmap;
pixmap.loadFromData(QByteArray::fromBase64(str.toLocal8Bit()));
// 保存圖片
pixmap.save(filePath);
}
}
五、QTextStream 操作文件:
QTextStream 類爲讀寫文本提供了一個方便的接口。
QTextStream 可以在 QIODevice、QByteArray 或 QString 上操作。使用 QTextStream 的流操作符,可以方便地讀寫單詞、行和數字。對於生成文本,QTextStream 支持字段填充和對齊的格式化選項,以及數字的格式化。
使用 QTextStream 讀取控制檯輸入和寫入控制檯輸出也是常見的。QTextStream 可識別區域設置,並將使用正確的編解碼器自動解碼標準輸入。
寫入文件:
// 寫文件
void Widget::on_btnWriteFile_clicked()
{
// 創建文件對象
QFile file("file.txt");
// 以只寫方式打開文件
if (file.open(QFile::WriteOnly))
{
// 創建文本流對象
QTextStream stream(&file);
// 設置寫入文件的編碼方式(默認情況下是根據平臺默認的編碼)
stream.setCodec("UTF-8");
// 向流中寫數據
stream << QString("主要看氣質") << 250;
// 關閉文件
file.close();
}
}
寫入的文件爲:
可以發現,寫入的兩個數據沒有分隔,而是緊挨在一起的;這種情況下如果使用下面的方法讀取數據,就無法讀出正確的數據:
// 讀文件
void Widget::on_btnReadFile_clicked()
{
// 創建文件對象
QFile file("file.txt");
// 以只讀方式打開文件
if (file.open(QFile::ReadOnly))
{
// 創建文本流對象
QTextStream stream(&file);
// 設置寫入文件的編碼方式(默認情況下是根據平臺默認的編碼)
stream.setCodec("UTF-8");
QString str;
int a;
// 讀取數據
stream >> str >> a;
qDebug() << str.toUtf8().data() << a;
// 關閉文件
file.close();
}
}
輸出結果如下:這是因爲讀取的數據全部給了變量 str,而整型變量 a 沒有接收到數據,只有初始值 0.
六、QBuffer:表示一個內存緩衝區
QBuffer 類爲 QByteArray 提供一個 QIODevice 接口。
QBuffer 允許我們使用 QIODevice 接口訪問 QByteArray。QByteArray 被視爲一個標準的隨機訪問文件。
QBuffer 相當於一個內存文件,也可以向其中讀寫數據:
// 聲明一個內存緩衝區(相當於一個內存文件,也可以讀寫數據)
QBuffer buf;
// 以只寫方式打開內存緩衝區
if (buf.open(QFile::WriteOnly))
{
// 向內存緩衝區寫入數據
buf.write("hello");
buf.write("nihao");
buf.write("how are you");
buf.close(); // 關閉緩衝區
}
// 從緩衝區中讀取數據
// 可以看到,向內存緩衝區中寫的多條數據,是緊挨在一起的
qDebug() << buf.buffer();
QBuffer 可以和 QByteArray 一起使用,用 QByteArray 來存儲 QBuffer 緩衝區中的數據:
// 聲明一個字節數組
QByteArray array;
// 聲明一個內存緩衝區,指向一個字節數組;
// 當向緩衝區中寫入數據時,就是在向字節數組中寫入數據;
QBuffer buf(&array);
// 以只寫方式打開內存緩衝區
if (buf.open(QFile::WriteOnly))
{
// 向內存緩衝區寫入數據
buf.write("hello");
buf.write("nihao");
buf.write("how are you");
buf.close(); // 關閉緩衝區
}
// 從緩衝區中讀取數據
// 可以看到,向內存緩衝區中寫的多條數據,是緊挨在一起的
qDebug() << buf.buffer();
qDebug() << "array:" << array;