bingbingyihao 2023-04-06 14:41 采纳率: 100%
浏览 90
已结题

关于C语言写入结构体时无法写入map的问题

在写哈夫曼编码压缩与解压文件的问题时,Java是非常容器且没有问题的;但是在C语言里面,这个文件读写就变得很困难;当我尝试写入一个结构体时,发现没有问题,但是实际并没有将map的内容顺利写入;而且我发现不管是fread还是fwrite,都无法将无法提前确定大小的map集合写入;太痛苦了;感觉我已经在尝试学习Java序列化的解决思路了。

代码如下:(原本以为已经写完的我,才发现只是开了个头;C语言的文件读写好折磨,太折磨了)
文章地址:

# include 
# include 
# include 
# include 
# include 
# include 
# include 
# include 
using namespace std;

// 读取文件得到int数组转化成哈夫曼编码得到哈夫曼树,进行压缩与解压

typedef int elementType;

struct Node
{
    elementType data;
    int value;
    Node* left;
    Node* right;
};

struct ZipStruct
{
    map encodeMap; // 由哈夫曼树生成的编码表
    int size = 0; // 所占字节数
    int zipSize = 0; // 所占比特数(用于处理末尾数字补全)
};

struct Zip
{
    string originFileName;
    string destFileName; // 压缩生成的文件名
    elementType* buff = NULL; // 读入的文件字符数据
    mapint> twoTuple; // 字符串转化为二元组形式,表示各个字符出现的频率
    map encodeMap; // 由哈夫曼树生成的编码表
    string res; // 生成的二进制编码字符串
    int size = 0; // 所占字节数
    int zipSize = 0; // 所占比特数(用于处理末尾数字补全)
};

struct UnZip
{
    string originFileName; // 要解压的文件名
    string destFileName; // 解压生成的文件名
};

// 将int数组转化为二元组形式,表示各个int数字出现的频率
mapint> getList(int* text, int length);

//自定义排序函数  
bool cmp(const Node* node1, const Node* node2);

// 通过二元组创建哈夫曼树
Node* createHafumanTree(vector list);

// 获取编码
void getCodes(Node* root, string path, map& encodeMap);

// 读取文件内容,得到int数组
elementType* readFile(string fileName, int& size);

// 根据编码表将int数组转化为新的编码
string transform(elementType* buff, int length, map encodeMap);

// 获取文件生成的二进制编码字符串
void generateCodes(Zip& zip);

//将传入的二进制字符串转换成十进制,并返回十进制数字
int binToTen(string binaryString);

// 将新编码字符串转化为int数组(转化采用末尾不够补0),减少占用空间,并写入文件中,返回文件比特数,同时一并将编码表和字节与比特数写入
void zipFile(string res, string fileName, int& zipSize, ZipStruct* zipStruct);

//将传入的int转换成二进制字符串
string intToBin(int ch);

// 根据编码表将文件还原
void unZipFile(string originFileName, string destFileName, ZipStruct* zipStruct);

int main()
{
    Zip zip;
    UnZip unZip;
    ZipStruct zipStruct;

    zip.originFileName = "D:/test.png";
    zip.destFileName = "D:/test.myZip";
    generateCodes(zip);
    zipStruct.encodeMap = zip.encodeMap;
    zipStruct.size = zip.size;
    zipFile(zip.res, zip.destFileName, zip.zipSize, &zipStruct);

    unZip.originFileName = zip.destFileName;
    unZip.destFileName = "D:/test1.png";
    unZipFile(unZip.originFileName, unZip.destFileName, &zipStruct);

    system("pause");
}

mapint> getList(int* text, int length)
{
    mapint> map;
    for (int i = 0; i < length; i++)
    {
        if (map.count((int)text[i]) == 1)
        {
            map[text[i]] = map[text[i]] + 1;
        }
        else
        {
            map[text[i]] = 1;
        }
    }
    return map;
}

bool cmp(const Node* node1, const Node* node2)
{
    return node1->value > node2->value;
}

Node* createHafumanTree(vector list)
{
    while (list.size() > 1)
    {
        sort(list.begin(), list.end(), cmp);

        Node* left = list.at(list.size() - 1);
        Node* right = list.at(list.size() - 2);
        Node* node = (Node*)malloc(sizeof(Node));
        if (node == NULL)
        {
            cout << "内存不足" << endl;
            return NULL;
        }
        node->value = left->value + right->value;
        node->left = left;
        node->right = right;

        list.pop_back();
        list.pop_back();
        list.push_back(node);
    }

    return list.at(0);
}

void getCodes(Node* root, string path, map& encodeMap)
{
    if (root == NULL)
    {
        return;
    }
    if (root->left == NULL && root->right == NULL)
    {
        encodeMap.insert(map::value_type(root->data, path));
    }
    getCodes(root->left, path + "0", encodeMap);
    getCodes(root->right, path + "1", encodeMap);
}

elementType* readFile(string fileName, int& size)
{
    struct stat buf;
    stat(fileName.c_str(), &buf);
    size = buf.st_size;

    FILE* fp = NULL;
    int* buff = new int[buf.st_size];

    errno_t err;
    if ((err = fopen_s(&fp, fileName.c_str(), "rb")) != 0)
    {
        cout << "文件打开失败" << endl;
        return NULL;
    }

    int index = 0;
    while (index < buf.st_size)
    {
        buff[index++] = fgetc(fp);
    }
    fclose(fp);
    return buff;
}

string transform(elementType* buff, int length, map encodeMap)
{
    string res = "";
    for (int i = 0; i < length; i++)
    {
        res += encodeMap[buff[i]];
    }
    return res;
}

void generateCodes(Zip& zip)
{
    zip.buff = readFile(zip.originFileName, zip.size);
    zip.twoTuple = getList(zip.buff, zip.size);
    vector list;
    for (auto& it : zip.twoTuple)
    {
        Node* node = (Node*)malloc(sizeof(Node));
        if (node == NULL)
        {
            cout << "内存不足" << endl;
            return;
        }
        node->data = it.first;
        node->value = it.second;
        node->left = NULL;
        node->right = NULL;
        list.push_back(node);
    }

    Node* root = createHafumanTree(list);
    getCodes(root, "", zip.encodeMap);
    zip.res = transform(zip.buff, zip.size, zip.encodeMap);
}

int binToTen(string binaryString)
{
    int parseBinary = 0;
    for (int i = 0; i < binaryString.length(); ++i)
    {
        if (binaryString[i] == '1')
        {
            parseBinary += pow(2.0, binaryString.length() - i - 1);
        }
    }
    return parseBinary;
}

void zipFile(string res, string fileName, int& zipSize, ZipStruct* zipStruct)
{
    zipSize = res.length();
    zipStruct->zipSize = zipSize;
    if (res.length() % 8 != 0)
    {
        for (int i = 0; i < res.length() % 8; i++)
        {
            res += "0";
        }
    }

    char* bytes = new char[res.length() / 8];
    int index = 0;
    for (int i = 0; i < res.length();)
    {
        string subStr = res.substr(i, 8);
        i += 8;
        bytes[index] = binToTen(subStr);
        index++;
    }

    FILE* fp = NULL;
    errno_t err;
    if ((err = fopen_s(&fp, fileName.c_str(), "wb")) != 0)
    {
        cout << "文件写入失败" << endl;
        return;
    }
    fwrite(zipStruct, sizeof(ZipStruct), 1, fp);
    fwrite(bytes, sizeof(unsigned __int8), res.length() / 8, fp);
    fclose(fp);
}

string intToBin(int ch)
{
    string res = "";
    for (int i = 7; i >= 0; i--)
    {
        if (ch & (128 >> 7 - i))
        {
            res += "1";
        }
        else
        {
            res += "0";
        }
    }
    return res;
}

void unZipFile(string originFileName, string destFileName, ZipStruct* zipStruct)
{
    FILE* fp = NULL;
    errno_t err;
    if ((err = fopen_s(&fp, originFileName.c_str(), "rb")) != 0)
    {
        cout << "文件打开失败" << endl;
        return;
    }

    fread(zipStruct, sizeof(ZipStruct), 1, fp);
    map decodeMap;
    for (auto& it : zipStruct->encodeMap)
    {
        decodeMap.insert(mapchar>::value_type(it.second, it.first));
    }

    // 为了凑整,省的用if语句了
    int size = zipStruct->zipSize;
    int length = (size + 7) / 8;
    int* buff = new int[length];
    int index = 0;
    while (index < length)
    {
        buff[index++] = fgetc(fp);
    }
    fclose(fp);

    string res = "";
    for (int i = 0; i < length; i++)
    {
        res += intToBin(buff[i]);
    }

    string text = "";
    for (int i = 0; i <= size;)
    {
        index = 1;
        while (i + index <= size)
        {
            string temp = res.substr(i, index);
            if (decodeMap.count(temp) == 1)
            {
                text += decodeMap[temp];
                break;
            }
            index++;
        }
        i += index;
    }

    if ((err = fopen_s(&fp, destFileName.c_str(), "wb")) != 0)
    {
        cout << "文件写入失败" << endl;
        return;
    }
    fwrite(text.c_str(), sizeof(unsigned __int8), text.length(), fp);
    fclose(fp);
}

文章可能在审核,要过一小会才能看到

  • 写回答

3条回答 默认 最新

  • Karaterelope 2023-04-06 14:50
    关注

    ChatGPT :
    在C语言中,无法直接将map写入文件,因为map是一个动态的数据结构,其大小在编译时无法确定。因此,需要将map转换为一种可以写入文件的数据结构,比如数组或者结构体。在本文中,作者使用了一个名为ZipStruct的结构体来存储压缩文件的相关信息,其中包括编码表(encodeMap)和所占字节数(size)。在压缩文件时,先将编码表和字节流写入ZipStruct结构体中,然后再将整个结构体写入文件中。在解压文件时,先读取ZipStruct结构体,然后根据其中的编码表将二进制编码字符串转化为原始字符,最后将原始字符写入文件中。需要注意的是,在写入和读取文件时,需要使用二进制模式打开文件,否则可能会出现数据损坏的情况。同时,在写入和读取结构体时,需要使用fwrite和fread函数,而不是普通的文件写入和读取函数。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 4月14日
  • 已采纳回答 4月6日
  • 创建了问题 4月6日

悬赏问题

  • ¥15 关于#wireshark#的问题:并且能够给数据做标注,如这个流量是在看视频或者是在转账
  • ¥90 请问,这个视频播放软件的名称,用过的朋友请给答案,上方..avi是啥意思?是看短剧还是播放本地视频?
  • ¥15 运筹优化,gurobi,python
  • ¥15 基于python的电影系统推荐
  • ¥20 springmvc重定向和返回json
  • ¥15 数学建模——参会安排怎么做
  • ¥15 电脑键盘实现触摸功能
  • ¥25 matlab无法将表达式转换为双数组怎么解决?
  • ¥15 单片机汇编语言相关程序
  • ¥20 家用射频美容仪技术规格