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 @@
+
+
+