天天看點

Qt中的檔案操作

  以普通檔案為例(文本檔案和二進制檔案在這裡統稱為普通檔案),它們是存放在計算機的硬碟上的,在Qt中它被視為一種特殊的IO裝置,操作IO裝置實質上是對一段存儲空間(以塊為機關)的讀寫。

Qt中的檔案操作

  QIODevice是一個抽象類(類内含純虛函數,不可生成對象),為IO裝置提供了共同的實作和接口,支援讀寫QFile、QBuffer和QTCPSocket等以塊為機關的資料。IO操作的常用接口有:

() 打開裝置:
    bool    open(OpenMode mode)

() 讀取資料:
    qint64 read(char * data, qint64 maxSize)
    QByteArray  read(qint64 maxSize)
    QByteArray readAll()
    qint64 readLine(char * data, qint64 maxSize)
    QByteArray readLine(qint64 maxSize = )

() 寫入資料:
    qint64 write(const char * data, qint64 maxSize)
    qint64 write(const char * data)
    qint64 write(const QByteArray &byteArray)

() 關閉裝置
    void close()
           

  QIODevice中區分了兩種類型的裝置:随機讀寫裝置和順序讀寫裝置,對于随機讀取裝置還可以調用seek()函數定位任意位置,調用pos()函數擷取目前位置。QFile類和QBuffer就是随機讀取裝置的例子。

1. QFile檔案類

  QFile是Qt中用于檔案操作的類,生成的對象需要對應到計算機上的某個檔案。建立Qt控制台程式:

#include <QtCore/QCoreApplication>
#include <QFile>
#include <QDebug>
#include <QFileInfo>
#include <QDateTime>
#include <QByteArray>

void write_file(QString file_path)
{
    QFile file(file_path);

    //以隻讀文本的方式打開(預設打開方式是資料檔案)
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        file.write("QFile test\n");
        file.write("2017.09.14");

        file.close();
    }
}

void read_file(QString file_path)
{
    QFile file(file_path);

    //以隻寫文本的方式打開
    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        //讀取檔案全部内容
        QByteArray dat = file.readAll();
        QString str(dat);

        qDebug() << str;

        file.close();
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    write_file("./tmp.txt");
    read_file("./tmp.txt");

    return a.exec();
}
           

  運作:

Qt中的檔案操作

  另外通過類QFileInfo可讀取檔案屬性資訊:

#include <QFileInfo>
#include <QDateTime>
#include <QByteArray>
void file_info(QString file_path)
{
    QFile file(file_path);
    QFileInfo info(file);

    qDebug() << info.exists();
    qDebug() << info.isFile();
    qDebug() << info.isReadable();
    qDebug() << info.isWritable();
    qDebug() << info.created();
    qDebug() << info.lastRead();
    qDebug() << info.lastModified();
    qDebug() << info.path();
    qDebug() << info.fileName();
    qDebug() << info.suffix();
    qDebug() << info.size();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    file_info("./tmp.txt");

    return a.exec();
}
           

  運作:

Qt中的檔案操作

2. QTempporaryFile臨時檔案類

  QTempporaryFile是Qt提供的臨時檔案操作類,它能安全地建立一個系統唯一的臨時檔案夾,當QTempporaryFile對象呗銷毀時,臨時檔案也會随之被删除。臨時檔案的打開操作被QTempporaryFile封裝,方式固定為QIODevice::ReadWrite。臨時檔案主要運用于程序間大批量資料的傳輸或者程序間通訊。

#include <QTemporaryFile>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QTemporaryFile tmp_file;

    if (tmp_file.open())
    {
        tmp_file.write("tmp file test!\n");
        tmp_file.write("2017.09.14\n");

        //一定要close,不然通過cat指令并不可看到檔案的内容
        tmp_file.close();
    }

    QFileInfo info(tmp_file);

    qDebug() << info.path();
    qDebug() << info.fileName();

    return a.exec();
}
           

  運作:

Qt中的檔案操作

3. 文本檔案和資料檔案

  (1) 文本檔案是指檔案中的内容是由文本構成的,文本指的是ASCII碼字元。在計算機看來,檔案的内容本質上都是數字0/1。那為什麼我們用編輯器打開文本檔案顯示的是ASCII的文本字元?這是因為編輯器預設對檔案的編碼都是ASCII碼。文本檔案的好處就是可以輕易被人讀寫。

  (2) 二進制檔案本質上還是數字,隻不過這些數字并非文本字元的編碼,而是真正意義上的資料,常見的.bin檔案,檔案内都是機器碼(0/1),Linux上的elf可執行檔案(elf檔案是除了機器碼外還包含其他額外資訊,如段的加載位址、運作位址、重定位表等)。

  從本質上看,文本檔案和二進制檔案并沒有任何差别,都是一個檔案中存放了數字。差別在于對檔案内容的了解方式,若将檔案上的數字當做數字處理則是二進制檔案,若将檔案上的數字按照某種編碼方式去解碼就成為文本檔案。

  從作業系統層面同樣不區分這兩種檔案。比如,

  利用系統調用或者标準c庫函數的檔案操作函數去實作将結構體變量寫入文本檔案:

typedef struct {
    char name[];
    int age;
    float score;
}stu;

int main(void)
{
    stu stu1 = {
        .name = "tom",
        .age = ,
        .score = ,
    };

    stu ret = {};

    FILE* fp = fopen("./tmp.file", "a+");

    fwrite(&stu1, sizeof(stu1), , fp);

    fseek(fp, , SEEK_SET);

    fread(&ret, sizeof(ret), , fp);    
    printf("ret.name = %s, ret.age = %d, ret.score = %.2f\n", ret.name, ret.age, ret.score);

    fclose(fp);
    return ;
}
           

  編譯運作:

Qt中的檔案操作

  通過file指令知該檔案的類型為data,也就是二進制檔案;

  再程式設計将字元串變量寫入文本檔案:

int main(void)
{
    FILE* fp = fopen("./tmp.file", "a+");

    fwrite("hello\n", , , fp);

    fseek(fp, , SEEK_SET);
    fread(ret, , , fp);

    printf("%s\n", ret);

    fclose(fp);
    return ;
}
           

編譯運作:

Qt中的檔案操作

  通過file指令檢視該檔案的類型為ASCII text。

  然而不管是文本檔案類型還是資料檔案類型,隻要在調用fread()函數時候使用與之對應的資料類型來接收,就可以得到程式員所預知的結果而非亂碼。

4. Qt中的文本流和資料流

  在了解完文本檔案和資料檔案之後,回到Qt。假設現在要通過Qt的檔案操作寫入double類型的變量,利用QFile類的read()/write()函數該如何實作?要寫入的是double類型的變量而非字元串,顯然被寫入的檔案是個二進制檔案。

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QFile file("./tmp.file");

    if (file.open(QIODevice::WriteOnly))
    {
        double dat = ;

        //需要強制轉換為char*類型
        file.write((char*)&dat, sizeof(dat));
        file.write(reinterpret_cast<char*>(&dat), sizeof(dat));
        file.close();
    }

    if (file.open(QIODevice::ReadOnly))
    {
        double dat = ;

        //需要強制轉換為char*類型
        //file.read((char*)(&dat), sizeof(dat));
        file.read(reinterpret_cast<char*>(&dat), sizeof(dat));
        file.close();

        qDebug() << dat;
    }

    return a.exec();
}
           

  QFile的read()/write()函數在寫入非字元串變量需要将非字元串變量強制轉換為char*類型,也就是将變量強制為一個位元組一個位元組的寫。跟前面的講解文本檔案和資料檔案的程式一樣,程式員需要很清楚的寫入的變量的類型,然後讀出的變量的類型需要以相同類型取解析,在Qt實作中需要涉及到了諸多強制類型轉換。于是,Qt提供了兩個普通檔案操作的輔助類,簡化了文本檔案/資料檔案的讀寫。

  (1) QTextStream:寫入的資料全部轉換為可讀文本

  (2) QDataStream: 寫入的資料根據類型轉化為二進制資料

//以文本流的方式讀寫檔案
void text_stream(QString path)
{
    QFile file(path);

    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);

        out << QString("hello") << endl;
        out <<  << '*' <<  << '=' <<  *  << endl;

        file.close();

    }

    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&file);

        while (!in.atEnd())
        {
            QString l = in.readLine();
            qDebug() << l;
        }
        file.close();
    }

}

//以資料流的方式讀寫檔案
void data_stream(QString path)
{
    QFile file(path);

    if (file.open(QIODevice::WriteOnly))
    {
        QDataStream out(&file);

        //設定Qt版本,以使得資料流為版本的資料流
        out.setVersion(QDataStream::Qt_4_7);

        out << QString("hello");
        out << ;

        file.close();
    }

    if (file.open(QIODevice::ReadOnly))
    {
        QDataStream in(&file);
        QString str = "";
        double dat = ;

        //設定Qt版本
        in.setVersion(QDataStream::Qt_4_7);

        in >> str;
        in >> dat;

        file.close();

        qDebug() << str;
        qDebug() << dat;
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    text_stream("./tmp.txt");
    data_stream("./tmp.txt");

    return a.exec();
}
           

  運作:

Qt中的檔案操作

  需要注意,不同的Qt版本的資料流檔案格式可能被不同,當資料流檔案可能在不同版本的Qt程式中傳遞資料時,需要在代碼中設定版本資訊。需要注意,不同的Qt版本的資料流檔案格式可能被不同,當資料流檔案可能在不同版本的Qt程式中傳遞資料時,需要在代碼中設定版本資訊。

5. 緩沖區類QBuffer

  緩沖區是什麼,幹什麼用的?做嵌入式軟體程式員再熟悉不過了。在Qt中定義了緩沖區類QBuffer,并将緩沖區看作是一種特殊的IO裝置。那就簡單了,前面講到的檔案操作函數和檔案流輔助類就可以直接用于操作緩沖區了。

#include <QBuffer>

void write_buffer(QBuffer& buffer)
{
    if( buffer.open(QIODevice::WriteOnly) )
    {
        QDataStream out(&buffer);

        out << QString("QBuffer test");
        out << ;

        buffer.close();
    }
}

void read_buffer(QBuffer& buffer)
{
    if( buffer.open(QIODevice::ReadOnly) )
    {
        QDataStream in(&buffer);       
        QString str = "";
        double i;

        in >> str;
        in >> i;

        qDebug() << str;
        qDebug() << i;       

        buffer.close();
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QByteArray array;
    QBuffer buffer(&array);

    write_buffer(buffer);
    read_buffer(buffer);

    return a.exec();
}
           

  運作:

Qt中的檔案操作

繼續閱讀