diff --git a/.gitignore b/.gitignore
index 42d3208..173c19b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,3 @@
-## .gitignore
-#.gitignore
-
-###
-## *.html
-###
-## !special.html
-### ..
-## *.[ao]
-
-
*.suo
*.user
.vs/
diff --git a/TestTask/CharType.cs b/TestTask/CharType.cs
deleted file mode 100644
index 899564d..0000000
--- a/TestTask/CharType.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace TestTask
-{
- ///
- /// Тип букв
- ///
- public enum CharType
- {
- ///
- /// Гласные
- ///
- Vowel,
-
- ///
- /// Согласные
- ///
- Consonants
- }
-}
diff --git a/TestTask/Enums/CharType.cs b/TestTask/Enums/CharType.cs
new file mode 100644
index 0000000..c1f896a
--- /dev/null
+++ b/TestTask/Enums/CharType.cs
@@ -0,0 +1,18 @@
+namespace TestTask.Enums
+{
+ ///
+ /// Тип букв
+ ///
+ public enum CharType
+ {
+ ///
+ /// Гласные
+ ///
+ Vowel,
+
+ ///
+ /// Согласные
+ ///
+ Consonants
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Helpers/Extensions/CharExtension.cs b/TestTask/Helpers/Extensions/CharExtension.cs
new file mode 100644
index 0000000..3ca4ae0
--- /dev/null
+++ b/TestTask/Helpers/Extensions/CharExtension.cs
@@ -0,0 +1,74 @@
+namespace TestTask.Helpers.Extensions
+{
+ ///
+ /// Расширение для char.
+ ///
+ internal static class CharExtension
+ {
+ #region Public Methods
+
+ ///
+ /// Проверяет символ английская ли глассная буква?
+ ///
+ /// Входной символ.
+ /// Английская глассная буква?
+ public static bool IsEnglishVowel(this char value)
+ {
+ value = char.ToLowerInvariant(value);
+
+ switch (value)
+ {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Проверяет символ русская ли глассная буква?
+ ///
+ /// Входной символ.
+ /// Русская глассная буква?
+ public static bool IsRussianVowel(this char value)
+ {
+ value = char.ToLowerInvariant(value);
+
+ switch (value)
+ {
+ case 'а':
+ case 'е':
+ case 'ё':
+ case 'и':
+ case 'о':
+ case 'у':
+ case 'ы':
+ case 'э':
+ case 'ю':
+ case 'я':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Проверяет символ глассная ли буква?
+ ///
+ /// Входной символ.
+ /// Глассная буква?
+ /// Проверка русского и английского алфовита.
+ public static bool IsVowel(this char value)
+ {
+ return IsRussianVowel(value) || IsEnglishVowel(value);
+ }
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Helpers/FillLetterStatsHelper.cs b/TestTask/Helpers/FillLetterStatsHelper.cs
new file mode 100644
index 0000000..f822aea
--- /dev/null
+++ b/TestTask/Helpers/FillLetterStatsHelper.cs
@@ -0,0 +1,99 @@
+namespace TestTask.Helpers
+{
+ using System.Collections.Generic;
+ using TestTask.Streams.Interfaces;
+ using TestTask.Structs;
+
+ ///
+ /// Хелпер для составления статистики букв.
+ ///
+ internal static class FillLetterStatsHelper
+ {
+ #region Public Methods
+
+ ///
+ /// Считывает из входящего потока все буквы, и возвращающает коллекцию статистик вхождения парных букв.
+ ///
+ /// Стрим для считывания символов для последующего анализа
+ /// Коллекция статистик по каждой букве, что была прочитана из стрима.
+ ///
+ /// В статистику должны попадать только пары из одинаковых букв, например АА, СС, УУ, ЕЕ и т.д.
+ /// Статистика - НЕ регистрозависимая!
+ ///
+ public static Dictionary FillDoubleLetterStats(IReadOnlyStream stream)
+ {
+ stream.ResetPositionToStart();
+
+ var statsByLetter = new Dictionary();
+ char? prevLetter = null;
+
+ while (!stream.IsEof)
+ {
+ char originalLetter = stream.ReadNextChar();
+
+ // В статистику идут только буквы
+ if (!char.IsLetter(originalLetter))
+ {
+ prevLetter = null;
+
+ continue;
+ }
+
+ char letter = char.ToUpper(originalLetter); // НЕ регистрозависимо
+
+ // Если текущая буква равна предыдущей — это парная буква (AA, CC, ...)
+ if (prevLetter.HasValue && prevLetter.Value == letter)
+ {
+ // ВАЖНО: инкрементим статистику именно по букве
+ if (statsByLetter.TryGetValue(letter, out LetterStats stats))
+ {
+ stats.Count++;
+ statsByLetter[letter] = stats; // обязательно вернуть обратно
+ }
+ else
+ {
+ statsByLetter[letter] = new LetterStats { Count = 1, Letter = $"{letter}{letter}" };
+ }
+ }
+
+ prevLetter = letter;
+ }
+
+ return statsByLetter;
+ }
+
+ ///
+ /// Считывает из входящего потока все буквы, и возвращающает коллекцию статистик вхождения каждой буквы.
+ ///
+ /// Стрим для считывания символов для последующего анализа
+ /// Коллекция статистик по каждой букве, что была прочитана из стрима.
+ /// Статистика РЕГИСТРОЗАВИСИМАЯ!
+ public static Dictionary FillSingleLetterStats(IReadOnlyStream stream)
+ {
+ stream.ResetPositionToStart();
+ Dictionary statsByLetter = new Dictionary();
+
+ while (!stream.IsEof)
+ {
+ char letter = stream.ReadNextChar();
+
+ if (char.IsLetter(letter))
+ {
+ if (statsByLetter.TryGetValue(letter, out LetterStats stats))
+ {
+ stats.Count++;
+ statsByLetter[letter] = stats; // обязательно вернуть обратно
+ }
+ else
+ {
+ statsByLetter[letter] = new LetterStats { Count = 1, Letter = letter.ToString() };
+ }
+ }
+ }
+
+ return statsByLetter;
+ }
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Helpers/FilterCharStats.cs b/TestTask/Helpers/FilterCharStats.cs
new file mode 100644
index 0000000..132b31e
--- /dev/null
+++ b/TestTask/Helpers/FilterCharStats.cs
@@ -0,0 +1,47 @@
+namespace TestTask.Helpers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using TestTask.Enums;
+ using TestTask.Helpers.Extensions;
+ using TestTask.Structs;
+
+ ///
+ /// Класс для фильтрации статистики букв.
+ ///
+ internal static class FilterCharStats
+ {
+ ///
+ /// Фильтруются все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы.
+ /// (Тип букв для перебора определяется параметром charType)
+ /// Все найденные буквы/пары соответствующие параметру поиска - удаляются из переданной коллекции статистик.
+ ///
+ /// Коллекция со статистиками вхождения букв/пар
+ /// Тип букв для анализа
+ public static void RemoveCharStatsByType(Dictionary letters, CharType charType)
+ {
+ List list = letters.Select(x => x.Key).ToList();
+
+ foreach (char value in list)
+ {
+ switch (charType)
+ {
+ case CharType.Consonants:
+
+ if (!value.IsVowel())
+ {
+ letters.Remove(value);
+ }
+ break;
+
+ case CharType.Vowel:
+ if (value.IsVowel())
+ {
+ letters.Remove(value);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/IReadOnlyStream.cs
deleted file mode 100644
index 6946745..0000000
--- a/TestTask/IReadOnlyStream.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace TestTask
-{
- ///
- /// Интерфейс для работы с файлом в сильно урезаном виде.
- /// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало.
- ///
- internal interface IReadOnlyStream
- {
- // TODO : Необходимо доработать данный интерфейс для обеспечения гарантированного закрытия файла, по окончанию работы с таковым!
- 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/Program.cs b/TestTask/Program.cs
index fdf048e..6897b46 100644
--- a/TestTask/Program.cs
+++ b/TestTask/Program.cs
@@ -1,129 +1,90 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using TestTask.Enums;
+using TestTask.Helpers;
+using TestTask.Streams;
+using TestTask.Streams.Interfaces;
+using TestTask.Structs;
namespace TestTask
{
- public class Program
- {
-
- ///
- /// Программа принимает на входе 2 пути до файлов.
- /// Анализирует в первом файле кол-во вхождений каждой буквы (регистрозависимо). Например А, б, Б, Г и т.д.
- /// Анализирует во втором файле кол-во вхождений парных букв (не регистрозависимо). Например АА, Оо, еЕ, тт и т.д.
- /// По окончанию работы - выводит данную статистику на экран.
- ///
- /// Первый параметр - путь до первого файла.
- /// Второй параметр - путь до второго файла.
- 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)
- {
- char c = stream.ReadNextChar();
- // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый.
- }
-
- //return ???;
-
- throw new NotImplementedException();
- }
-
- ///
- /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения парных букв.
- /// В статистику должны попадать только пары из одинаковых букв, например АА, СС, УУ, ЕЕ и т.д.
- /// Статистика - НЕ регистрозависимая!
- ///
- /// Стрим для считывания символов для последующего анализа
- /// Коллекция статистик по каждой букве, что была прочитана из стрима.
- private static IList FillDoubleLetterStats(IReadOnlyStream stream)
- {
- stream.ResetPositionToStart();
- while (!stream.IsEof)
- {
- char c = stream.ReadNextChar();
- // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый.
- }
-
- //return ???;
-
- throw new NotImplementedException();
- }
-
- ///
- /// Ф-ция перебирает все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы.
- /// (Тип букв для перебора определяется параметром charType)
- /// Все найденные буквы/пары соответствующие параметру поиска - удаляются из переданной коллекции статистик.
- ///
- /// Коллекция со статистиками вхождения букв/пар
- /// Тип букв для анализа
- private static void RemoveCharStatsByType(IList letters, CharType charType)
- {
- // TODO : Удалить статистику по запрошенному типу букв.
- switch (charType)
- {
- case CharType.Consonants:
- break;
- case CharType.Vowel:
- break;
- }
-
- }
-
- ///
- /// Ф-ция выводит на экран полученную статистику в формате "{Буква} : {Кол-во}"
- /// Каждая буква - с новой строки.
- /// Выводить на экран необходимо предварительно отсортировав набор по алфавиту.
- /// В конце отдельная строчка с ИТОГО, содержащая в себе общее кол-во найденных букв/пар
- ///
- /// Коллекция со статистикой
- private static void PrintStatistic(IEnumerable letters)
- {
- // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту!
- throw new NotImplementedException();
- }
-
- ///
- /// Метод увеличивает счётчик вхождений по переданной структуре.
- ///
- ///
- private static void IncStatistic(LetterStats letterStats)
- {
- letterStats.Count++;
- }
-
-
- }
-}
+ ///
+ /// Корневой класс программы.
+ ///
+ public class Program
+ {
+ ///
+ /// Программа принимает на входе 2 пути до файлов.
+ /// Анализирует в первом файле кол-во вхождений каждой буквы (регистрозависимо). Например А, б, Б, Г и т.д.
+ /// Анализирует во втором файле кол-во вхождений парных букв (не регистрозависимо). Например АА, Оо, еЕ, тт и т.д.
+ /// По окончанию работы - выводит данную статистику на экран.
+ ///
+ /// Первый параметр - путь до первого файла.
+ /// Второй параметр - путь до второго файла.
+ private static void Main(string[] args)
+ {
+ try
+ {
+ if (args.Length != 2)
+ {
+ throw new ArgumentException("Входных аргументов должно быть два ", nameof(args));
+ }
+
+ Dictionary singleLetterStats = null;
+ Dictionary doubleLetterStats = null;
+
+ using (IReadOnlyStream inputStream1 = new ReadOnlyStream(args[0]))
+ {
+ singleLetterStats = FillLetterStatsHelper.FillSingleLetterStats(inputStream1);
+ }
+ using (IReadOnlyStream inputStream2 = new ReadOnlyStream(args[1]))
+ {
+ doubleLetterStats = FillLetterStatsHelper.FillDoubleLetterStats(inputStream2);
+ }
+
+ FilterCharStats.RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
+ FilterCharStats.RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);
+
+ PrintStatistic(singleLetterStats);
+ PrintStatistic(doubleLetterStats);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.Message);
+ }
+ finally
+ {
+ Console.ReadKey();
+ }
+ }
+
+ ///
+ /// Выводит на экран полученную статистику в формате "{Буква} : {Кол-во}"
+ /// Каждая буква - с новой строки.
+ /// Выводить на экран необходимо предварительно отсортировав набор по алфавиту.
+ /// В конце отдельная строчка с ИТОГО, содержащая в себе общее кол-во найденных букв/пар
+ ///
+ /// Коллекция со статистикой
+ private static void PrintStatistic(Dictionary letters)
+ {
+ // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту!
+ IEnumerable> orderCollection = letters.OrderBy(x => x.Key).Select(x => x);
+ int uniqueCount = 0;
+ int generalCount = 0;
+
+ foreach (KeyValuePair item in orderCollection)
+ {
+ Console.WriteLine(string.Format("{0} : {1}", item.Value.Letter, item.Value.Count));
+
+ uniqueCount++;
+ generalCount += item.Value.Count;
+ }
+
+ //По задаче не понял что имелось в виду под ИТОГО.
+ Console.WriteLine("ИТОГО уникальных: " + uniqueCount);
+ Console.WriteLine("ИТОГО сумма: " + generalCount);
+ Console.WriteLine();
+ }
+ }
+}
\ No newline at end of file
diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs
deleted file mode 100644
index a51a61e..0000000
--- a/TestTask/ReadOnlyStream.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-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;
- }
- }
-}
diff --git a/TestTask/Streams/Interfaces/IReadOnlyStream.cs b/TestTask/Streams/Interfaces/IReadOnlyStream.cs
new file mode 100644
index 0000000..fdc33b1
--- /dev/null
+++ b/TestTask/Streams/Interfaces/IReadOnlyStream.cs
@@ -0,0 +1,26 @@
+namespace TestTask.Streams.Interfaces
+{
+ using System;
+
+ ///
+ /// Интерфейс для работы с файлом в сильно урезаном виде.
+ ///
+ internal interface IReadOnlyStream : IDisposable
+ {
+ ///
+ /// Читает следующий символ из потока.
+ ///
+ /// Считанный символ.
+ char ReadNextChar();
+
+ ///
+ /// Сбрасывает текущую позицию потока на начало.
+ ///
+ void ResetPositionToStart();
+
+ ///
+ /// Флаг окончания файла.
+ ///
+ bool IsEof { get; }
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Streams/ReadOnlyStream.cs b/TestTask/Streams/ReadOnlyStream.cs
new file mode 100644
index 0000000..5523baf
--- /dev/null
+++ b/TestTask/Streams/ReadOnlyStream.cs
@@ -0,0 +1,89 @@
+using System.IO;
+using System.Text;
+using TestTask.Streams.Interfaces;
+
+namespace TestTask.Streams
+{
+ ///
+ /// Кастомный класс потока для чтения текстовых файлов.
+ ///
+ public class ReadOnlyStream : IReadOnlyStream
+ {
+ #region Fields
+
+ ///
+ /// Поток для чтения файла.
+ ///
+ private StreamReader _streamReader;
+
+ #endregion Fields
+
+ #region Properties
+
+ ///
+ public bool IsEof
+ {
+ get;
+ private set;
+ }
+
+ #endregion Properties
+
+ #region Public Constructors
+
+ ///
+ /// Конструктор класса.
+ ///
+ /// Полный путь до файла для чтения
+ public ReadOnlyStream(string fileFullPath) : this(fileFullPath, Encoding.UTF8)
+ {
+ }
+
+ ///
+ /// Конструктор класса.
+ ///
+ /// Полный путь до файла для чтения.
+ /// Кодировка.
+ public ReadOnlyStream(string fileFullPath, Encoding encoding)
+ {
+ var localStream = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read);
+ _streamReader = new StreamReader(localStream, encoding);
+ }
+
+ #endregion Public Constructors
+
+ #region Public Methods
+
+ ///
+ public void Dispose()
+ {
+ _streamReader?.Dispose();
+ _streamReader = null;
+ }
+
+ ///
+ public char ReadNextChar()
+ {
+ char letter = (char)_streamReader.Read();
+
+ if (_streamReader.EndOfStream)
+ IsEof = true;
+
+ return letter;
+ }
+
+ ///
+ public void ResetPositionToStart()
+ {
+ if (_streamReader != null)
+ {
+ _streamReader.DiscardBufferedData();
+ _streamReader.BaseStream.Position = 0;
+ }
+
+ IsEof = false;
+ }
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/TestTask/Structs/LetterStats.cs b/TestTask/Structs/LetterStats.cs
new file mode 100644
index 0000000..4c666d0
--- /dev/null
+++ b/TestTask/Structs/LetterStats.cs
@@ -0,0 +1,18 @@
+namespace TestTask.Structs
+{
+ ///
+ /// Статистика вхождения буквы/пары букв
+ ///
+ public struct LetterStats
+ {
+ ///
+ /// Буква/Пара букв для учёта статистики.
+ ///
+ public string Letter { get; set; }
+
+ ///
+ /// Кол-во вхождений буквы/пары.
+ ///
+ public int Count { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj
index dc470b0..765e16f 100644
--- a/TestTask/TestTask.csproj
+++ b/TestTask/TestTask.csproj
@@ -8,9 +8,10 @@
Exe
TestTask
TestTask
- v4.6.1
+ v4.8
512
true
+
AnyCPU
@@ -42,12 +43,15 @@
-
-
-
+
+
+
+
+
+
-
+
\ No newline at end of file