diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..e7e7cb3 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,6 @@ +image: Visual Studio 2019 + +build_script: + - For /R %%I in (*.sln) do dotnet test %%I + +test: off \ No newline at end of file diff --git a/hw2LZW/hw2LZW.sln b/hw2LZW/hw2LZW.sln new file mode 100644 index 0000000..884139e --- /dev/null +++ b/hw2LZW/hw2LZW.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31005.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hw2LZW", "hw2LZW\Hw2LZW.csproj", "{956FF906-65B8-45A7-AEEC-0C4F4D47EF5D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "hw2Lzw.Test", "hw2LzwTest\hw2Lzw.Test.csproj", "{705F21DF-850F-439F-BF37-F0F8EF554073}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {956FF906-65B8-45A7-AEEC-0C4F4D47EF5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {956FF906-65B8-45A7-AEEC-0C4F4D47EF5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {956FF906-65B8-45A7-AEEC-0C4F4D47EF5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {956FF906-65B8-45A7-AEEC-0C4F4D47EF5D}.Release|Any CPU.Build.0 = Release|Any CPU + {705F21DF-850F-439F-BF37-F0F8EF554073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {705F21DF-850F-439F-BF37-F0F8EF554073}.Debug|Any CPU.Build.0 = Debug|Any CPU + {705F21DF-850F-439F-BF37-F0F8EF554073}.Release|Any CPU.ActiveCfg = Release|Any CPU + {705F21DF-850F-439F-BF37-F0F8EF554073}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DB7EE293-FCD3-4437-92C9-C3ABFADE5DE7} + EndGlobalSection +EndGlobal diff --git a/hw2LZW/hw2LZW/LZW.cs b/hw2LZW/hw2LZW/LZW.cs new file mode 100644 index 0000000..0f36315 --- /dev/null +++ b/hw2LZW/hw2LZW/LZW.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.IO; + +namespace Hw2LZW +{ + /// + /// класс для сжатия и разжатия + /// + public static class LZW + { + private static int GetCountOfBytes(byte[] array) + { + for (int i = array.Length - 1; i >= 0; i--) + { + if (array[i] != 0) + { + return i + 1; + } + } + return 1; + } + + /// + /// функция сжатия файла + /// + /// путь до файла, который будем сжимать + public static void Compress(string pathFile) + { + using var file = new FileStream(pathFile, FileMode.Open); + var trie = new Trie(); + var codes = new Queue(); + for (int i = 0; i < file.Length; ++i) + { + var byter = (byte)file.ReadByte(); + var codeOfBytes = trie.IsAdd(byter); + if (codeOfBytes != -1) + { + codes.Enqueue(codeOfBytes); + trie.IsAdd(byter); + } + } + codes.Enqueue(trie.GetCode()); + var countOfBytes = GetCountOfBytes(BitConverter.GetBytes(trie.CountCodes)); + using var fileZipped = new FileStream(pathFile + ".zipped", FileMode.CreateNew); + fileZipped.WriteByte((byte)countOfBytes); + var size = codes.Count; + for (int i = 0; i < size; i++) + { + var helpArray = BitConverter.GetBytes(codes.Dequeue()); + fileZipped.Write(helpArray, 0, countOfBytes); + } + } + + private static Hashtable InitializeHashtable() + { + var hashtable = new Hashtable(); + for (int i = 0; i < 256; i++) + { + hashtable.Add(i, new byte[] { (byte)i }); + } + return hashtable; + } + + private static void AddLastSymbol(int code, Hashtable hashtable) + { + var bytes = (byte[])hashtable[code]; + var previous = (byte[])hashtable[hashtable.Count - 1]; + previous[previous.Length - 1] = bytes[0]; + } + + /// + /// функция для разжатия + /// + /// путь до сжатого файла + public static void Decompress(string pathFile) + { + var hashtable = InitializeHashtable(); + var codes = hashtable.Count; + using var fileZipped = new FileStream(pathFile, FileMode.Open); + using var file = new FileStream(pathFile.Substring(0, pathFile.Length - 7), FileMode.OpenOrCreate); + int maxLength = fileZipped.ReadByte(); + for (int i = 0; i < fileZipped.Length - 1; i += maxLength) + { + var codeInBytes = new byte[4]; + for (int j = 0; j < maxLength; ++j) + { + codeInBytes[j] = (byte)fileZipped.ReadByte(); + } + var code = BitConverter.ToInt32(codeInBytes, 0); + if (i != 0) + { + AddLastSymbol(code, hashtable); + } + var bytesArray = (byte[])hashtable[code]; + var copyBytesArray = new byte[bytesArray.Length + 1]; + Array.Copy(bytesArray, copyBytesArray, bytesArray.Length); + hashtable.Add(codes, copyBytesArray); + codes++; + file.Write(bytesArray); + } + } + } +} \ No newline at end of file diff --git a/hw2LZW/hw2LZW/Program.cs b/hw2LZW/hw2LZW/Program.cs new file mode 100644 index 0000000..2bb683e --- /dev/null +++ b/hw2LZW/hw2LZW/Program.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; + +namespace Hw2LZW +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("Ошибка ввода!"); + return; + } + if (args[1] == "-c") + { + LZW.Compress(args[0]); + var compressedFileSize = new FileInfo(args[0]); + var decompressedFileSize = new FileInfo(args[0] + ".zipped"); + Console.WriteLine($"Коэффициент сжатия: x {(double)compressedFileSize.Length / decompressedFileSize.Length}"); + } + else if (args[1] == "-u") + { + LZW.Decompress(args[0]); + Console.WriteLine("Файл разжат!"); + } + else + { + Console.WriteLine("Ошибка ввода!"); + return; + } + } + } +} \ No newline at end of file diff --git a/hw2LZW/hw2LZW/Trie.cs b/hw2LZW/hw2LZW/Trie.cs new file mode 100644 index 0000000..11c3488 --- /dev/null +++ b/hw2LZW/hw2LZW/Trie.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; + +namespace Hw2LZW +{ + /// + /// вспомогательный класс для реализация Lzw + /// + public class Trie + { + private class Node + { + public byte Bytes { get; } + + public bool IsUsed { get; set; } + + public int IdByte; + + public int CodeBytes { get; set; } + + public Dictionary Sons; + + public Node(byte bytes, int codeByte, bool isUsed) + { + IsUsed = isUsed; + Bytes = bytes; + IdByte = codeByte; + Sons = new Dictionary(); + } + + public Node IsFind(byte value) + => Sons.TryGetValue(value, out Node node) ? node : null; + } + + private Node root = new Node(0, 0, false); + + private Node runner; + + public Trie() + { + for (int i = 0; i < 256; ++i) + { + InitRoot((byte)i, i); + } + runner = root; + } + + public int CountCodes { get; set; } + + private void InitRoot(byte idSymbol, int index) + { + + var son = new Node(idSymbol, index, false); + root.Sons.Add(idSymbol, son); + son.CodeBytes = CountCodes; + CountCodes++; + } + + private bool CheckAdd(byte value, Node node) + => node.IsFind(value) == null; + + public int LastCode { get; set; } + + /// + /// функция добавления + /// + /// байт, который хотим добавить + /// если такой байт уже есть, то вернем "-1" + public int IsAdd(byte value) + { + if (runner == root) + { + var check = runner.Sons[value]; + if (!check.IsUsed) + { + check.IsUsed = true; + runner = runner.Sons[value]; + LastCode = runner.Bytes; + return -1; + } + } + var isCheck = CheckAdd(value, runner); + if (isCheck) + { + var son = new Node(value, CountCodes, true); + son.CodeBytes = CountCodes; + CountCodes++; + runner.Sons.Add(value, son); + runner = root; + return LastCode; + } + else + { + runner = runner.Sons[value]; + LastCode = runner.CodeBytes; + return -1; + } + } + + /// + /// функция для возврата кода у узла + /// + /// возвращается код узла + public int GetCode() + => runner.Bytes; + } +} \ No newline at end of file diff --git a/hw2LZW/hw2LZW/hw2LZW.csproj b/hw2LZW/hw2LZW/hw2LZW.csproj new file mode 100644 index 0000000..c73e0d1 --- /dev/null +++ b/hw2LZW/hw2LZW/hw2LZW.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/hw2LZW/hw2LzwTest/LZWTest.cs b/hw2LZW/hw2LzwTest/LZWTest.cs new file mode 100644 index 0000000..3ab34f1 --- /dev/null +++ b/hw2LZW/hw2LzwTest/LZWTest.cs @@ -0,0 +1,53 @@ +using NUnit.Framework; +using System.IO; + +namespace hw2LzwTest +{ + [TestFixture] + public class Tests + { + private bool Compare(string path1, string path2) + { + using FileStream file1 = File.OpenRead(path1); + using FileStream file2 = File.OpenRead(path2); + if (file1.Length != file2.Length) + { + return false; + } + for (int i = 0; i < file1.Length; ++i) + { + if (file1.ReadByte() != file2.ReadByte()) + { + return false; + } + } + return true; + } + + [TestCase] + public void LZWWithTxt() + { + string path1 = "..\\..\\..\\TestTxt.txt"; + string path2 = "..\\..\\..\\TestTxtCopy.txt"; + File.Copy(path1, path2); + Hw2LZW.LZW.Compress(path1); + Hw2LZW.LZW.Decompress(path1 + ".zipped"); + File.Delete(path1 + ".zipped"); + Assert.IsTrue(Compare(path1, path2)); + File.Delete(path2); + } + + [TestCase] + public void LZWWithImg() + { + string path1 = "..\\..\\..\\TestImg.jpg"; + string path2 = "..\\..\\..\\TestImgCopy.jpg"; + File.Copy(path1, path2); + Hw2LZW.LZW.Compress(path1); + Hw2LZW.LZW.Decompress(path1 + ".zipped"); + File.Delete(path1 + ".zipped"); + Assert.IsTrue(Compare(path1, path2)); + File.Delete(path2); + } + } +} \ No newline at end of file diff --git a/hw2LZW/hw2LzwTest/TestImg.jpg b/hw2LZW/hw2LzwTest/TestImg.jpg new file mode 100644 index 0000000..e27885f Binary files /dev/null and b/hw2LZW/hw2LzwTest/TestImg.jpg differ diff --git a/hw2LZW/hw2LzwTest/TestTxt.txt b/hw2LZW/hw2LzwTest/TestTxt.txt new file mode 100644 index 0000000..7801a8a --- /dev/null +++ b/hw2LZW/hw2LzwTest/TestTxt.txt @@ -0,0 +1,3 @@ +sleep and code +eat and code +walk and coee \ No newline at end of file diff --git a/hw2LZW/hw2LzwTest/hw2Lzw.Test.csproj b/hw2LZW/hw2LzwTest/hw2Lzw.Test.csproj new file mode 100644 index 0000000..1ff4cb0 --- /dev/null +++ b/hw2LZW/hw2LzwTest/hw2Lzw.Test.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + +