【QT】二进制文件读写

 

目录

1 实例功能概述

2 Qt预定义编石马文件的读写

2.1 保存为文件

2.2 stm文件格式

2.3 读取stm文件

3 标准编码文件的读写

3.1 保存为dat文件

3.2 dat文件格式

3.3 读取dat文件


       文件的读写是很多应用程序具有的功能,甚至某些应用程序就是围绕着某一种格式文件的处 理而开发的,所以文件读写是应用程序开发的一个基本功能。

1 实例功能概述

        除了文本文件之外,其他需要按照一定的格式定义读写的文件都称为二进制文件。每种格式 的二进制文件都有自己的格式定义,写入数据时按照一定的顺序写入,读出时也按照相应的顺序 读出。例如地球物理中常用的SEG-Y格式文件,必须按照其标准格式要求写入数据才符合这种文 件的格式规范,读取数据时也需要按照格式定义来读出。
        Qt使用QFile和QDataStream进行二进制数据文件的读写。QFile负责文件的IO设备接口, 即与文件的物理交互,QDataStream以数据流的方式读取文件内容或写入文件内容。
        本节以实例samp7_2演示二进制文件的读写,图7-2是程序运行的界面。
图7-2   实例samp7_2的二进制文件读写功能
        实例以表格形式编辑一个数据表,采用Model/View结构,编辑后的数据保存为二进制文件。
        根据QDataStream保存文件时使用的数据编码的方式不同,可以保存为两种文件。
    (1)用Qt预定义编码保存各种类型数据的文件,定义文件后缀为“stm”。Qt预定义编码是指 在写入某个类型数据,如整形数、字符串等到文件流时,使用Qt预定义的编码。可以将这种Qt预 定义数据格式编码类比于HTML的标记符,Qt写入某种类型数据时用了Qt预定义的标记符,读出 数据时,根据标记符读出数据。使用Qt预定义编码保存的流文件,某些字节是QDataStream自己写 入的,我们并不完全知道文件内每个字节的意义,但是用QDataStream可以读出相应的数据。
(2)标准编码数据文件,定义文件后缀为".dat”。在将数据写到文件时,完全使用数据的二进 制原始内容,每个字节都有具体的定义,在读出数据时,只需根据每个字节的定义读出数据即可。
        实例samp7_2具有如下功能:
  • 可以在表格内编辑数据,同样的表格数据内容可以保存为两种格式的文件,Qt预定义编 码文件(文件)和标准编码文件(dat文件);
  • 界面上的表格数据可以修改,可以添加行、插入行、删除行;
  • 可以读取stm文件或文件,虽然文件格式不一样,但对相同的界面数据表存储的文件 的实质内容是一样的。
        实例samp7_2的主窗口使用了Model/View结构、标准项数据模型QStandardItemModel和选 择模型QItemSelectionModel,界面上使用了QTableView组件,还有代理组件。
        为便于理解后面的程序,这里给出主窗口MainWindow类中自定义的一些变量和函数,具体 如下(忽略了自动生成的一些定义):
class MainWindow : public QMainWindow
{
private:
//用于状态栏的信息显示
    QLabel  *LabCellPos;    //当前单元格行列号
    QLabel  *LabCellText;   //当前单元格内容
    QWIntSpinDelegate    intSpinDelegate; //整型数
    QWFloatSpinDelegate  floatSpinDelegate; //浮点数
    QWComboBoxDelegate   comboBoxDelegate; //列表选择
    QStandardItemModel  *theModel;//数据模型
    QItemSelectionModel *theSelection;//Item选择模型
    void    resetTable(int aRowCount);  //表格复位,设定行数
    bool    saveDataAsStream(QString& aFileName);//将数据保存为数据流文件
    bool    openDataAsStream(QString& aFileName);//读取数据流文件
    bool    saveBinaryFile(QString& aFileName);//保存为二进制文件
    bool    openBinaryFile(QString& aFileName);//打开二进制文件
};

2 Qt预定义编石马文件的读写

2.1 保存为文件

        先看文件保存功能,因为从文件保存功能的代码可以看出文件内数据的存储顺序。在图7-2 的窗口上编辑表格的数据后,单击工具栏上的“保存stm文件”,可以使用Qt预定义编码方式保 存文件。此按钮的响应代码如下:
void MainWindow::on_actSave_triggered()
{ //以Qt预定义编码保存数据文件
    QString curPath=QDir::currentPath();
    QString aFileName=QFileDialog::getSaveFileName(this,tr("选择保存文件"),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<<rowCount; //写入文件流,行数
    aStream<<colCount;//写入文件流,列数
//获取表头文字
    for (int i=0;i<theModel->columnCount();i++)
    {
        QString str=theModel->horizontalHeaderItem(i)->text();//获取表头文字
        aStream<<str; //字符串写入文件流,Qt预定义编码方式
    }

//获取数据区的数据
    for (int i=0;i<theModel->rowCount();i++)
    {
        QStandardItem* aItem=theModel->item(i,0); //测深
        qint16 ceShen=aItem->data(Qt::DisplayRole).toInt();
        aStream<<ceShen;// 写入文件流,qint16

        aItem=theModel->item(i,1); //垂深
        qreal chuiShen=aItem->data(Qt::DisplayRole).toFloat();
        aStream<<chuiShen;//写入文件流, qreal

        aItem=theModel->item(i,2); //方位
        qreal fangWei=aItem->data(Qt::DisplayRole).toFloat();
        aStream<<fangWei;//写入文件流, qreal

        aItem=theModel->item(i,3); //位移
        qreal weiYi=aItem->data(Qt::DisplayRole).toFloat();
        aStream<<weiYi;//写入文件流, qreal

        aItem=theModel->item(i,4); //固井质量
        QString zhiLiang=aItem->data(Qt::DisplayRole).toString();
        aStream<<zhiLiang;// 写入文件流,字符串

        aItem=theModel->item(i,5); //测井
        bool quYang=(aItem->checkState()==Qt::Checked);
        aStream<<quYang;// 写入文件流,bool型
    }
    aFile.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.2 stm文件格式

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

2.3 读取stm文件

        下面是工具栏按钮“打开stm文件”的响应代码及相关函数代码,选择需要打开的stm文件 后,主要是调用自定义函数openDataAsStream()将其打开。
void MainWindow::on_actOpen_triggered()
{
    QString curPath=QDir::currentPath();
//调用打开文件对话框打开一个文件
    QString aFileName=QFileDialog::getOpenFileName(this,tr("打开一个文件"),curPath, "流数据文件(*.stm)");

    if (aFileName.isEmpty())
        return; 
    if  (openDataAsStream(aFileName)) //保存为流数据文件
         QMessageBox::information(this,"提示消息","文件已经打开!");
}

bool MainWindow::openDataAsStream(QString &aFileName)
{ //从Qt预定义流文件读入数据
    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<colCount;i++)
        aStream>>str;  //读取表头字符串

    //获取数据区文字,
    qint16  ceShen;
    qreal  chuiShen;
    qreal  fangWei;
    qreal  weiYi;
    QString  zhiLiang;
    bool    quYang;
    QStandardItem   *aItem;
    QModelIndex index;

    for (int i=0;i<rowCount;i++)
    {
        aStream>>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;//bool
        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); //获取item
        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 的内容是如何组织的。

3 标准编码文件的读写

3.1 保存为dat文件

        前面是采用Qt预定义编码读写文件,这种方法使用简单,但是文件的格式不完全透明, 不能创建用于交换的通用格式文件。
        创建通用格式文件(即文件格式完全透明,每个字节都有具体的定义,如SEG-Y文件)的方 法是以标准编码方式创建文件,使文件的每个字节都有具体的定义。用户在读取这种文件时,按 照文件格式定义读取出每个字节数据并做解析即可,不管使用什么编程语言都可以编写读写文件 的程序。
        主窗口工具栏上的“保存dat文件”按钮将表格中的数据保存为标准编码的文件,文件后缀 是“.dat”。保存文件的代码是:
void MainWindow::on_actSaveBin_triggered()
{//保存二进制文件
    QString curPath=QDir::currentPath();
    //调用打开文件对话框选择一个文件
    QString aFileName=QFileDialog::getSaveFileName(this,tr("选择保存文件"),curPath, "二进制数据文件(*.dat)");
    if (aFileName.isEmpty())
        return; 

    if  (saveBinaryFile(aFileName)) //保存为流数据文件
        QMessageBox::information(this,"提示消息","文件已经成功保存!");
}

bool MainWindow::saveBinaryFile(QString &aFileName)
{ //保存为纯二进制文件
    QFile aFile(aFileName);  //以文件方式读出
    if (!(aFile.open(QIODevice::WriteOnly)))
        return false;

    QDataStream aStream(&aFile); //用文本流读取文件
    aStream.setByteOrder(QDataStream::LittleEndian);//windows平台

    qint16  rowCount=theModel->rowCount();
    qint16  colCount=theModel->columnCount();

    aStream.writeRawData((char *)&rowCount,sizeof(qint16)); //写入文件流
    aStream.writeRawData((char *)&colCount,sizeof(qint16));//写入文件流

//获取表头文字
    QByteArray  btArray;
    QStandardItem   *aItem;
    for (int i=0;i<theModel->columnCount();i++)
    {
        aItem=theModel->horizontalHeaderItem(i); //获取表头item
        QString str=aItem->text(); //获取表头文字
        btArray=str.toUtf8(); //转换为字符数组
        aStream.writeBytes(btArray,btArray.length()); //写入文件流,长度uint型,然后是字符串内容
    }

//获取数据区文字,
    qint8   yes=1,no=0; //分别代表逻辑值 true和false
    for (int i=0;i<theModel->rowCount();i++)
    {
        aItem=theModel->item(i,0); //测深
        qint16 ceShen=aItem->data(Qt::DisplayRole).toInt();//qint16类型
        aStream.writeRawData((char *)&ceShen,sizeof(qint16));//写入文件流

        aItem=theModel->item(i,1); //垂深
        qreal chuiShen=aItem->data(Qt::DisplayRole).toFloat();//qreal 类型
        aStream.writeRawData((char *)&chuiShen,sizeof(qreal));//写入文件流

        aItem=theModel->item(i,2); //方位
        qreal fangWei=aItem->data(Qt::DisplayRole).toFloat();
        aStream.writeRawData((char *)&fangWei,sizeof(qreal));

        aItem=theModel->item(i,3); //位移
        qreal weiYi=aItem->data(Qt::DisplayRole).toFloat();
        aStream.writeRawData((char *)&weiYi,sizeof(qreal));

        aItem=theModel->item(i,4); //固井质量
        QString zhiLiang=aItem->data(Qt::DisplayRole).toString();
        btArray=zhiLiang.toUtf8();
        aStream.writeBytes(btArray,btArray.length()); //写入长度,uint,然后是字符串
        aItem=theModel->item(i,5); //测井取样
        bool quYang=(aItem->checkState()==Qt::Checked); //true or false
        if (quYang)
            aStream.writeRawData((char *)&yes,sizeof(qint8));
        else
            aStream.writeRawData((char *)&no,sizeof(qint8));
    }

    aFile.close();
    return true;
}
  • 字节序
在保存为标准编码的二进制文件时,无须指定QDataStream的版本,因为不会用到Qt的类型 预定义编码,文件的每个字节的意义都是用户自己定义的。但是如有必要,需要为文件指定字节 顺序,如:
aStream.setByteOrder(QDataStream::LittleEndian);
        字节顺序分为大端字节序和小端字节序,小端字节序指低字节数据存放在内存低地址处,高 字节数据存放在内存高地址处;大端字节序则相反。
        基于X86平台的计算机是小端字节序的,所以Windows系统是小端字节序,而有的嵌入式平台或工作站平台则是大端字节序的。读取一个文件时,首先需要知道它是以什么字节序存储的, 这样才可以正确的读出。
        setByteOrder()函数的参数是QDataStream::ByteOrder枚举类型常量,QDataStream::BigEndian 是大端字节序,QDataStream::
LittleEndian 是小端字节序。
  • writeRawData()函数
        QDataStream采用函数writeRawData()将数据写入数据流,在保存qint8、qint16、qreal等类型 的数据时都使用这个函数,其函数原型是:
int QDataStream::writeRawData(const char *s,int len)
        其中参数s是一个指向字节型数据的指针,len是字节数据的长度。调用writeRawData()函数 将会向文件流连续写入len个字节的数据,这些字节数据保存在指针s指向的起始地址里。例如, 将qint16类型变量rowCount写入文件的语句是:
qint16 rowCount=the ModeI->rowCount();
aStream.writeRawData((char*)&rowCount,sizeof(qint16));
  • writeBytes()函数
        在将字符串数据写入文件时,使用的是writeBytes()函数,而不是writeRawData()。下面是 writeBytes()函数的原型定义:
QDataStream &QDataStream::writeBytes(const char *s,uint len)
        其中参数s是一个指向字节型数据的指针,Ien是字节数据的长度。writeBytes()在写入数据时, 会先将len作为一个quint32类型写入数据流,然后再写入len个从指针s获取的数据。
        writeBytes()适合于写入字符串数据,因为在写入字符串之前要先写入字符串的长度,这样在 读取文件时,就能知道字符串的长度,以便正确读出字符串。
例如,下面的代码将字符串“Depth"写入文件流:
QString str="Depth"
QByteArray btArray=str.toUtf8();
aStream.writeBytes(btArray,btArray·length());
        文件中实际保存的内容见表7-2。前4个字节是quint32类型的整数,表示保存数据的字节个 数,这里是5,表示后续有5个字节数据。从第5字节开始,是保存的字符串"Depth"的每个字符 的ASCII码。
        由于写入文件的字符串的长度一般是不固定的,因此如果以writeRawData()函数写入文件, 只会写入字符串的内容,而没有表示字符串的长度。在文件读出时,如果不己知字符串长度,则 难以正确读出字符串内容。而writeBytes()函数首先写入了字符串的长度,在读取文件时,先从前 四个字节读出字符串长度,知道数据有多少个字节就可以正确读出了。
        QDataStream提供了与writeBytes()对应的函数readBytes(),它可以自动读取长度和内容,适 用于字符串数据的读取。

3.2 dat文件格式

用saveBinaryFile()函数保存数据为标准编码二进制文件,文件后缀为“.dat”。根据 saveBinaryFile()函数的内容,dat文件的格式见表7-3。
在表7-3中,可以看到文件内的每个字节都是有具体定义的,这样,无论用什么语言编写一 个文件读取的程序,只要按照这个格式来读取,都可以正确读出文件内容。
        dat文件的数据是否是按照表7-3所示的顺序存储的呢?可以创建一个简单的数据表格,保存 为dat后缀的文件,然后用显示文件二进制内容的软件来查看, 如UltraEdit或WinHex,这些软件 在分析文件格式,编写文件读写程序时特别有用。

3.3 读取dat文件

        对于保存的dat文件,主窗口工具栏上的“打开dat文件”按钮可以打开保存的dat文件,下 面是打开dat文件的函数openBinaryFile()的代码。
bool MainWindow::openBinaryFile(QString &aFileName)
{//打开二进制文件
    QFile aFile(aFileName);  //以文件方式读出
    if (!(aFile.open(QIODevice::ReadOnly)))
        return false;

    QDataStream aStream(&aFile); //用文本流读取文件
    aStream.setByteOrder(QDataStream::LittleEndian);

    qint16  rowCount,colCount;
    aStream.readRawData((char *)&rowCount, sizeof(qint16));
    aStream.readRawData((char *)&colCount, sizeof(qint16));

    this->resetTable(rowCount);

    //获取表头文字,但是并不利用
    char *buf;
    uint strLen;  //也就是 quint32
    for (int i=0;i<colCount;i++)
    {
        aStream.readBytes(buf,strLen);//同时读取字符串长度,和字符串内容
        QString str=QString::fromLocal8Bit(buf,strLen); //可处理汉字
    }


//获取数据区数据
    QStandardItem   *aItem;
    qint16  ceShen;
    qreal  chuiShen;
    qreal  fangWei;
    qreal  weiYi;
    QString  zhiLiang;
    qint8   quYang; //分别代表逻辑值 true和false
    QModelIndex index;

    for (int i=0;i<rowCount;i++)
    {
        aStream.readRawData((char *)&ceShen, sizeof(qint16)); //测深
        index=theModel->index(i,0);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(ceShen,Qt::DisplayRole);

        aStream.readRawData((char *)&chuiShen, sizeof(qreal)); //垂深
        index=theModel->index(i,1);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(chuiShen,Qt::DisplayRole);

        aStream.readRawData((char *)&fangWei, sizeof(qreal)); //方位
        index=theModel->index(i,2);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(fangWei,Qt::DisplayRole);

        aStream.readRawData((char *)&weiYi, sizeof(qreal)); //位移
        index=theModel->index(i,3);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(weiYi,Qt::DisplayRole);

        aStream.readBytes(buf,strLen);//固井质量
        zhiLiang=QString::fromLocal8Bit(buf,strLen);
        index=theModel->index(i,4);
        aItem=theModel->itemFromIndex(index);
        aItem->setData(zhiLiang,Qt::DisplayRole);

        aStream.readRawData((char *)&quYang, sizeof(qint8)); //测井取样
        index=theModel->index(i,5);
        aItem=theModel->itemFromIndex(index);
        if (quYang==1)
            aItem->setCheckState(Qt::Checked);
        else
            aItem->setCheckState(Qt::Unchecked);
    }

    aFile.close();
    return true;
}
  • 字节序
        在流创建后,需要用setByteOrder()函数指定字节序,并且与写入文件时用的字节序一致。
  • readRawData()函数
        在读取基本类型数据时,使用QDataStream的readRawData()函数,该函数原型为:
int QDataStream::readRawData(char *s,int len)
        它会读取个字节的数据,并且保存到指针s指向的存储区。例如:
qint16 rowCount;
aStream.readRawData((char*)&rowCount,sizeof(qint16));
  • readBytes()函数
        读取字符串时使用readBytes()函数,它是与writeBytes()功能对应的函数,其函数原型为:
QDataStream &QDataStream::readBytes(char *&s,uint &len)
        对应表格7-2,使用readBytes()函数时,会先自动读取前4个字节数据作为quint32的数据, 并赋值给len参数,因为len是以引用方式传递的参数,所以,len返回读取的数据的字节数。然 后根据len的大小读取相应字节的数据,存储到指针s指向的存储区。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/348888.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[docker] Docker的数据卷、数据卷容器,容器互联

一、数据卷&#xff08;容器与宿主机之间数据共享&#xff09; 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立刻可见&#xff0c;并且更新数据不会影响镜像&#xff0c;从而实现数据在宿主机与容…

开发知识点-Flutter移动应用开发

支持 安卓 IOS Android 鸿蒙 第一章dart基础章节介绍 移动电商——Flutter-广告Banner组件制作 移动电商——Flutter实战课程介绍 Flutter实例——路由跳转的动画效果

禅道(HIS医疗系统)项目管理

文章目录 前言禅道的基本使用指南本次讲解举例参与人员&#xff1a;一、admin管理组织结构1.1批量新增用户 二、产品经理使用禅道2.1以陈雪燕账号去创建产品2.2添加产品模块2.3添加产品计划2.4添加产品需求2.5创建项目4.6设置团队 三、项目经理使用禅道3.1关联需求3.2分解任务 …

【寒假每日一题·2024】AcWing 4965. 三国游戏(补)

文章目录 一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、时间复杂度3、代码详解 一、题目 1、原题链接 4965. 三国游戏 2、题目描述 二、解题报告 1、思路分析 思路参考y总&#xff1a;y总讲解视频 &#xff08;1&#xff09;题目中的获胜情况分为三种&#xff…

【Servlet】如何编写第一个Servlet程序

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Servlet】 本专栏旨在分享学习Servlet的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; Servlet是Java编写的服务器端…

《WebKit 技术内幕》学习之十三(1):移动WebKit

1 触控和手势事件 1.1 HTML5规范 随着电容屏幕的流行&#xff0c;触控操作变得前所未有的流行起来。时至今日&#xff0c;带有多点触控功能已经成为了移动设备的标准配置&#xff0c;基于触控的手势识别技术也获得巨大的发展&#xff0c;如使用两个手指来缩放应用的大小等。…

深度学习(6)---Transformer

文章目录 一、介绍二、架构2.1 Multi-head Attention2.2 Encoder(编码器)2.3 Decoder(解码器) 三、Encoder和Decoder之间的传递四、Training五、其他介绍5.1 Copy Mechanism5.2 Beam Search 一、介绍 1. Transformer是一个Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;…

Christmas Log Village Pack (Interior / Exterior) - VR/Mobile

这个圣诞主题的包包含了建造一个美丽的雪村所需的一切! 现在已更新为Unity 2019.3(与Unity 4以来的所有Unity版本兼容) 该包针对移动设备进行了优化,每个道具仅有两个纹理图集(外部和内部),2个雪地纹理,2个房屋地面纹理。该包包含大约150个预制件,还包括演示场景。 每…

【计算机网络】协议,电路交换,分组交换

定义了在两个或多个通信实体之间交换的报文格式和次序,以及报文发送和/或接收一个报文或其他事件所采取的动作.网络边缘: 端系统 (因为处在因特网的边缘) 主机 端系统 客户 client服务器 server今天大部分服务器都属于大型数据中心(data center)接入网(access network) 指将端…

项目解决方案:非执法视频监控系统项目设计方案

目 录 一、概述 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;设计思路 &#xff08;三&#xff09;设计原则 1、实用性 2、可靠性 3、安全性 4、先进性 5、开放性 6、易管理、易维护 &#xff08;四&#xff09;设计依据 二、方案总…

【QT+QGIS跨平台编译】之十:【libbz2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、libbz2介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libbz2介绍 bzip2是一个基于Burrows-Wheeler 变换的无损压缩软件,压缩效果比传统的LZ77/LZ78压缩算法来得好。它是一款免费软件。可以自由分发免费使用。 bzip2能够进行高质量的数据压缩。它利用…

Spring Boot如何统计一个Bean中方法的调用次数

目录 实现思路 前置条件 实现步骤 首先我们先自定义一个注解 接下来定义一个切面 需要统计方法上使用该注解 测试 实现思路 通过AOP即可实现&#xff0c;通过AOP对Bean进行代理&#xff0c;在每次执行方法前或者后进行几次计数统计。这个主要就是考虑好如何避免并发情况…

Gradle学习笔记:Gradle的使用方法

文章目录 1.初始化项目2.构建脚本语言选择3.项目命名4.项目构建过程 1.初始化项目 创建一个test空文件夹&#xff0c;在该文件夹下打开终端&#xff0c;并执行命令&#xff1a;gradle init. 会有一个选项让你选择项目的类型。下面是每个选项的含义和用途&#xff1a; basic&am…

腾讯LLaMA Pro大模型:突破大模型微调的知识遗忘难题

引言&#xff1a;大模型微调中的挑战 在人工智能的发展过程中&#xff0c;大型语言模型&#xff08;LLM&#xff09;的微调&#xff08;fine-tuning&#xff09;始终是提升模型在特定任务上性能的关键。然而&#xff0c;微调过程中常面临一个主要挑战&#xff1a;知识遗忘。这…

【TCP】传输控制协议

前言 TCP&#xff08;Transmission Control Protocol&#xff09;即传输控制协议&#xff0c;是一种面向连接的、可靠的、基于字节流的传输层通信协议。它由IETF的RFC 793定义&#xff0c;为互联网中的数据通信提供了稳定的传输机制。TCP在不可靠的IP层之上实现了数据传输的可…

HCIE之BGP正则表达式(四)

BGP 一、AS-Path正则表达式数字| 等同于或的关系[]和.$ 一个字符串的结束_代表任意^一个字符串的开始()括号包围的是一个组合\ 转义字符* 零个或多个&#xff1f;零个或一个一个或多个 二、BGP对等体组 一、AS-Path正则表达式 正则表达式是按照一定模版匹配字符串的公式 AR3上…

数字孪生系统的难点

数字孪生系统的开发和实施涉及一些技术难点&#xff0c;这些难点需要综合应用多个领域的知识和技术来克服。以下是一些数字孪生系统开发中的技术难点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1…

React进阶 - 14(说一说”虚拟DOM“中的”Diff算法“)

本章内容 目录 一、了解 Diff 算法二、key 值的重要性三、为什么不建议使用 index 做 key 值 上一节我们初步了解了 React中的”虚拟 DOM“ &#xff0c;本节我们来说一说”虚拟DOM“中的”Diff算法“ 一、了解 Diff 算法 在上一篇中&#xff0c;我们有讲到&#xff1a;当 st…

CentOS 6/7/8系统加固方案

密码失效时间 设置密码失效时间,强制定期修改密码,减少密码被泄漏和猜测风险,若使用非密码登陆方式(如密钥对)请忽略此项。 在 /etc/login.defs 中将 PASS_MAX_DAYS 参数设置为 60-180之间,如: PASS_MAX_DAYS 180 需同时执行命令设置root密码失效时间: chage --maxdays…

编程笔记 html5cssjs 057 CSS导航栏

编程笔记 html5&css&js 057 CSS导航栏 一、导航栏 链接列表二、垂直导航栏三、水平导航栏四、下拉菜单五、实例: 响应式导航栏小结 导航栏。易用的导航对于任何网站都很重要。通过使用 CSS&#xff0c;您可以将无聊的 HTML 菜单转换为美观的导航栏。 一、导航栏 链接…