C语言二进制文件读取解析

时间:2023-01-04 08:59:30

// FileDefine.h

#ifndef _FILEDEFINE_H_

#define _FILEDEFINE_H_
#include <string>
using namespace std;
/*
文件操作,对磁盘的读写:
fopen 打开模式 和 缓存区大小
打开模式:
r:read 读没有文件会读取失败
w:write 写会清空文件,没有可以创建文件
a:append 写追加没有文件可以创建文件

+: input + output

b: binary 没有b修饰的是默认ascii文本操作

t: text,是文本文件方式打开读写,如果没有b修饰,那么默认就是t 文本方式。

wb:只写方式打开二进制文件,可以写入的时候去掉文本结束符。
fwrite返回的长度不等于要写的长度,那说明磁盘满了。

fclose

读写:
字符读写:
fputc fgetc
字符串读写:
fputs fgets

数据块读写:
fread 连续读写,文件内部的指针游标会跟随累加
fwrite 连续写也会累加

参数:new内存,结构体的地址。

格式化读写:
fscanf(fp,"%d%s", num, name)格式化读取,将从文件指针fp中读取的数据存入num,name中,然后格式化输出
fprintf


跳转计算访问索引:
fseek跳到指定的位置
ftell返回当前的长度

EOF是-1作为文本文件结束的标志,但是在二进制文件中不行,需要用到feof函数。

屏幕操作,对键盘和屏幕的读写:
printf输出到屏幕
gets从屏幕输入
getc
putchar显示读取的字符

*/

// 包头
struct PackageHeader
{
    int m_nCount; // 文件的个数
    int m_nLen; // 文件的长度
    PackageHeader()
    {
        m_nCount = 0;
        m_nLen = 0;
    }

    /*PackageHeader& operator =(const PackageHeader &b)
    {
        this->m_nCount = b.m_nCount;
        this->m_nLen = b.m_nLen;
        return (*this);
    }*/
};

// 每个文件的索引信息
struct FileList
{
    unsigned int m_nFileID;
    int m_nOffset; // 每个文件在包中的偏移
    int m_nSize; // 文件的大小
    FileList()
    {
        m_nFileID = 0;
        m_nOffset = 0;
        m_nSize = 0;
    }
};

// 数据内容
struct StudentData
{
    unsigned int m_nNum; // 序号
    unsigned int m_nLevel; // 等级
    unsigned int m_nScroe; // 分数
    string m_szName;

    //string m_strName; // 名字
    //string m_strDepartment; // 部门
    //string m_strSchool; // 学校
};

#endif


// FilePackage.h

#ifndef _FILEPACKAGE_H_
#define _FILEPACKAGE_H_
#include <stdio.h>
#include <map>
#include <vector>
using namespace std;
#include "FileDefine.h"
class CFilePackage
{
public:
    CFilePackage();
    ~CFilePackage(){};
    void OpenFile(const string strFilePath, char *szMode);
    void ReadFile();
    void WriteFile(vector<StudentData> *pVecData);
    StudentData* GetFileData(unsigned int fileID);
    void CloseFile();
    
private:
    FILE *m_pFile;
    PackageHeader m_fileHead;
    map<unsigned int, FileList> m_mapFileList; // unsigned int是文件FileID
    
    StudentData m_stuData; // 数据

};
#endif


// FilePackage.cpp

#include "stdafx.h"
#include "FilePackage.h"
CFilePackage::CFilePackage()
{
    m_pFile = NULL;
    //PackageHeader m_fileHead;
    //map<unsigned int, FileList> m_mapFileList; // unsigned int是文件FileID

    //StudentData m_stuData; // 数据

}

void CFilePackage::OpenFile(const string strFilePath, char *szMode)
{
    if(strFilePath.empty())
    {
        return;
    }

    m_pFile = fopen(strFilePath.c_str(), szMode);
    if(m_pFile == NULL)
    {
        return;
    }
}

void CFilePackage::CloseFile()
{
    fclose(m_pFile);
    m_pFile = NULL;
}

void CFilePackage::ReadFile()
{
    if(m_pFile == NULL)
    {
        return;
    }

    // 直接读取头文件到内存结构体中
    fread(&m_fileHead, sizeof(PackageHeader),1, m_pFile);
    m_mapFileList.clear();
    // 读取m_nCount个文件的索引
    for(int i = 0; i < m_fileHead.m_nCount; i++)
    {
        FileList fileListTemp;
        // 直接读取到索引结构体中
        fread(&fileListTemp, sizeof(FileList), 1, m_pFile);
        // 连续的读取,保存索引
        m_mapFileList[fileListTemp.m_nFileID] = fileListTemp;
    }
}

StudentData* CFilePackage::GetFileData(unsigned int fileID)
{
    map<unsigned int, FileList>::iterator itFile = m_mapFileList.find(fileID);
    if(itFile == m_mapFileList.end())
    {
        return NULL;
    }

    FileList *fileList = &(itFile->second);
    // 直接跳转,读取文件数据
    fseek(m_pFile, fileList->m_nOffset, SEEK_SET);
    fread(&m_stuData, sizeof(StudentData), 1, m_pFile);
    //int nCurLength = ftell(m_pFile);//ftell函数是用来获取文件的当前读写位置;
    // 重置文件指针到文件头部
    fseek(m_pFile, 0, SEEK_SET); // fseek(fp,OL,SEEK_END);移动到文件结尾

    return &m_stuData;
}

void CFilePackage::WriteFile(vector<StudentData> *pVecData)
{
    //typedef struct tagPackageHeader
    //{
    //    int m_nCount; // 文件的个数
    //    int m_nLen; // 文件的长度
    //}PackageHeader;

    //// 每个文件的索引信息
    //typedef struct tagFileList
    //{
    //    unsigned int m_nFileID;
    //    int m_nOffset; // 每个文件在包中的偏移
    //    int m_nSize; // 文件的大小
    //}FileList;

    if(m_pFile == NULL && pVecData == NULL)
    {
        return;
    }

    int nCount = (int)pVecData->size();
    fseek(m_pFile, 0, SEEK_SET);

    fwrite(&m_fileHead, sizeof(PackageHeader), 1, m_pFile);

    FileList fileList;
    fwrite(&fileList, sizeof(FileList), nCount, m_pFile);
    map<unsigned int, FileList> tempFileList;
    
    for(int i = 0; i < nCount; i++)
    {
        StudentData stuData;

        FileList fileList;
        fileList.m_nFileID = (*pVecData)[i].m_nNum;
        fileList.m_nOffset = ftell(m_pFile);
        fileList.m_nSize = 3 * sizeof(unsigned int) + (*pVecData)[i].m_szName.length();
        tempFileList[stuData.m_nNum] = fileList;

        
        stuData.m_nNum = (*pVecData)[i].m_nNum;
        stuData.m_nLevel = (*pVecData)[i].m_nLevel;
        stuData.m_nScroe = (*pVecData)[i].m_nScroe;
        stuData.m_szName = (*pVecData)[i].m_szName;

        fwrite(&stuData, sizeof(StudentData), 1, m_pFile);
    }

    map<unsigned int, FileList>::iterator itrFileList = tempFileList.begin();
    fseek(m_pFile, sizeof(PackageHeader), SEEK_SET);
    for(; itrFileList != tempFileList.end(); ++itrFileList)
    {
        fwrite(&(itrFileList->second), sizeof(FileList),1, m_pFile);
    }
    
    fseek(m_pFile, 0, SEEK_END);
    int nFileLen = ftell(m_pFile);
    m_fileHead.m_nCount = (int)tempFileList.size();
    m_fileHead.m_nLen =  nFileLen;
    fseek(m_pFile, 0, SEEK_SET);
    fwrite(&m_fileHead, sizeof(PackageHeader), 1, m_pFile);
    fseek(m_pFile, 0, SEEK_SET);

    tempFileList.clear();
}


//// FileOperate.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
using namespace std;

#include "FilePackage.h"

int _tmain(int argc, _TCHAR* argv[])
{
    CFilePackage File;

    File.OpenFile("d:\\jiayuan.bin", "wb");

    vector<StudentData> vecData;
    StudentData data;
    data.m_nNum = 100;
    data.m_nLevel = 9;
    data.m_nScroe = 78;
    data.m_szName = "家园";
    vecData.push_back(data);

    data.m_nNum = 101;
    data.m_nLevel = 8;
    data.m_nScroe = 88;
    data.m_szName = "欧圈圈";

    
    vecData.push_back(data);

    File.WriteFile(&vecData);
    File.CloseFile();

    File.OpenFile("d:\\jiayuan.bin", "rb");
    File.ReadFile();
    StudentData *pResData;
    while(1)
    {
        cout<< "请输入人物ID号:"<<endl;
        int nNum;
        cin>> nNum;
        pResData = File.GetFileData(nNum);
        if(pResData != NULL)
        {
            cout<<"查询结果:"<<endl;
            cout<<"序号:"<<pResData->m_nNum<<" 名字:"<<pResData->m_szName<<" 等级:"<<pResData->m_nLevel<<" 分数:"<<pResData->m_nScroe<<endl;
        }
        else
        {
            cout<<"序号没有查询结果!"<<endl;
        }
        
    }
    
    return 0;
}