当前位置:嗨网首页>书籍在线阅读

09-Qt预定义编码文件的读写

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

7.2.2 Qt预定义编码文件的读写

1.保存为stm文件

先看文件保存功能,因为从文件保存功能的代码可以看出文件内数据的存储顺序。在图7-2的窗口上编辑表格的数据后,单击工具栏上的“保存stm文件”,可以使用Qt预定义编码方式保存文件。此按钮的响应代码如下:

void MainWindow::on_actSave_triggered()
{ //以Qt预定义编码保存文件
   QString curPath=QDir::currentPath();
   QString aFileName=QFileDialog::getSaveFileName(this,"选择保存文件", 
             curPath,"Qt预定义编码数据文件(*.stm)");
   if (aFileName.isEmpty())
      return; 
   if  (saveDataAsStream(aFileName))  
      QMessageBox::information(this,"提示消息","文件已经成功保存!");
}
bool MainWindow::saveDataAsStream(QString &aFileName)
{//将模型数据保存为Qt预定义编码的数据文件
   QFile aFile(aFileName); 
   if (!(aFile.open(QIODevice::WriteOnly | QIODevice::Truncate)))
      return false;
   QDataStream aStream(&aFile); 
   aStream.setVersion(QDataStream::Qt_5_9); //流版本号,写入和读取版本要兼容
   qint16  rowCount=theModel->rowCount();
   qint16  colCount=theModel->columnCount();
   aStream<columnCount();i++)
   {
      QString str=theModel->horizontalHeaderItem(i)->text();//获取表头文字
      aStream<rowCount();i++)
   {
      QStandardItem* aItem=theModel->item(i,0); //测深
      qint16 ceShen=aItem->data(Qt::DisplayRole).toInt();
      aStream<item(i,1); //垂深
      qreal chuiShen=aItem->data(Qt::DisplayRole).toFloat();
      aStream<item(i,2); //方位
      qreal fangWei=aItem->data(Qt::DisplayRole).toFloat();
      aStream<item(i,3); //位移
      qreal weiYi=aItem->data(Qt::DisplayRole).toFloat();
      aStream<item(i,4); //固井质量
      QString zhiLiang=aItem->data(Qt::DisplayRole).toString();
      aStream<item(i,5); //测井取样
      bool quYang=(aItem->checkState()==Qt::Checked);
      aStream<close();
   return true;
}

自定义函数saveDataAsStream()将表格的数据模型theModel的数据保存为一个stm文件。代码首先是创建QFile对象aFile打开文件,然后创建QDataStream对象aStream与QFile对象关联。

在开始写数据流之前,为QDataStream对象aStream设置版本号,即调用setVersion()函数,并传递一个QDataStream:: Version枚举类型的值。

aStream.setVersion(QDataStream::Qt_5_9);

这表示aStream将以QDataStream::Qt_5_9版本的预定义类型写文件流。

注意 以Qt的预定义类型编码保存的文件需要指定流版本号,因为每个版本的Qt对数据类型的编码可能有差别,需要保证写文件和读文件的流版本是兼容的。

接下来,就是按照需要保存数据的顺序写入文件流。例如在文件开始,先写入行数和列数两个qint16的整数。因为行数和列数关系到后面的数据是如何组织的,因此在读取文件数据时,首先读取这两个整数,然后根据数据存储方式的约定,就知道后续数据该如何读取了。向文件写入数据时,直接用流的输入操作,如:

aStream<<rowCount; //写入文件流,行数
aStream<<colCount;//写入文件流,列数

在读取各列的表头字符串之后,将其写入数据流。然后逐行扫描表格的数据模型,将每一行的列数据写入数据流。

数据流写入数据时都使用运算符“<<”,不论写的是qint16、qreal,还是字符串。除了可以写入基本的数据类型外,QDataStream流操作还可以写入很多其他类型的数据,如QBrush、QColor、QImage、QIcon等,这些称为可序列化的数据类型(Serializing Qt Data Types)。

QDataStream以流操作写入这些数据时,我们并不知道文件里每个字节是如何存储的,但是知道数据写入的顺序,以及每次写入数据的类型。在文件数据读出时,只需按照顺序和类型对应读出即可。

2.stm文件格式

根据saveDataAsStream()函数的代码,可知Qt预定义编码保存的stm文件的格式,如表7-1所示。

表7-1 以Qt预定义编码保存的stm文件的格式定义

| 顺序号 | 数据 | 类型 | 备注 | | :----- | :----- | :----- | :----- | :----- | :----- | | 1 | rowCount | qint16 | 行数 | | 2 | colCount | qint16 | 列数 | | 3 | “Depth” | QString | 表头标题1 | | 4 | "Measured Depth" | QString | 表头标题2 | | 5 | "Direction" | QString | 表头标题3 | | 6 | "Offset" | QString | 表头标题4 | | 7 | "Quality" | QString | 表头标题5 | | 8 | "Sampled" | QString | 表头标题6 | | 9 | 第1行各列数据 | qint16 | 测深 | | 10 | | qreal | 垂深 | | 11 | | qreal | 方位 | | 12 | | qreal | 位移 | | 13 | | QString | 固井质量 | | 14 | | bool | 是否测井取样 | | 15 | 第2行各列数据 | | ... |

从表7-1中可以知道stm文件的数据存储顺序和类型,但是并不知道qint16类型的数据存储为几个字节以及QString类型的数据是如何定义长度和字符内容的,其实也不需要知道这些具体的存储方式,在从文件读出时,只需按照表7-1的顺序和类型读出数据即可。

3.读取stm文件

下面是工具栏按钮“打开stm文件”的响应代码及相关函数代码,选择需要打开的stm文件后,主要是调用自定义函数openDataAsStream()将其打开。

void MainWindow::on_actOpen_triggered()
{//打开stm文件
   QString curPath=QDir::currentPath();
   QString aFileName=QFileDialog::getOpenFileName(this,"打开一个文件", 
                curPath,"Qt预定义编码数据文件(*.stm)");
   if (aFileName.isEmpty())
      return; 
   if  (openDataAsStream(aFileName)) 
       QMessageBox::information(this,"提示消息","文件已经打开!");
}
bool MainWindow::openDataAsStream(QString &aFileName)
{ //从stm文件读入数据
   QFile aFile(aFileName); 
   if (!(aFile.open(QIODevice::ReadOnly)))
      return false;
   QDataStream aStream(&aFile); 
   aStream.setVersion(QDataStream::Qt_5_9); //设置流文件版本号
   qint16  rowCount,colCount;
   aStream>>rowCount; //读取行数
   aStream>>colCount; //读取列数
   this->resetTable(rowCount); //表格复位,设定行数
//获取表头文字,但并不使用
   QString str;
   for (int i=0;i>str;  //读取表头字符串
//读取数据区数据
   qint16  ceShen;
   qreal  chuiShen, fangWei, weiYi;
   QString  zhiLiang;
   bool   quYang;
   QStandardItem   *aItem;
   QModelIndex  index;
   for (int i=0;i>ceShen;  //读取测深, qint16
      index=theModel->index(i,0);
      aItem=theModel->itemFromIndex(index);
      aItem->setData(ceShen,Qt::DisplayRole);
      aStream>>chuiShen;//垂深,qreal
      index=theModel->index(i,1);
      aItem=theModel->itemFromIndex(index);
      aItem->setData(chuiShen,Qt::DisplayRole);
      aStream>>fangWei;//方位,qreal
      index=theModel->index(i,2);
      aItem=theModel->itemFromIndex(index);
      aItem->setData(fangWei,Qt::DisplayRole);
      aStream>>weiYi;//位移,qreal
      index=theModel->index(i,3);
      aItem=theModel->itemFromIndex(index);
      aItem->setData(weiYi,Qt::DisplayRole);
      aStream>>zhiLiang;//固井质量,QString
      index=theModel->index(i,4);
      aItem=theModel->itemFromIndex(index);
      aItem->setData(zhiLiang,Qt::DisplayRole);
      aStream>>quYang;//测井取样
      index=theModel->index(i,5);
      aItem=theModel->itemFromIndex(index);
      if (quYang)
         aItem->setCheckState(Qt::Checked);
      else
         aItem->setCheckState(Qt::Unchecked);
   }
   aFile.close();
   return true;
}
void MainWindow::resetTable(int aRowCount)
{ //表格复位 
   theModel->removeRows(0,theModel->rowCount());
   theModel->setRowCount(aRowCount);
   QString str=theModel->headerData(theModel->columnCount()-1,
                 Qt::Horizontal,Qt::DisplayRole).toString();
   for (int i=0;i<theModel->rowCount();i++)
   { //设置最后一列
      QModelIndex index=theModel->index(i,FixedColumnCount-1); 
      QStandardItem* aItem=theModel->itemFromIndex(index);
      aItem->setCheckable(true);
      aItem->setData(str,Qt::DisplayRole);
      aItem->setEditable(false); 
   }
}

读取stm文件的数据之前也必须设置QDataStream的流版本号,应该等于或高于数据保存时的流版本号。

然后就是按照表7-1所示的写入数据时的顺序和类型,相应地读出每个数据。文件里最早的两个数据是表格的行数和列数,读出这两个数据,就能知道数据的行数和列数,并调用自定义函数resetTable()给数据模型复位,并设置其行数。

然后将保存的每行数据读入到数据模型的每个项中,这样窗口上的QTableView组件就可以显示数据了。

使用QDataStream的流操作方式读写文件的特点如下。

  • 读写操作都比较方便,支持读写各种数据类型,包括Qt的一些类,还可以为流数据读写扩展自定义的数据类型。读写某种类型的数据时,只要是流支持即可,而在文件内部是如何存储的,用户无需关心,由Qt预定义。
  • 写文件和读文件时必须保证使用的流版本兼容,即流的版本号相同,或读取文件的流版本号高于写文件时的流版本号。这是因为在不同的流版本中,流支持的数据类型的读写方式可能有所改变,必须保证读写版本的兼容。
  • 用这种方式保存文件时,写入数据采用Qt预定义的编码,即写入文件的二进制编码是由Qt预定义的,写多少个字节、字节是什么样的顺序,用户是不知道的。如果是由QDataStream读取数据,只需按类型读出即可。但是,如果由这种方法创建的文件是用于交换的,需要用其他的编程语言(如Matlab)来读取文件内容,则存在问题了。因为其他语言并没有与Qt的流写入完全一致的流读出功能,例如,其他语言并不知道Qt保存的QString或QFont的内容是如何组织的。