Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Homework3/LZW/LZW.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LZW", "LZW\LZW.csproj", "{5DAEADDF-7108-4028-90E1-5C05C2034360}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "LZWTest\Tests.csproj", "{455E760B-8592-4A15-BB37-A79DAE435B4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DAEADDF-7108-4028-90E1-5C05C2034360}.Release|Any CPU.Build.0 = Release|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{455E760B-8592-4A15-BB37-A79DAE435B4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7673691E-822C-4405-8F11-DE9C2BCBAE62}
EndGlobalSection
EndGlobal
81 changes: 81 additions & 0 deletions Homework3/LZW/LZW/Bor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
namespace Bor;

/// <summary>
/// A class representing the bor data structure
/// </summary>
public class Bor
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Он Trie

{
/// <summary>
/// // A class representing the bor data structure
/// </summary>
private class Node
{
// Dictionary for storing characters for each node
public Dictionary<byte, Node> Nodes = new();

// Code for each node
public int Code { get; set;}
}

// Bor root
private readonly Node root = new();

// Bor size
public int Size { get; private set; }

private Node currentNode;

public int GetCode()
{
return currentNode.Code;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public int GetCode()
{
return currentNode.Code;
}
public int GetCode()
=> currentNode.Code;


public Bor()
{
root.Code = -1;

// Initialize the bor with numbers from 0 to 256
for (int i = 0; i < 256; i++)
{
Node node = new();
root.Nodes.Add((byte)i, node);
node.Code = Size;
Size++;
}

// The current node is the root
currentNode = root;
}

/// <summary>
/// Function to add a byte
/// </summary>
/// <param name="byteToAdd"> byte to add </param>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// <param name="byteToAdd"> byte to add </param>
/// <param name="byteToAdd"> Byte to add </param>

public void Add(byte byteToAdd)
{
Node node = new();
currentNode?.Nodes.Add(byteToAdd, node);
currentNode = root;
node.Code = Size;
Size++;
}

/// <summary>
/// Is the byte contained in Current Node
/// </summary>
/// <param name="element"> Element to search </param>
/// <returns> True if there is such a byte. False if there is no such byte </returns>
public bool Contains(byte byteToSearch) => currentNode.Nodes.ContainsKey(byteToSearch);

/// <summary>
/// Function for moving to another node
/// </summary>
/// <param name="byteT">the byte to be moved to</param>
public void MoveIntoDesiredNode(byte byteToBeMovedTo) => currentNode = currentNode.Nodes[byteToBeMovedTo];

/// <summary>
/// Function for moving to root
/// </summary>
public void MovedToRoot() => currentNode = root;
}

150 changes: 150 additions & 0 deletions Homework3/LZW/LZW/LZW.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
namespace LZW;

using System;
using System.IO;
using Bor;

/// <summary>
/// A class representing the LZW algorithm
/// </summary>
public class LZW
{
//A function for determining the number of bytes needed to store a number
private static int NumberOfBytes(int number)
{
if (number < 256)
{
return 1;
}
if (number < 65536)
{
return 2;
}
if (number < 16777216)
{
return 3;
}
return 4;
}

/// <summary>
/// Function for file compression
/// </summary>
/// <param name="pathToFileToCompress">The path to the file to compress</param>
public static void CompressFile(string pathToFileToCompress)
{

string fileName = Path.GetFileNameWithoutExtension(pathToFileToCompress);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
string fileName = Path.GetFileNameWithoutExtension(pathToFileToCompress);
{
string fileName = Path.GetFileNameWithoutExtension(pathToFileToCompress);

После открывающей фигурной скобки пустая строка не ставится

fileName = $"{pathToFileToCompress}..//..//{fileName}.zipped";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так мы теряем расширение файла в процессе запаковки/распаковки, что не очень.


// Name of the compressed file
using FileStream fs = new(fileName, FileMode.Create);

// Reading all bytes from a file
var stringToConvert = File.ReadAllBytes(pathToFileToCompress);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вообще, может быть плохой идеей читать файл в память целиком. Файлы бывают большие. Тем более что LZW может работать побайтово. Но это можно не править, для учебных целей вполне ок.


// Creating bor
Bor bor = new();

for (int i = 0; i < stringToConvert.Length; i++)
{
// If the byte is contained in the bor
if (bor.Contains(stringToConvert[i]))
{
// Moving on to the next byte
bor.MoveIntoDesiredNode(stringToConvert[i]);
}
// Otherwise, we add it to the bor
else
{
// Taking the idex from the parent vertex and encode it
var bytes = BitConverter.GetBytes(bor.GetCode());

// Cut off the extra bytes
// The required number of bytes is selected according to the size of the bor
// Since among the numbers that need to be encoded there may be a bor size (index of the maximum vertex),
// the number of bytes to store depends on the size of the bor.
// Even a number requires fewer bytes,
// it is written in a large number of bytes so that it can be decoded later.
Array.Resize(ref bytes, NumberOfBytes(bor.Size - 1));
bor.Add(stringToConvert[i]);
fs.Write(bytes);

// Going back to the last vertex
i--;
}
}

// The last bytes that are already in the dictionary and that are not written in the loop
var newbytes = BitConverter.GetBytes(bor.GetCode());
Array.Resize(ref newbytes, NumberOfBytes(bor.Size));
fs.Write(newbytes);
}

public static void DecompressFile(string pathToFile)
{

string fileName = Path.GetFileNameWithoutExtension(pathToFile);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
string fileName = Path.GetFileNameWithoutExtension(pathToFile);
{
string fileName = Path.GetFileNameWithoutExtension(pathToFile);

fileName = $"{pathToFile}..//..//{fileName}";

// Create file
using FileStream fs = new(fileName, FileMode.Create);

// Reading all bytes from a file
byte[] stringToConvert = File.ReadAllBytes(pathToFile);

// Dictionary for decoding
var dictionary = new Dictionary<int, byte[]>();

for (int i = 0; i < 256; i++)
{
var byteArray = BitConverter.GetBytes(i);
dictionary.Add(i, byteArray[0..1]);
}

int rightBorder = 0;
int leftBorder = 0;
bool flag = false;

while (leftBorder < stringToConvert.Length)
{
// The right border of the current subarray
rightBorder = leftBorder + NumberOfBytes(dictionary.Count - 1) - 1;

// If the right border has gone beyond the edge, then we will make it the last byte
rightBorder = rightBorder > stringToConvert.Length - 1 ? stringToConvert.Length - 1 : rightBorder;


if (leftBorder > stringToConvert.Length - 1)
Comment on lines +115 to +130
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rightBorder = rightBorder > stringToConvert.Length - 1 ? stringToConvert.Length - 1 : rightBorder;
if (leftBorder > stringToConvert.Length - 1)
rightBorder = rightBorder > stringToConvert.Length - 1 ? stringToConvert.Length - 1 : rightBorder;
if (leftBorder > stringToConvert.Length - 1)

Две пустые строки подряд не принято

{
break;
}

// Converting bytes to int
var bytes = new byte[4];
Array.Copy(stringToConvert, leftBorder, bytes, 0, NumberOfBytes(dictionary.Count - 1));
leftBorder = rightBorder + 1;
int answer = BitConverter.ToInt32(bytes);

// If it is the first number
if (!flag)
{
flag = true;
}

// Otherwise, we concatenate the last element with the first byte of the current one
else
{
var newArray = new byte[dictionary[dictionary.Count - 1].Length + 1];
Array.Copy(dictionary[dictionary.Count - 1], newArray, dictionary[dictionary.Count - 1].Length);
newArray[newArray.Length - 1] = dictionary[answer][0];
// Changing the value in the dictionary to the value we need
dictionary[dictionary.Count - 1] = newArray;
}

// Adding a new element to the dictionary
dictionary.Add(dictionary.Count, dictionary[answer]);
fs.Write(dictionary[answer]);
}
}
}
10 changes: 10 additions & 0 deletions Homework3/LZW/LZW/LZW.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
32 changes: 32 additions & 0 deletions Homework3/LZW/LZW/Solution.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace LZW;

public class Solution
{
static void Main(string[] args)
{
if (args.Length != 2)
{
throw new FileNotFoundException();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не, а кто его поймает, если исключение бросается прямо из Main- а? Тут надо вежливо поругаться и рассказать, какие параметры программа ожидает.

}

string pathToFile = args[0];

if (args[1] == "-c")
{
var file = new FileInfo(pathToFile);
long uncompressedFileSize = file.Length;
string fileName = Path.GetFileNameWithoutExtension(pathToFile);
fileName = $"{pathToFile}..\\..\\{fileName}.zipped";
LZW.CompressFile(pathToFile);
file = new FileInfo(fileName);
long compressedFileSize = file.Length;
Console.WriteLine((float)(uncompressedFileSize) / (float)compressedFileSize);
return;
}

if (args[1] == "-u")
{
LZW.DecompressFile(pathToFile);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если не -c и не -u, то надо что-то сказать пользователю

}
}
62 changes: 62 additions & 0 deletions Homework3/LZW/LZWTest/BorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace Test;

using NUnit.Framework;
using Bor;
using System;

public class BorTest
{
Bor bor = new();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Bor bor = new();
private Bor bor = new();


[SetUp]
public void Setup()
{
bor = new();
}

[Test]
public void ShouldExpectedTrueWhenContainsForBorWhereinCurrentNodeContainsThisNode()
{
bor.MoveIntoDesiredNode((byte)123);
bor.Add((byte)143);
bor.MoveIntoDesiredNode((byte)123);
Assert.IsTrue(bor.Contains((byte)143));
}

[Test]
public void ShouldExpectedFalseWhenContainsForBorWhereinCurrentNodeContainsThisNode()
{
bor.MoveIntoDesiredNode((byte)123);
Assert.IsFalse(bor.Contains((byte)143));
}

[Test]
public void ShouldBorSizeIncreaseByOneWhenAdd()
{
bor.MoveIntoDesiredNode((byte)123);
var size = bor.Size;
bor.Add((byte)12);
Assert.AreEqual(size + 1, bor.Size);
}

[Test]
public void ShouldWhenAddExistingNode()
{
Assert.Throws<ArgumentException>(() => bor.Add((byte)12));
}

[Test]
public void ShouldExpectedCurrentRootEqualRootWhenMoveToRoot()
{
bor.MoveIntoDesiredNode((byte)123);
bor.MovedToRoot();
Assert.AreEqual(-1, bor.GetCode());
}

[Test]
public void ShouldExpectedCurrentRootWhenMoveIntoDesiredNode()
{
bor.MoveIntoDesiredNode((byte)123);
Assert.AreEqual(123, bor.GetCode());
}
}
19 changes: 19 additions & 0 deletions Homework3/LZW/LZWTest/LZWTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Test;

using NUnit.Framework;
using System.IO;

public class LZWTests
{
[Test]
public void ShouldExpectedStringAreEqualWhenLZWForFile()
{
string filename = "..//..//..//Test.txt";
LZW.LZW.CompressFile(filename);
string newFilename = "..//..//..//Test.zipped";
LZW.LZW.DecompressFile(newFilename);
var firstString = File.ReadAllBytes(filename);
var secondString = File.ReadAllBytes("..//..//..//Test");
Assert.AreEqual(firstString, secondString);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На одном файле работает. А на пустом, на текстовом/бинарном?

}
Loading