diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/IReadOnlyStream.cs index 6946745..db8764d 100644 --- a/TestTask/IReadOnlyStream.cs +++ b/TestTask/IReadOnlyStream.cs @@ -1,12 +1,13 @@ -namespace TestTask +using System; + +namespace TestTask { /// /// Интерфейс для работы с файлом в сильно урезаном виде. /// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало. /// - internal interface IReadOnlyStream + internal interface IReadOnlyStream : IDisposable { - // TODO : Необходимо доработать данный интерфейс для обеспечения гарантированного закрытия файла, по окончанию работы с таковым! char ReadNextChar(); void ResetPositionToStart(); diff --git a/TestTask/LetterStats.cs b/TestTask/LetterStats.cs index aa10728..df8064c 100644 --- a/TestTask/LetterStats.cs +++ b/TestTask/LetterStats.cs @@ -3,7 +3,7 @@ /// /// Статистика вхождения буквы/пары букв /// - public struct LetterStats + public class LetterStats { /// /// Буква/Пара букв для учёта статистики. diff --git a/TestTask/Program.cs b/TestTask/Program.cs index fdf048e..c2d0096 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace TestTask { @@ -16,6 +17,14 @@ public class Program /// Второй параметр - путь до второго файла. static void Main(string[] args) { + if (args == null || args.Length < 2) + { + Console.WriteLine("Нужно передать 2 параметра: <путь_к_файлу_1> <путь_к_файлу_2>"); + Console.WriteLine("Пример: TestTask.exe \"C:\\temp\\1.txt\" \"C:\\temp\\2.txt\""); + Console.ReadKey(); + return; + } + IReadOnlyStream inputStream1 = GetInputStream(args[0]); IReadOnlyStream inputStream2 = GetInputStream(args[1]); @@ -28,7 +37,7 @@ static void Main(string[] args) PrintStatistic(singleLetterStats); PrintStatistic(doubleLetterStats); - // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. + Console.ReadKey(); } /// @@ -49,16 +58,32 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) /// Коллекция статистик по каждой букве, что была прочитана из стрима. private static IList FillSingleLetterStats(IReadOnlyStream stream) { + var dict = new Dictionary(); + stream.ResetPositionToStart(); + while (!stream.IsEof) { - char c = stream.ReadNextChar(); - // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. - } + char c; + + try { c = stream.ReadNextChar(); } + catch { break; } + + if (!char.IsLetter(c)) + continue; + + string key = c.ToString(); - //return ???; + if (!dict.TryGetValue(key, out var stat)) + { + stat = new LetterStats { Letter = key }; + dict[key] = stat; + } - throw new NotImplementedException(); + IncStatistic(stat); + } + + return dict.Values.ToList(); } /// @@ -70,16 +95,44 @@ private static IList FillSingleLetterStats(IReadOnlyStream stream) /// Коллекция статистик по каждой букве, что была прочитана из стрима. private static IList FillDoubleLetterStats(IReadOnlyStream stream) { + var dict = new Dictionary(); + stream.ResetPositionToStart(); + + char? prev = null; + while (!stream.IsEof) { - char c = stream.ReadNextChar(); - // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. - } + char c; + + try { c = stream.ReadNextChar(); } + catch { break; } + + if (!char.IsLetter(c)) + { + prev = null; + continue; + } - //return ???; + if (prev.HasValue && + char.ToUpperInvariant(prev.Value) == char.ToUpperInvariant(c)) + { + char up = char.ToUpperInvariant(c); + string key = new string(new[] { up, up }); - throw new NotImplementedException(); + if (!dict.TryGetValue(key, out var stat)) + { + stat = new LetterStats { Letter = key }; + dict[key] = stat; + } + + IncStatistic(stat); + } + + prev = c; + } + + return dict.Values.ToList(); } /// @@ -91,15 +144,22 @@ private static IList FillDoubleLetterStats(IReadOnlyStream stream) /// Тип букв для анализа private static void RemoveCharStatsByType(IList letters, CharType charType) { - // TODO : Удалить статистику по запрошенному типу букв. - switch (charType) + for (int i = letters.Count - 1; i >= 0; i--) { - case CharType.Consonants: - break; - case CharType.Vowel: - break; + var item = letters[i]; + + bool match = item.Letter.All(c => + charType == CharType.Vowel ? IsVowel(c) : !IsVowel(c)); + + if (match) + letters.RemoveAt(i); } - + } + + private static bool IsVowel(char c) + { + c = char.ToUpperInvariant(c); + return "AEIOUYАЕЁИОУЫЭЮЯ".Contains(c); } /// @@ -111,8 +171,26 @@ private static void RemoveCharStatsByType(IList letters, CharType c /// Коллекция со статистикой private static void PrintStatistic(IEnumerable letters) { - // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту! - throw new NotImplementedException(); + if (letters == null) + { + Console.WriteLine("ИТОГО : 0"); + return; + } + + var ordered = letters + .Where(x => x != null && !string.IsNullOrEmpty(x.Letter)) + .OrderBy(x => x.Letter, StringComparer.CurrentCulture) + .ToList(); + + int total = 0; + + foreach (var item in ordered) + { + Console.WriteLine($"{item.Letter} : {item.Count}"); + total += item.Count; + } + + Console.WriteLine($"ИТОГО : {total}"); } /// diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs index a51a61e..abac97f 100644 --- a/TestTask/ReadOnlyStream.cs +++ b/TestTask/ReadOnlyStream.cs @@ -5,7 +5,8 @@ namespace TestTask { public class ReadOnlyStream : IReadOnlyStream { - private Stream _localStream; + private readonly FileStream _fileStream; + private readonly StreamReader _reader; /// /// Конструктор класса. @@ -15,18 +16,18 @@ public class ReadOnlyStream : IReadOnlyStream /// Полный путь до файла для чтения public ReadOnlyStream(string fileFullPath) { - IsEof = true; + _fileStream = File.OpenRead(fileFullPath); + _reader = new StreamReader(_fileStream); - // TODO : Заменить на создание реального стрима для чтения файла! - _localStream = null; + ResetPositionToStart(); } - + /// /// Флаг окончания файла. /// public bool IsEof { - get; // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении + get; private set; } @@ -38,8 +39,21 @@ public bool IsEof /// Считанный символ. public char ReadNextChar() { - // TODO : Необходимо считать очередной символ из _localStream - throw new NotImplementedException(); + if (IsEof) + throw new EndOfStreamException(); + + int value = _reader.Read(); + + if (value == -1) + { + IsEof = true; + throw new EndOfStreamException(); + } + + if (_reader.Peek() == -1) + IsEof = true; + + return (char)value; } /// @@ -47,14 +61,15 @@ public char ReadNextChar() /// public void ResetPositionToStart() { - if (_localStream == null) - { - IsEof = true; - return; - } + _fileStream.Position = 0; + _reader.DiscardBufferedData(); + IsEof = _reader.Peek() == -1; + } - _localStream.Position = 0; - IsEof = false; + public void Dispose() + { + _reader?.Dispose(); + _fileStream?.Dispose(); } } } diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index dc470b0..9903e02 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -8,9 +8,10 @@ Exe TestTask TestTask - v4.6.1 + v4.8 512 true + AnyCPU @@ -49,5 +50,8 @@ + + + \ No newline at end of file diff --git a/TestTask/app.config b/TestTask/app.config new file mode 100644 index 0000000..3e0e37c --- /dev/null +++ b/TestTask/app.config @@ -0,0 +1,3 @@ + + +