最近用C#写huffman编译码程序的时候发现了一些问题,这里整理一下(新手勿喷): [文件为非中文文件]
1.文件中出现的各个字符的出现频率的取得。其实现方法如下:
1.1很容易想到一种一个个(Streamreader.read)读入或者一行行(Streamreader.readLine)读入或者整个文本读入(Streamreader.readToEnd),再进行匹配。可以预见,这个方法非常慢,对于几百kb的txt或者几Mb的txt会很慢。
1.2这里看到一种方法是读入后,进行整个文本的查找,替换成空,前后的字符串长度的变化即为这个字符出现的频次。
c2 = str.Length - str.Replace("A", String.Empty).Length;
1.3 根据1.2,想到另外一种方法。即要查找的字符作为分隔符,将原字符串分隔为多个子串,然后求子串的数目即可
c3 = str.Split(new char[] { 'A' }).Length - 1;
统计整个文本的字符时应注意以下问题:
- 注意读入文本时所用的方法的异同;read()返回对应的ASCII,readLine()遇到“\n”,“\r\n”等会对文本中出现的换行有影响,建议使用整个文本读入,速度也快。
- 由于我是静态开辟的数组存储对应的字符频次,所以使用的是ASCII表,这里当然会有一些字符对应的频次是0,故这些要去掉,不去掉的话对后面的Huffman编码影响较大,个人建议使用动态开辟的;
//通过遍历进行Hufman编码,并输出
public void traverse(Node root)
{
if (root != null)
{
if (root.character != "NO CHAR")
{
this.code[this.i, 0] = root.character;
this.code[this.i, 1] = root.code;
this.i++; }
if (root.left != null)
{
root.left.code = root.left.parent.code + "0";
traverse(root.left);
}
if (root.right != null)
{
root.right.code = root.left.parent.code + "1";
traverse(root.right);
}
}
}
3.对文件进行Huffman编码。
很容易想到每个字符对应每个Huffman码,那么直接替换就可以了。但是要注意0和1的特殊性,所以这里要对0和1在文本的字符进行优先处理,而且要注意到两个之间任意一个先替换都会影响后一个的替换。所以只能是将文本读入的信息进行一次0和1处理(我目前只想到了这个,不过貌似可以记录已经编码的字符位置,然后来实现)。
对于其他非0和1字符,直接进行全文替换就可以了。
//按照编码顺序一行行编码,实为按照编码替换
public void make_string()
{
this.m_streamReader.BaseStream.Seek(0, SeekOrigin.Begin);// 从数据流中读取每一行,直到文件的最后一行
int strLine;
//查到0和1的编码
string code0=null,code1=null;
for(int i=0;i<this.code.GetLength(0);i++)
{
if(this.code[i,0]=="0") code0=this.code[i,1];
if(this.code[i,0]=="1") code1=this.code[i,1];
}
while (this.m_streamReader.Peek() >= 0)
{
strLine = this.m_streamReader.Read();
switch (strLine)
{
case 48: this.s += code0; break;
case 49: this.s += code1; break;
default: this.s += (char)strLine; break;
}
}
this.m_streamReader.Close();
this.fs1.Close();
for (int i = 0; i < this.code.GetLength(0); i++)
if ((this.code[i, 0]!= "0") && (this.code[i, 0]!= "1"))
this.s = this.s.Replace(this.code[i, 0], this.code[i, 1]);
}
4.Huffman译码的实现。根据输入的文本信息01流,按照Huffman树进行依次查找。这里直接贴代码
//通过遍历进行Huffman译码,并输出
public Node decode(int a,Node root)
{
Node node=null;
if (root != null)
{
if (a == 48)//左子树
{
if (!root.left.isLeaf())
{
node = root.left;
}
else
{
this.txt += root.left.character;
node = this.root;
}
}
if (a == 49)
{
if (!root.right.isLeaf())
{
node = root.right;
}
else
{
this.txt += root.right.character;
node = this.root;
}
}
}
return node;
}
注意Huffman树随着源文件的变大,编译码的速度会很慢,很多时候代码本身还会出现内存溢出等各种错误,所以就要求仔细分析代码的效率。