根据别人的动态哈夫曼编码进行了改写,用10多个字节的文件进行过测试,成功压缩并解压了,但是换成400mb的文件,压缩速度直线下降,并在700kb左右就停止了,这是为啥?或者说这样的问题怎么检测出来呀?
输入数据:二进制流,字符范围在-128~127之间
输出:二进制流
#include "huffman_coder.h"
#include <iostream>
#include <fstream>
// 获得初始码表 与 根节点
void huffman_coder::get_initialCode(char *inSymbol, std::string *inCode, int num)
{
// 获得码表
numSymbol = num;
symbol.push_back(0);
initialCode.push_back(NYT);
std::cout << std::endl << "==========编码器载入码表:";
for (int i = 0; i < numSymbol; i++) {
std::cout << symbol[i] << " ";
}
std::cout << std::endl;
// 初始化根结点
root.id = 0;
root.weight = 0;
root.parent = nullptr;
root.left = nullptr;
root.right = nullptr;
new_node = &root; // NEW初始在根
}
// 输入code,进行哈夫曼编码
std::vector<std::string> huffman_coder::get_huffmanCode(char** file, int file_size)
{
char* encode = *file;
std::cout << "==========开始编码:" ;
std::vector<std::string> out_code; // 输出编码序列
std::ofstream outFile("predeal_point_adaptivehuffman", std::ios::out | std::ios::binary);
//测试用变量
int sum = 0;
//给输出缓冲区预留100MB大小
const int BUFFER_SIZE = 100;
char* charBuffer = new char[BUFFER_SIZE];
int charBufferLength = 0; //缓冲区中字节的大小
std::string s; //替代vector<string> out_code
int count = 0; //对编译出的字符进行计数
for (int i = 0; i < file_size; i++) { // 遍历待编码字符串
int index = getIndex_symbol(encode[i]); // 当前字符在symbol的下标
std::bitset<8> bits = std::bitset<8>(encode[i]); //将字符转换成二进制形式
Node *this_node; // 当前字符指针
// 是否首次发送
if (index == -1) { // 首次发送
// std::cout << " 首次编码" << std::endl;
symbol.push_back(encode[i]); //将符号加入symbol中
initialCode.push_back(bits.to_string()); //将二进制编码加入initialCode中
numSymbol++; //增加计数
// 更新输出序列字符串
out_code.push_back(initialCode[0]); // 加入new
out_code.push_back(bits.to_string()); // 加入对应初始码
s += initialCode[0] + bits.to_string();
// 遍历树,更新结点编号 每个结点编号+2
renew_id_huffmanTree(&root);
// 更新哈夫曼树 原NEW结点分支,左孩子为新NEW,右孩子为首次发送字符
new_node->left = new Node;
new_node->left->parent = new_node;
new_node->right = new Node;
new_node->right->parent = new_node;
// this 指向右孩子 更新值 权重 编号
this_node = new_node->right;
this_node->character = encode[i];
this_node->id = 1;
this_node->weight = 1; // 之后从其根开始遍历权值+1
// new 指向左孩子
new_node = new_node->left;
new_node->character = symbol[0];
new_node->id = 0;
new_node->weight = 0;
// 从被输出结点父节点开始,每结点权重+1,并置换
renew_weight_huffmanTree(this_node->parent);
}
else { // 不是首次发送
// 输出哈夫曼编码
out_code.push_back(initialCode[index]); // 加入对应编码
s += initialCode[index];
this_node = getNode_character_huffamanTree(&root, encode[i]); // 获得该结点指针
renew_weight_huffmanTree(this_node);
}
// 根据哈夫曼树更新编码表
renew_initialCode(&root, "");
//将字符串s转换成字符数组
if(s.size() >= 8){
int byteLength = s.size() / 8;
for(int p = 0; p < byteLength; p++){
for (int j = 0; j < 8; j++) {
charBuffer[count] = (charBuffer[count] << 1) | (s[j] - '0');
}
count++;
s = s.substr(8,s.size());
}
}
//如果是文件的最后一个字节,则左移动useless位。
if(file_size - i == 1){
char useless = 8-s.size();
for (int j = 0; j < s.size(); j++) {
charBuffer[count] = (charBuffer[count] << 1) | (s[j] - '0');
}
charBuffer[count] <<= useless;
count++;
outFile.write(charBuffer, count);
}
if(count >= BUFFER_SIZE){
if (outFile.is_open()) { // 确保文件成功打开
int size = sizeof(charBuffer); // 获取字符数组大小(排除null终止符)
outFile.write(charBuffer, size); // 将字符数组写入文件
} else {
std::cout << "无法打开文件!" << std::endl;
}
sum += count;
count = 0;
}
}
sum += count;
outFile.close(); // 关闭文件
return out_code;
}
// 获取ch在symbol的下标
int huffman_coder::getIndex_symbol(char ch)
{
int i = -1;
for (int j = 0; j < numSymbol; j++) { // 0为NEW位
if (symbol[j] == ch) {
//cout << "码表位:" << i << endl;
i = j;
break;
}
}
return i;
}
// 判断ch是否为第一次发送
bool huffman_coder::is_FirstSend(char ch)
{
// 获得ch在symbol的下标
int i = getIndex_symbol(ch);
// 根据下标查找isFirst对应位置是否为0
if (isFirst[i] == 0) { // 不为第一次
return false;
}
else { // 为第一次
isFirst[i] = 0;
return true;
}
}
// 更新结点编号
void huffman_coder::renew_id_huffmanTree(Node *node)
{
// 编号+2
node->id = node->id + 2;
// 递归左右子树
if (node->left)
renew_id_huffmanTree(node->left);
if (node->right)
renew_id_huffmanTree(node->right);
}
// 获取哈夫曼树中对应字符的结点指针
Node * huffman_coder::getNode_character_huffamanTree(Node *node, char ch)
{
// 判断该结点是否为ch
if (node->character == ch)
return node;
else { // 递归到左右子结点
if (node->left)
if (getNode_character_huffamanTree(node->left, ch)) { // 要先判断是否获取到,否则返回null
return getNode_character_huffamanTree(node->left, ch);
}
if (node->right)
if (getNode_character_huffamanTree(node->right, ch)) {
return getNode_character_huffamanTree(node->right, ch);
}
}
return nullptr; // 不做判断与null处理,ch不在树内则出错
}
// 从输出结点开始更新权重
void huffman_coder::renew_weight_huffmanTree(Node * this_code)
{
if (this_code == &root) { // 如果递归到根 返回
root.weight = root.weight + 1;
return;
}
// 获得对应权重最大编号结点指针
Node *node_maxid = getNode_maxID_huffmanTree(&root, this_code->weight);
//cout << " 权重:" << this_code->weight << " 最大编号:" << node_maxid->id << endl;
// 该节点是否为同权重中编号最大的结点
if (this_code == node_maxid || this_code->parent == node_maxid) { // 该节点同权重编号最大
// 或者同权重最大编号为其父,即连续添加2个新值
this_code->weight = this_code->weight + 1;
renew_weight_huffmanTree(this_code->parent); // 递归父节点
}
else { // 不是
// 与maxid交换位置
change_2nodes(this_code, node_maxid); // 交换位置,再交换id
this_code->weight = this_code->weight + 1;
renew_weight_huffmanTree(this_code->parent); // 递归父节点
}
}
// 获得对应权重的最大id结点
Node * huffman_coder::getNode_maxID_huffmanTree(Node * node, int weight)
{
// 判断该结点是否为ch
if (node->weight == weight)
return node;
else { // 递归到左右子结点
// 先查右孩子(id值较大)
if (node->right)
if (getNode_maxID_huffmanTree(node->right, weight)) { // 要先判断是否获取到,否则返回null
return getNode_maxID_huffmanTree(node->right, weight);
}
if (node->left)
if (getNode_maxID_huffmanTree(node->left, weight)) {
return getNode_maxID_huffmanTree(node->left, weight);
}
}
return nullptr; // 不做判断与null处理,ch不在树内则出错
}
// 交换两结点位置,交换位置,再交换id
void huffman_coder::change_2nodes(Node * A, Node * B)
{
// 复制a位置和id
Node *copyA = new Node;
copyA->parent = A->parent;
copyA->id = A->id;
if (is_leftSon(A))
A->parent->left = copyA;
else
A->parent->right = copyA;
// a结点移动到b位置,id置为Bid
A->parent = B->parent;
A->id = B->id;
if (is_leftSon(B))
B->parent->left = A;
else
B->parent->right = A;
// b结点移动到a之前位置,id置为Aid
B->parent = copyA->parent;
B->id = copyA->id;
if (is_leftSon(copyA))
copyA->parent->left = B;
else
copyA->parent->right = B;
}
// 判断node是父节点的左孩子还是右孩子
bool huffman_coder::is_leftSon(Node * node)
{
if (node->parent->left == node)
return true;
else
return false;
}
// 根据哈夫曼树更新编码表
void huffman_coder::renew_initialCode(Node *node, std::string code)
{
// 从根结点开始遍历,储存字符串
if (node->left) { // 有左孩子
//cout << " 进入左孩子" << code + "0" << endl;
renew_initialCode(node->left, code + "0");
}
if (node->right) { // 有右孩子
//cout << " 进入右孩子" << code + "1" << endl;
renew_initialCode(node->right, code + "1");
}
if (node->left == nullptr && node->right == nullptr) {// 若为非new叶子
// 将initialCode对应叶子字符更新为code
int i = getIndex_symbol(node->character);
// std::cout << " 到达叶子:";
// printf("%d",node->character);
// std::cout << " 更新值:" << initialCode[i] << "->" << code << std::endl;
initialCode[i] = code;
}
}
有时不将“调用函数名字+各参数值,进入函数后各参数值,中间变量值,退出函数前准备返回的值,返回函数到调用处后函数名字+各参数值+返回值”这些信息写日志到文件中是无论如何也发现不了问题在哪里的,包括捕获各种异常、写日志到屏幕、单步或设断点或生成core或dmp文件、……这些方法都不行! 写日志到文件参考下面:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
#pragma warning(disable:4996)
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
#define CRITICAL_SECTION pthread_mutex_t
#define _vsnprintf vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef _MSC_VER
void Lock(CRITICAL_SECTION *l) {
EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
struct tm *now;
struct timeb tb;
if (NULL==pszFmt||0==pszFmt[0]) return;
_vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
ftime(&tb);
now=localtime(&tb.time);
sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
sprintf(timestr,"%02d:%02d:%02d",now->tm_hour ,now->tm_min ,now->tm_sec );
sprintf(mss,"%03d",tb.millitm);
printf("%s %s.%s %s",datestr,timestr,mss,logstr);
flog=fopen(logfilename1,"a");
if (NULL!=flog) {
fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
if (ftell(flog)>MAXLOGSIZE) {
fclose(flog);
if (rename(logfilename1,logfilename2)) {
remove(logfilename2);
rename(logfilename1,logfilename2);
}
} else {
fclose(flog);
}
}
}
void Log(const char *pszFmt,...) {
va_list argp;
Lock(&cs_log);
va_start(argp,pszFmt);
LogV(pszFmt,argp);
va_end(argp);
Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
int i;
#ifdef _MSC_VER
InitializeCriticalSection(&cs_log);
#else
pthread_mutex_init(&cs_log,NULL);
#endif
for (i=0;i<10000;i++) {
Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
}
#ifdef _MSC_VER
DeleteCriticalSection(&cs_log);
#else
pthread_mutex_destroy(&cs_log);
#endif
return 0;
}
//1-79行添加到你带main的.c或.cpp的那个文件的最前面
//82-86行添加到你的main函数开头
//90-94行添加到你的main函数结束前
//在要写LOG的地方仿照第88行的写法写LOG到文件MyLog1.log中