//Method used for compressing files static void Compress(string input, string output) { //start of method //Initilize variables int totalChars = 0; //Counts number of characters in file, used for decompression purposes BinaryTree <CharacterFrequency> charTree = new BinaryTree <CharacterFrequency>(); StreamReader read = new StreamReader(input); //Create a dictionary of CharacterFrequency objects with the int key being the ascii code of each character IDictionary <int, CharacterFrequency> charDictionary = new Dictionary <int, CharacterFrequency>(); //Read entire file character by character... while (!read.EndOfStream) { int i = read.Read(); //checks if Dictionary contains the char i... if (!charDictionary.ContainsKey(i)) { //if not add new record to dictionary charDictionary.Add(i, new CharacterFrequency((char)i, 1)); } else { //if so increment the frequency of this record charDictionary[i].increment(); } totalChars++; } read.Close(); //Create ordered linked list of TreeNode<CharacterFrequency> objects OrderedLinkedList <TreeNode <CharacterFrequency> > orderedNodeList = new OrderedLinkedList <TreeNode <CharacterFrequency> >(); //Go throughs each KeyValuePair in the CharacterFrequency dicitionary.. foreach (KeyValuePair <int, CharacterFrequency> c in charDictionary) { //Make a new TreeNode of the CharacterFrequency value from c... TreeNode <CharacterFrequency> node = new TreeNode <CharacterFrequency>(c.Value); //add into the ordered linked list of TreeNode<CharacterFrequency> orderedNodeList.Add(node); } //Run a method in the binary tree to convert the ordered node list into a single node with branching children container all other nodes //and return and set said node as the root node of the tree charTree.Insert(charTree.TransferListToTree(orderedNodeList).GetFirst(), Relative.Root); //Run a method from the tree to return a list of BinaryPath objects LinkedList <BinaryPath <CharacterFrequency> > binaryTable = charTree.BinaryTable(charTree.Root); //Removes all nodes from the binarytable that have a null character as they cannot show up in a file var binary = binaryTable.First; while (binary != null) { var nextNode = binary.Next; if (binary.Value.Element.Character == '\0') { binaryTable.Remove(binary); } binary = nextNode; } //Byte writing section int bitPosition = 7; //creates an int that determines what bit in the byte to set byte y = 0; //creates the byte to set and write with BinaryWriter binaryWriter = null; bool error = true; while (error) { try { binaryWriter = new BinaryWriter(File.Create(CheckFile(output))); error = false; } catch (UnauthorizedAccessException ex) { MessageBox.Show("You do not have the permissions needed to write to this folder"); } } StreamReader streamReader = new StreamReader(input); //string of the entire table, to used to append to the beginning of the output file. Will be read by out in decompression process string tableText = String.Empty; //Take the total characters from the input file and append to the beginning of the output file tableText += totalChars + "~~~"; //Take table and write to output file foreach (BinaryPath <CharacterFrequency> bp in binaryTable) { tableText += bp.Element.Character + bp.Path + ";"; } binaryWriter.Write(tableText); //Compression process while (!streamReader.EndOfStream) { //Read each character from file.. int i = streamReader.Read(); //Find path of character from binary table... string path = binaryTable.FirstOrDefault(obj => obj.Element.Character == (char)i).Path; //Iterate through each character in path... foreach (char c in path) { //If bitposition = 0 we have set all other bits if (bitPosition == 0) { //if character is '1' set bit at bitposition in byte to 1 using bitwise OR(|) if (c == '1') { y = (byte)(y | (byte)Math.Pow(2, bitPosition)); } //Writes byte to output file binaryWriter.Write(y); //Resets byte and bitposition y = 0; bitPosition = 7; } //If bitposition != 0 else { //if character is '1' set bit at bitposition in byte to 1 using bitwise OR(|) if (c == '1') { y = (byte)(y | (byte)Math.Pow(2, bitPosition)); } //progresses bitposition down 1 to next bit in byte bitPosition--; } } } //Close stream reader read.Close(); //write final byte to output file and close BinaryWriter binaryWriter.Write(y); binaryWriter.Close(); //Output to command prompt to let user know file is finished being compressied, end program Console.WriteLine($"File has been compressed to the file {output}"); }
static void Decompress(string input, string output) { //get tuple from method var tuple = GetTableFromFile(input); //get binary reader from tuple to maintain stream position BinaryReader read = tuple.Item3; StreamWriter write = null; bool error = true; while (error) { try { write = new StreamWriter(CheckFile(output)); error = false; } catch (UnauthorizedAccessException ex) { MessageBox.Show("You do not have the permissions needed to write to this folder"); } } //get total number of characters to write to output file from method int totalChar = tuple.Item2; //get binary path array from tuple BinaryPath <char>[] binaryCharTable = tuple.Item1; BinaryTree <TreeNode <char> > tree = new BinaryTree <TreeNode <char> >(); //create root note in binary tree tree.Insert(new TreeNode <char>(), Relative.Root); //iterate through the binary paths for each character... foreach (BinaryPath <char> bp in binaryCharTable) { tree.moveTo(Relative.Root); int i = 1; //navigate through tree using characters from path of binarypath object bp foreach (char c in bp.Path) { //if character is a 1... if (c == '1') { //and if the next left node does not exist but we have reached the end of the path... if (tree.Current.Left == null && i == bp.Path.Length) { //insert a new node to the left with the value being the character of the bp object tree.Insert(new TreeNode <char>(bp.Element), Relative.LeftChild); } //or if the next left node does not exist but we are not at the end of the path... else if (tree.Current.Left == null) { //insert a new null node to the left... tree.Insert(new TreeNode <char>(), Relative.LeftChild); //then move to this new node and continue the loop tree.moveTo(Relative.LeftChild); i++; } //or if the next left node does exist... else { //then move to the next left node... tree.moveTo(Relative.LeftChild); //and continue the loop i++; } } //else if the character in the path is a 0... else { //and if the next right node does not exist but we have reached the end of the path... if (tree.Current.Right == null && i == bp.Path.Length) { //insert a new node to the right with the value being the character of the bp object tree.Insert(new TreeNode <char>(bp.Element), Relative.RightChild); } //or if the next right node does not exist but we are not at the end of the path... else if (tree.Current.Right == null) { //insert a new null node to the right... tree.Insert(new TreeNode <char>(), Relative.RightChild); //then move to this new node and continue the loop tree.moveTo(Relative.RightChild); i++; } //or if the right node does exist... else { //then move to the next right node... tree.moveTo(Relative.RightChild); //and continue the loop i++; } } } } //once the tree is complete move back to the root tree.moveTo(Relative.Root); //Decompression process //create byte for reading purposes byte b = 0; //number of characters read from the file, to be compaired to the number of characters needing to be written for the output file int charWritten = 0; //Continue to read the next byte in the file until we have reached it's end while (read.BaseStream.Position != read.BaseStream.Length) { //get the next byte b = read.ReadByte(); //loop to iterate through each bit in the byte starting at the 8th position and going backwards for (int i = 128; i > 0; i = i / 2) { //a check to see if we have written all the characters we need to the output file, if not then continue the navigation if (charWritten != totalChar) { //using bitwise AND to check if the bit at the current position is a 0... if ((b & i) == 0) { //if it is a 0, move to the right child of the current node in the tree tree.moveTo(Relative.RightChild); //check if the node we moved to is a leaf if (tree.Current.isLeaf) { //if it is a leaf, write the character from that leaf to the output file, and move back to the tree's root node write.Write(tree.Current.Element.Element); tree.moveTo(Relative.Root); charWritten++; } } //or a 1 else { //if it is a 1, move to the left child of the current node in the tree tree.moveTo(Relative.LeftChild); //check if the node we moved to is a leaf if (tree.Current.isLeaf) { //if it is a leaf, write the character from that leaf to the output file, and move back to the tree's root node write.Write(tree.Current.Element.Element); tree.moveTo(Relative.Root); charWritten++; } } } } } //close the stream writer and output to screen the file has been decompressed and to what file, end program write.Close(); Console.WriteLine($"File has been decompressed to the file {output}"); }