Schalal

7 文件系统和文件读写

本章介绍在Qt中如何实现文本文件、二进制文件的读写,以及文件和目录的管理功能。

7.1 文本文件读写

文本文件就是以纯文本格式存储的文件,Qt提供了以下两种方式对文本文件进行读写:

继承关系:QObject <— QIODevice <— AFileDevice <— QFile

//读入文件例子
bool MainWindow::openTextByIoDevice(const QString &aFileName)
{
    //由文本文件的路径构造一个QFile对象
    QFile aFile(aFileName);
    //判断该文件是否存在
    if(!aFile.exists())
    {
        return false;
    }
    //以只读模式和文本模式打开文件,这个函数和下面的readAll()函数都是继承自QIODevice的
    if(!aFile.open(QIODevice::ReadOnly|QIODevice::Text))
    {
        return false;
    }
    //给文本框组件设置文本
    ui->textEditDevice->setPlainText(aFile.readAll());
    //关闭文件
    aFile.close();
    return true;
}
//写入文件例子
bool MainWindow::saveTextByIODevice(const QString &aFileName)
{
    //构造QFile对象
    QFile aFile(aFileName);
    //以只写模式和文本模式打开该文件
    if(!aFile.open(QIODevice::WriteOnly|QIODevice::Text))
    {
        return false;
    }
    QString str=ui->textEditDevice->toPlainText();
    QByteArray strBytes=str.toUtf8();
    //write函数有三个重载,其中一个的形参与QByteArray有关,另外两个的形参与const char*类型有关,
    //而const char*又需要经QByteArray转换,所以这里直接使用QByteArray
    aFile.write(strBytes, strBytes.length());
    //关闭文件
    aFile.close();
    return true;
}

QTextStream can operate on a QIODevice, a QByteArray or a QString. Using QTextStream’s streaming operators, you can conveniently read and write words, lines and numbers. For generating text, QTextStream supports formatting options for field padding and alignment, and formatting of numbers.

//读入文件例子
bool MainWindow::openTextByStream(const QString &aFileName)
{
    //由文本文件的路径构造一个QFile对象
    QFile aFile(aFileName);
    //判断该文件是否存在
    if(!aFile.exists())
    {
        return false;
    }
    //以只读模式和文本模式打开文件,这个函数和下面的readAll()函数都是继承自QIODevice的
    if(!aFile.open(QIODevice::ReadOnly|QIODevice::Text))
    {
        return false;
    }
    //构造QTextStream对象,其构造函数有5个重载,两个与QByteArray有关,一个与QString*有关,
    //一个与QFile*有关,一个与QIODevice*有关
    QTextStream aStream(&aFile);
    //自动检测Unicode,以使汉字正常显示
    aStream.setAutoDetectUnicode(true);
    //给文本框组件设置文本,这里使用了readAll函数,除此之外,还可以使用readLine逐行读取
    ui->textEditDevice->setPlainText(aStream.readAll());
    //关闭文件
    aFile.close();
    return true;
}
//写入文件例子
bool MainWindow::saveTextByStream(const QString &aFileName)
{
    //构造QFile对象
    QFile aFile(aFileName);
    //以只写模式和文本模式打开该文件
    if(!aFile.open(QIODevice::WriteOnly|QIODevice::Text))
    {
        return false;
    }
    QTextStream aStream(&aFile);
    aStream.setAutoDetectUnicode(true);
    QString str=ui->textEditDevice->toPlainText();
    //写入文本流,<<就是流插入运算符
    aStream<<str;
    //关闭文件
    aFile.close();
    return true;
}

Qt uses Unicode to store, draw and manipulate strings. In many situations you may wish to deal with data that uses a different encoding. For example, most Japanese documents are still stored in Shift-JIS or ISO 2022-JP, while Russian users often have their documents in KOI8-R or Windows-1251. Qt provides a set of QTextCodec classes to help with converting non-Unicode formats to and from Unicode. You can also create your own codec classes.

可以使用以下代码进行全局编码设置。

QTextCodec *codec=QTextCode::codeForName("UTF-8");
QTextCodec::setCodeForLocale(codec);

7.2 二进制文件的读写

除了文本文件之外,其他需要按照一定格式定义的读写文件都归为二进制文件。每种二进制文件都有自己的格式定义,写入数据时需要按照一定的顺序写入,读出时也需要按照一定的顺序读出。

Qt使用QFile和QDataStream进行二进制文件的读写。其中QFile负则文件的IO设备接口,即与文件物理交互;QDataStream以数据流的形式读取或写入文件内容。

QDataStream可以保存为两种文件:

A data stream is a binary stream of encoded information which is 100% independent of the host computer’s operating system, CPU or byte order. For example, a data stream that is written by a PC under Windows can be read by a Sun SPARC running Solaris. You can also use a data stream to read/write raw unencoded binary data. If you want a “parsing” input stream, see QTextStream. The QDataStream class implements the serialization of C++’s basic data types, like char, short, int, char *, etc. Serialization of more complex data is accomplished by breaking up the data into primitive units.(即可序列化的数据类型,如QBrush、QColor、QIcon、QImage等都属于此类。)

//示例1,写stm文件
bool MainWindow::saveDataAsStream(QString& aFileName)
{
    QFile aFile(aFileName);
    if(!aFile.open(QIODevice::WriteOnly|QIODevice::Truncate))
        return false;
    QDataStream aStream(&aFile);
    //版本号,读取文件的版本号必须不低于写入文件时文件的版本号
    aStream.setVersion(QDataStream::Qt_5_14);
    //行列数
    qint16 rowCount = theModel->rowCount();
    qint16 colCount = theModel->columnCount();
    aStream<<rowCount;
    aStream<<colCount;
    for(int i=0;i<theModel->columnCount();i++)
    {
        QString str=theModel->horizontalHeaderItem(i)->text();
        aStream<<str;
    }
    for(int i=0;i<rowCount;i++)
    {
        for(int j=0;j<colCount;j++)
        {
            //假设都取字符串
            QStandardItem* aItem = theModel->item(i,j);
            QString depth = aItem->data(Qt::DisplayRole).toString();
            aStream<<depth;
        }
    }
    aFile.close();
    return true;
}
//示例2,读stm文件
bool MainWindow::openDataAsStream(QString& aFileName)
{
    QFile aFile(aFileName);
    if(!aFile.open(QIODevice::ReadOnly))
        return false;
    QDataStream aStream(&aFile);
    aStream.setVersion(QDataStream::Qt_5_14);
    qint16 rowCount, colCount;
    //按写的顺序读文件内容,赋值给变量
    aStream>>rowCount;
    aStream>>colCount;
    this->resetTable(rowCount);//给表格设定行数
    //字段
    QString str;
    for(int i=0;i<colCount;i++)
    {
        aStream>>str;
    }
    //单元格数据
    QString strData;
    for(int i=0;i<rowCount;i++)
    {
        for(int j=0;j<colCount;j++)
        {
            aStream>>strData;
            aItem = theModel->itemFromIndex(theModel->index(i,j));
            aItem->setData(strData);
        }
    }
    aFile.closed();
    return true;
}
//示例3,写dat文件
bool MainWindow::saveBinaryFile(QString& aFileName)
{
    QFile aFile(aFileName);
    if(!aFile.open(QIODevice::WriteOnly))
        return false;
    QDataStream aStream(&aFile);
    //Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,Windows系统是此类
    aStream.setByteOrder(QDataStream::LittleEndian);
    //行列数
    qint16 rowCount = theModel->rowCount();
    qint16 colCount = theModel->columnCount();
    aStream.writeRawData((char*)&rowCount,sizeof(qint16));
    aStream.writeRawData((char*)&colCount,sizeof(qint16));
    //字段,这里之所以不用writeRawData,是因为字符串的长度不固定,使用writeBytes()会先将字符串的长度作为qint32
    //类型的数据写入,再写字符串数据,这样方便文件的读取
    QByteArray btArray;
    QStandardItem *aItem;
    for(int i=0;i<colCount;i++)
    {
        aItem = theModel->horizontalHeaderItem(i);
        QString str = aItem->text();
        btArray = str.toUtf8();
        aStream.writeBytes(btArray,btArray.length());
    }
    //单元格数据
    for(int i=0;i<rawCount;i++)
    {
        for(int j=0;j<colCount;j++)
        {
            aItem=theModel->item(i,j);
            btArray=aItem->data(Qt::DisplayRole).toString().toUtf8();
            aStream.writeBytes(btArray,btArray.length());
        }
    }
    aFile.close();
    return true;
}
//示例4,读dat文件
bool MainWindow::readBinaryFile(QString& aFileName)
{
    QFile aFile(aFileName);
    if(!aFile.open(QIODevice::WriteOnly))
        return false;
    QDataStream aStream(&aFile);
    aStream.setByteOrder(QDataStream::LittleEndian);
    //行列数,readRawData原型为readRawData(char*s,int len);
    //读取len个字节的数据到s指向的存储单元中
    qint16 rowCount, colCount;
    aStream.readRawData((char*)&rowCount,sizeof(qint16));
    aStream.readRawData((char*)&colCount,sizeof(qint16));
    this->resetTable(rowCount);
    //字段,readBytes原型为readBytes(char*&s,unit &len);
    //读取字符串长度到len中,读取字符串到s指向的变量中
    char *buf;
    uint strLength;
    for(int i=0;i<colCount;i++)
    {
        aStream.readBytes(buf,strLength);
        QString str = QString::fromLocal8Bit(buf,strLength);
    }
    //单元格数据
    QStandardItem *aItem;
    QString strData;
    for(int i=0;i<rawCount;i++)
    {
        for(int j=0;j<colCount;j++)
        {
            aStream.readBytes(buf,strLength);
            strData = QString::fromLocal8Bit(buf,strLength);
            aItem = theModel->itemFromIndex(theModel->index(i,j));
            aItem->setData(str,Qt::DisplayRole);
        }
    }
    aFile.close();
    return true;
}

7.3 文件目录操作

概览:

类名 说明
QCoreApplication 用于提取应用程序的路径(applicationFilePath())、程序名(applicationFileName())等信息
QFile 有复制文件(copy(QString& fileName, QString& newName))、删除文件(remove(QString &fileName))等功能
QFileInfo 用于提取文件信息,如路径(path())、文件名(fileName())、后缀(completeSuffix())等
QDir 用于提取目录或文件信息,如获取一个目录下的文件或目录列表(entryList(Filters filters=NoFilter,SortFlags sort=NoSort)),文件重命名(rename(QString& oldName,QString& newName))
QTemporaryDir和QTemporaryFile 用于创建临时目录和临时文件,使用remove()方法删除临时目录及文件
QFileSystemWatcher 文件和目录监听类,监听目录下文件的添加、删除等变化,监听文件修改变化