diff --git a/TestTask/Extensions/CharInvariantCompareExtension.cs b/TestTask/Extensions/CharInvariantCompareExtension.cs
new file mode 100644
index 0000000..a81642c
--- /dev/null
+++ b/TestTask/Extensions/CharInvariantCompareExtension.cs
@@ -0,0 +1,28 @@
+namespace TestTask.Extensions;
+
+public static class CharInvariantCompareExtension
+{
+ ///
+ /// Проверка символов без учета регистра.
+ ///
+ /// Символ, относительно которого сравниваем.
+ /// Символ сравнения.
+ /// Учитывать ли регистр.
+ public static bool EqualsWithCaseOption(this char c1, char c2, bool ignoreCase = false)
+ {
+ if (ignoreCase)
+ return c1.ToLowerInvariant().Equals(c2.ToLowerInvariant());
+
+ return c1.Equals(c2);
+ }
+
+ ///
+ /// Приведение к нижнему регистру.
+ ///
+ /// Символ приведения.
+ ///
+ public static char ToLowerInvariant(this char c)
+ {
+ return char.ToLowerInvariant(c);
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Extensions/CharStreamExtension.cs b/TestTask/Extensions/CharStreamExtension.cs
new file mode 100644
index 0000000..6aa84f4
--- /dev/null
+++ b/TestTask/Extensions/CharStreamExtension.cs
@@ -0,0 +1,21 @@
+using TestTask.Interfaces;
+
+namespace TestTask.Extensions;
+
+internal static class CharStreamExtension
+{
+ ///
+ /// Проходит по потоку и находит первый символ являющийся буквой по Unicode.
+ ///
+ /// Поток текста.
+ /// Буква или символ новой строки(конец потока).
+ public static char GetNextLetter(this IReadOnlyStream stream)
+ {
+ char c = '\0';
+
+ while (!stream.IsEof && !char.IsLetter(c))
+ c = stream.ReadNextChar();
+
+ return c;
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Extensions/ConsonantVowelClassificator.cs b/TestTask/Extensions/ConsonantVowelClassificator.cs
new file mode 100644
index 0000000..7b22687
--- /dev/null
+++ b/TestTask/Extensions/ConsonantVowelClassificator.cs
@@ -0,0 +1,36 @@
+namespace TestTask.Extensions;
+///
+/// Классификатор гласных и согласных. Определяет русские и английские буквы. Откидывает мягкий и твердый знаки.
+///
+public static class ConsonantVowelClassificator
+{
+ private static readonly HashSet Vowels = new HashSet("аеёиоуыэюяАЕЁИОУЫЭЮЯaeiouAEIOU");
+ private static readonly HashSet UncategorizedChars = new HashSet("ьъЬЪ");
+
+ ///
+ /// Является ли гласной.
+ ///
+ /// Проверяемый символ
+ ///
+ public static bool IsVowel(char c)
+ {
+ if (!char.IsLetter(c))
+ return false;
+ if (UncategorizedChars.Contains(c))
+ return false;
+ return Vowels.Contains(c);
+ }
+ ///
+ /// Является ли согласной.
+ ///
+ /// Проверяемый символ
+ ///
+ public static bool IsConsonant(char c)
+ {
+ if (!char.IsLetter(c))
+ return false;
+ if (UncategorizedChars.Contains(c))
+ return false;
+ return !Vowels.Contains(c);
+ }
+}
\ No newline at end of file
diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/Interfaces/IReadOnlyStream.cs
similarity index 54%
rename from TestTask/IReadOnlyStream.cs
rename to TestTask/Interfaces/IReadOnlyStream.cs
index 6946745..d70d786 100644
--- a/TestTask/IReadOnlyStream.cs
+++ b/TestTask/Interfaces/IReadOnlyStream.cs
@@ -1,16 +1,15 @@
-namespace TestTask
-{
- ///
- /// Интерфейс для работы с файлом в сильно урезаном виде.
- /// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало.
- ///
- internal interface IReadOnlyStream
- {
- // TODO : Необходимо доработать данный интерфейс для обеспечения гарантированного закрытия файла, по окончанию работы с таковым!
- char ReadNextChar();
-
- void ResetPositionToStart();
-
- bool IsEof { get; }
- }
-}
+namespace TestTask.Interfaces
+{
+ ///
+ /// Интерфейс для работы с файлом в сильно урезаном виде.
+ /// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало.
+ ///
+ public interface IReadOnlyStream : IDisposable
+ {
+ char ReadNextChar();
+
+ void ResetPositionToStart();
+
+ bool IsEof { get; }
+ }
+}
diff --git a/TestTask/LetterStats.cs b/TestTask/LetterStats.cs
deleted file mode 100644
index aa10728..0000000
--- a/TestTask/LetterStats.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace TestTask
-{
- ///
- /// Статистика вхождения буквы/пары букв
- ///
- public struct LetterStats
- {
- ///
- /// Буква/Пара букв для учёта статистики.
- ///
- public string Letter;
-
- ///
- /// Кол-во вхождений буквы/пары.
- ///
- public int Count;
- }
-}
diff --git a/TestTask/CharType.cs b/TestTask/Models/CharType.cs
similarity index 87%
rename from TestTask/CharType.cs
rename to TestTask/Models/CharType.cs
index 899564d..82b3cb3 100644
--- a/TestTask/CharType.cs
+++ b/TestTask/Models/CharType.cs
@@ -1,18 +1,18 @@
-namespace TestTask
-{
- ///
- /// Тип букв
- ///
- public enum CharType
- {
- ///
- /// Гласные
- ///
- Vowel,
-
- ///
- /// Согласные
- ///
- Consonants
- }
-}
+namespace TestTask.Models
+{
+ ///
+ /// Тип букв
+ ///
+ public enum CharType
+ {
+ ///
+ /// Гласные
+ ///
+ Vowel,
+
+ ///
+ /// Согласные
+ ///
+ Consonants
+ }
+}
diff --git a/TestTask/Models/LetterStats.cs b/TestTask/Models/LetterStats.cs
new file mode 100644
index 0000000..69f97f9
--- /dev/null
+++ b/TestTask/Models/LetterStats.cs
@@ -0,0 +1,7 @@
+namespace TestTask.Models
+{
+ ///
+ /// Статистика вхождения буквы/пары букв
+ ///
+ public record struct LetterStats (string Letter, int Count = 0);
+}
diff --git a/TestTask/Program.cs b/TestTask/Program.cs
index fdf048e..3c4b849 100644
--- a/TestTask/Program.cs
+++ b/TestTask/Program.cs
@@ -1,11 +1,15 @@
using System;
using System.Collections.Generic;
+using TestTask.Extensions;
+using TestTask.Interfaces;
+using TestTask.Models;
+using TestTask.Services;
+using TestTask.Services.LetterStrategy;
namespace TestTask
{
- public class Program
+ internal static class Program
{
-
///
/// Программа принимает на входе 2 пути до файлов.
/// Анализирует в первом файле кол-во вхождений каждой буквы (регистрозависимо). Например А, б, Б, Г и т.д.
@@ -16,70 +20,44 @@ public class Program
/// Второй параметр - путь до второго файла.
static void Main(string[] args)
{
- IReadOnlyStream inputStream1 = GetInputStream(args[0]);
- IReadOnlyStream inputStream2 = GetInputStream(args[1]);
-
- IList singleLetterStats = FillSingleLetterStats(inputStream1);
- IList doubleLetterStats = FillDoubleLetterStats(inputStream2);
-
- RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
- RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);
-
- PrintStatistic(singleLetterStats);
- PrintStatistic(doubleLetterStats);
-
- // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы.
- }
-
- ///
- /// Ф-ция возвращает экземпляр потока с уже загруженным файлом для последующего посимвольного чтения.
- ///
- /// Полный путь до файла для чтения
- /// Поток для последующего чтения.
- private static IReadOnlyStream GetInputStream(string fileFullPath)
- {
- return new ReadOnlyStream(fileFullPath);
- }
-
- ///
- /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения каждой буквы.
- /// Статистика РЕГИСТРОЗАВИСИМАЯ!
- ///
- /// Стрим для считывания символов для последующего анализа
- /// Коллекция статистик по каждой букве, что была прочитана из стрима.
- private static IList FillSingleLetterStats(IReadOnlyStream stream)
- {
- stream.ResetPositionToStart();
- while (!stream.IsEof)
+ if (args.Length < 2)
{
- char c = stream.ReadNextChar();
- // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый.
+ Console.WriteLine("Использование: TestTask <путь_к_файлу1> <путь_к_файлу2>");
+ return;
}
- //return ???;
-
- throw new NotImplementedException();
- }
-
- ///
- /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения парных букв.
- /// В статистику должны попадать только пары из одинаковых букв, например АА, СС, УУ, ЕЕ и т.д.
- /// Статистика - НЕ регистрозависимая!
- ///
- /// Стрим для считывания символов для последующего анализа
- /// Коллекция статистик по каждой букве, что была прочитана из стрима.
- private static IList FillDoubleLetterStats(IReadOnlyStream stream)
- {
- stream.ResetPositionToStart();
- while (!stream.IsEof)
+ try
{
- char c = stream.ReadNextChar();
- // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый.
+ IList singleLetterStats;
+ IList doubleLetterStats;
+
+ using (IReadOnlyStream inputStream1 = new ReadOnlyStream(args[0]))
+ {
+ singleLetterStats = LetterStatsService.FillSingleLetterStats(inputStream1);
+ }
+ using (IReadOnlyStream inputStream2 = new ReadOnlyStream(args[1]))
+ {
+ doubleLetterStats = LetterStatsService.FillDoubleLetterStats(inputStream2);
+ }
+
+ RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
+ RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);
+
+ LetterStatisticsPrintHelper.PrintStatisticSorted(singleLetterStats);
+ LetterStatisticsPrintHelper.PrintStatisticSorted(doubleLetterStats);
+ }
+ catch (FileNotFoundException e)
+ {
+ Console.WriteLine($"Файл не найден: {e.FileName}");
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Ошибка: {e.Message}");
+ }
+ finally
+ {
+ Console.ReadKey();
}
-
- //return ???;
-
- throw new NotImplementedException();
}
///
@@ -91,39 +69,9 @@ private static IList FillDoubleLetterStats(IReadOnlyStream stream)
/// Тип букв для анализа
private static void RemoveCharStatsByType(IList letters, CharType charType)
{
- // TODO : Удалить статистику по запрошенному типу букв.
- switch (charType)
- {
- case CharType.Consonants:
- break;
- case CharType.Vowel:
- break;
- }
-
+ var strategy = CharFilterStrategyFactory.GetStrategy(charType);
+ letters.RemoveElementsByStrategy(strategy);
}
- ///
- /// Ф-ция выводит на экран полученную статистику в формате "{Буква} : {Кол-во}"
- /// Каждая буква - с новой строки.
- /// Выводить на экран необходимо предварительно отсортировав набор по алфавиту.
- /// В конце отдельная строчка с ИТОГО, содержащая в себе общее кол-во найденных букв/пар
- ///
- /// Коллекция со статистикой
- private static void PrintStatistic(IEnumerable letters)
- {
- // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту!
- throw new NotImplementedException();
- }
-
- ///
- /// Метод увеличивает счётчик вхождений по переданной структуре.
- ///
- ///
- private static void IncStatistic(LetterStats letterStats)
- {
- letterStats.Count++;
- }
-
-
}
}
diff --git a/TestTask/Properties/AssemblyInfo.cs b/TestTask/Properties/AssemblyInfo.cs
deleted file mode 100644
index aecb854..0000000
--- a/TestTask/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("TestTask")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("TestTask")]
-[assembly: AssemblyCopyright("Copyright © 2018")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("6e9922f2-af75-4807-b6e4-f74499317c39")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TestTask/Services/LetterStatisticsPrintHelper.cs b/TestTask/Services/LetterStatisticsPrintHelper.cs
new file mode 100644
index 0000000..b486499
--- /dev/null
+++ b/TestTask/Services/LetterStatisticsPrintHelper.cs
@@ -0,0 +1,26 @@
+using TestTask.Models;
+
+namespace TestTask.Services;
+
+public static class LetterStatisticsPrintHelper
+{
+ ///
+ /// Ф-ция выводит на экран полученную статистику в формате "{Буква/пара} : {Кол-во}"
+ /// Каждая буква/пара - с новой строки.
+ /// Выводить на экран необходимо предварительно отсортировав набор по алфавиту.
+ /// В конце отдельная строчка с ИТОГО, содержащая в себе общее кол-во найденных букв/пар
+ ///
+ /// Коллекция со статистикой
+ public static void PrintStatisticSorted(IList letters)
+ {
+ var sorted = letters.OrderBy(x => x.Letter.ToLowerInvariant());
+ int total = 0;
+ foreach (var letter in sorted)
+ {
+ Console.WriteLine($"{letter.Letter} : {letter.Count}");
+ total += letter.Count;
+ }
+
+ Console.WriteLine($"ИТОГО : {total}");
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Services/LetterStatsService.cs b/TestTask/Services/LetterStatsService.cs
new file mode 100644
index 0000000..502e4fd
--- /dev/null
+++ b/TestTask/Services/LetterStatsService.cs
@@ -0,0 +1,82 @@
+using TestTask.Extensions;
+using TestTask.Interfaces;
+using TestTask.Models;
+
+namespace TestTask.Services;
+
+///
+/// Сервис для сбора статистики букв из потока.
+///
+public static class LetterStatsService
+{
+ ///
+ /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения каждой буквы.
+ /// Статистика РЕГИСТРОЗАВИСИМАЯ!
+ ///
+ /// Стрим для считывания символов для последующего анализа
+ /// Коллекция статистик по каждой букве, что была прочитана из стрима.
+ public static IList FillSingleLetterStats(IReadOnlyStream stream)
+ {
+ stream.ResetPositionToStart();
+ var stats = new Dictionary();
+
+ while (!stream.IsEof)
+ {
+ char c = stream.GetNextLetter();
+ if (!char.IsLetter(c)) break;
+
+ stats[c] = stats.GetValueOrDefault(c) + 1;
+ }
+
+ return stats
+ .Select(s => new LetterStats(s.Key.ToString(), s.Value))
+ .ToList();
+ }
+
+ ///
+ /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения парных букв.
+ /// В статистику должны попадать только пары из одинаковых букв, например АА, сС, УУ, ee и т.д.
+ /// Статистика - НЕ регистрозависимая!
+ ///
+ /// Стрим для считывания символов для последующего анализа
+ /// Коллекция статистик по каждой букве, что была прочитана из стрима.
+ public static IList FillDoubleLetterStats(IReadOnlyStream stream)
+ {
+ stream.ResetPositionToStart();
+ var stats = new Dictionary();
+
+ if (stream.IsEof)
+ return new List();
+
+ char prevChar = stream.GetNextLetter();
+
+ if (!char.IsLetter(prevChar))
+ return new List();
+
+ while (!stream.IsEof )
+ {
+ char c = stream.ReadNextChar();
+
+ if (!char.IsLetter(c))
+ {
+ prevChar = '\0';
+ continue;
+ }
+
+ if (!prevChar.EqualsWithCaseOption(c, true))
+ {
+ prevChar = c;
+ continue;
+ }
+
+ var combination = new string(new[] { prevChar.ToLowerInvariant(), c.ToLowerInvariant() });
+
+ stats[combination] = stats.GetValueOrDefault(combination) + 1;
+ prevChar = c;
+ }
+
+ return stats
+ .Select(s => new LetterStats(s.Key, s.Value))
+ .ToList();
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Services/LetterStrategy/CharFilterStrategyFactory.cs b/TestTask/Services/LetterStrategy/CharFilterStrategyFactory.cs
new file mode 100644
index 0000000..8b38a76
--- /dev/null
+++ b/TestTask/Services/LetterStrategy/CharFilterStrategyFactory.cs
@@ -0,0 +1,26 @@
+using TestTask.Models;
+
+namespace TestTask.Services.LetterStrategy;
+
+public static class CharFilterStrategyFactory
+{
+ private static readonly Dictionary Strategies = new()
+ {
+ { CharType.Vowel, new VowelFilterStrategy() },
+ { CharType.Consonants, new ConsonantFilterStrategy() },
+ };
+
+ ///
+ /// Возвращает стратегию фильтрации для указанного типа символов.
+ ///
+ /// Тип символов.
+ /// Соответствующая стратегия.
+ /// Если тип не поддерживается.
+ public static ICharFilterStrategy GetStrategy(CharType charType)
+ {
+ if (!Strategies.TryGetValue(charType, out var strategy))
+ throw new ArgumentOutOfRangeException(nameof(charType), $"Неизвестный тип: {charType}");
+
+ return strategy;
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Services/LetterStrategy/ConsonantFilterStrategy.cs b/TestTask/Services/LetterStrategy/ConsonantFilterStrategy.cs
new file mode 100644
index 0000000..be4db7f
--- /dev/null
+++ b/TestTask/Services/LetterStrategy/ConsonantFilterStrategy.cs
@@ -0,0 +1,15 @@
+using TestTask.Extensions;
+
+namespace TestTask.Services.LetterStrategy;
+
+///
+/// Стратегия удаления согласных букв из статистики.
+///
+public class ConsonantFilterStrategy : ICharFilterStrategy
+{
+ public bool ShouldRemove(string letter)
+ {
+ if (string.IsNullOrEmpty(letter)) return false;
+ return ConsonantVowelClassificator.IsConsonant(letter[0]);
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Services/LetterStrategy/ICharFilterStrategy.cs b/TestTask/Services/LetterStrategy/ICharFilterStrategy.cs
new file mode 100644
index 0000000..a8a9402
--- /dev/null
+++ b/TestTask/Services/LetterStrategy/ICharFilterStrategy.cs
@@ -0,0 +1,14 @@
+namespace TestTask.Services.LetterStrategy;
+
+///
+/// Стратегия выбора фильтров букв из статистики
+///
+public interface ICharFilterStrategy
+{
+ ///
+ /// Определяет, надо ли убрать букву/пару из статистики
+ ///
+ /// Буква или пара букв для проверки
+ /// true - если надо удалить
+ bool ShouldRemove(string letter);
+}
\ No newline at end of file
diff --git a/TestTask/Services/LetterStrategy/VowelFilterStrategy.cs b/TestTask/Services/LetterStrategy/VowelFilterStrategy.cs
new file mode 100644
index 0000000..fc5d402
--- /dev/null
+++ b/TestTask/Services/LetterStrategy/VowelFilterStrategy.cs
@@ -0,0 +1,15 @@
+using TestTask.Extensions;
+
+namespace TestTask.Services.LetterStrategy;
+
+///
+/// Стратегия удаления гласных букв из статистики.
+///
+public class VowelFilterStrategy : ICharFilterStrategy
+{
+ public bool ShouldRemove(string letter)
+ {
+ if (string.IsNullOrEmpty(letter)) return false;
+ return ConsonantVowelClassificator.IsVowel(letter[0]);
+ }
+}
\ No newline at end of file
diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/Services/ReadOnlyStream.cs
similarity index 53%
rename from TestTask/ReadOnlyStream.cs
rename to TestTask/Services/ReadOnlyStream.cs
index a51a61e..568402a 100644
--- a/TestTask/ReadOnlyStream.cs
+++ b/TestTask/Services/ReadOnlyStream.cs
@@ -1,60 +1,59 @@
-using System;
-using System.IO;
-
-namespace TestTask
-{
- public class ReadOnlyStream : IReadOnlyStream
- {
- private Stream _localStream;
-
- ///
- /// Конструктор класса.
- /// Т.к. происходит прямая работа с файлом, необходимо
- /// обеспечить ГАРАНТИРОВАННОЕ закрытие файла после окончания работы с таковым!
- ///
- /// Полный путь до файла для чтения
- public ReadOnlyStream(string fileFullPath)
- {
- IsEof = true;
-
- // TODO : Заменить на создание реального стрима для чтения файла!
- _localStream = null;
- }
-
- ///
- /// Флаг окончания файла.
- ///
- public bool IsEof
- {
- get; // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении
- private set;
- }
-
- ///
- /// Ф-ция чтения следующего символа из потока.
- /// Если произведена попытка прочитать символ после достижения конца файла, метод
- /// должен бросать соответствующее исключение
- ///
- /// Считанный символ.
- public char ReadNextChar()
- {
- // TODO : Необходимо считать очередной символ из _localStream
- throw new NotImplementedException();
- }
-
- ///
- /// Сбрасывает текущую позицию потока на начало.
- ///
- public void ResetPositionToStart()
- {
- if (_localStream == null)
- {
- IsEof = true;
- return;
- }
-
- _localStream.Position = 0;
- IsEof = false;
- }
- }
-}
+using System.Text;
+using TestTask.Interfaces;
+
+namespace TestTask.Services
+{
+ public sealed class ReadOnlyStream : IReadOnlyStream
+ {
+ private StreamReader _localStream;
+
+ ///
+ /// Конструктор класса.
+ ///
+ /// Полный путь до файла для чтения
+ public ReadOnlyStream(string fileFullPath)
+ {
+ var fileStream = File.Open(fileFullPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ _localStream = new StreamReader(fileStream, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
+ }
+
+ ///
+ /// Флаг окончания файла.
+ ///
+ public bool IsEof => _localStream == null || _localStream.EndOfStream;
+
+ ///
+ /// Ф-ция чтения следующего символа из потока.
+ /// Если произведена попытка прочитать символ после достижения конца файла, метод
+ /// должен бросать соответствующее исключение
+ ///
+ /// Считанный символ.
+ public char ReadNextChar()
+ {
+ if(IsEof)
+ throw new EndOfStreamException();
+
+ return (char)_localStream.Read();
+ }
+
+ ///
+ /// Сбрасывает текущую позицию потока на начало.
+ ///
+ public void ResetPositionToStart()
+ {
+ if (_localStream == null)
+ {
+ return;
+ }
+
+ _localStream.DiscardBufferedData();
+ _localStream.BaseStream.Position = 0;
+ }
+
+ public void Dispose()
+ {
+ _localStream?.Dispose();
+ _localStream = null;
+ }
+ }
+}
diff --git a/TestTask/Services/StatisticRemover.cs b/TestTask/Services/StatisticRemover.cs
new file mode 100644
index 0000000..dd7f326
--- /dev/null
+++ b/TestTask/Services/StatisticRemover.cs
@@ -0,0 +1,25 @@
+using TestTask.Models;
+using TestTask.Services.LetterStrategy;
+
+namespace TestTask.Services;
+
+public static class LetterStatsExtension
+{
+ ///
+ /// Удалить записи по типу стратегии.
+ ///
+ /// Список статистики.
+ /// Тип стратегии.
+ public static void RemoveElementsByStrategy(this IList letterStats, ICharFilterStrategy strategy)
+ {
+ for(int i = 0; i < letterStats.Count; i++)
+ {
+ if(!strategy.ShouldRemove(letterStats[i].Letter))
+ {
+ continue;
+ }
+ letterStats.RemoveAt(i);
+ i--;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj
index dc470b0..2da0747 100644
--- a/TestTask/TestTask.csproj
+++ b/TestTask/TestTask.csproj
@@ -1,53 +1,12 @@
-
-
-
-
- Debug
- AnyCPU
- {6E9922F2-AF75-4807-B6E4-F74499317C39}
- Exe
- TestTask
- TestTask
- v4.6.1
- 512
- true
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Exe
+ net8.0
+ latest
+ enable
+ enable
+ true
+ true
+
\ No newline at end of file