diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/IReadOnlyStream.cs
index 6946745..0ddbaf0 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..2919bc0 100644
--- a/TestTask/LetterStats.cs
+++ b/TestTask/LetterStats.cs
@@ -3,16 +3,16 @@
///
/// Статистика вхождения буквы/пары букв
///
- public struct LetterStats
+ public class LetterStats
{
///
/// Буква/Пара букв для учёта статистики.
///
- public string Letter;
+ public string Letter { get; set; }
///
/// Кол-во вхождений буквы/пары.
///
- public int Count;
+ public int Count{get;set;}
}
}
diff --git a/TestTask/Program.cs b/TestTask/Program.cs
index fdf048e..68be70a 100644
--- a/TestTask/Program.cs
+++ b/TestTask/Program.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
namespace TestTask
{
public class Program
{
+ private static readonly HashSet _vowels = new HashSet("ЁУЕЫАОЭЯИЮ");
///
/// Программа принимает на входе 2 пути до файлов.
@@ -16,19 +19,44 @@ public class Program
/// Второй параметр - путь до второго файла.
static void Main(string[] args)
{
- IReadOnlyStream inputStream1 = GetInputStream(args[0]);
- IReadOnlyStream inputStream2 = GetInputStream(args[1]);
+ if (args == null|| args.Length <2)
+ {
+ Console.WriteLine("Нужно указать пути к двум файлам");
+ Console.ReadKey(true);
+ return;
+ }
+
+ try
+ {
+ using (IReadOnlyStream inputStream1 = GetInputStream(args[0]))
+ using (IReadOnlyStream inputStream2 = GetInputStream(args[1]))
+ {
+ IList singleLetterStats = FillSingleLetterStats(inputStream1);
+ IList doubleLetterStats = FillDoubleLetterStats(inputStream2);
- IList singleLetterStats = FillSingleLetterStats(inputStream1);
- IList doubleLetterStats = FillDoubleLetterStats(inputStream2);
+ RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
+ RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);
- RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
- RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);
+ PrintStatistic(singleLetterStats);
+ PrintStatistic(doubleLetterStats);
+ }
- PrintStatistic(singleLetterStats);
- PrintStatistic(doubleLetterStats);
+ }
+ catch (FileNotFoundException ex)
+ {
+ Console.WriteLine("Ошибка.Файл не найден");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Ошибка.{ex.Message}");
+ }
+ finally
+ {
+ Console.WriteLine("Нажмите любую клавишу, чтобы выйти");
+ Console.ReadKey(true);
+ }
+
- // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы.
}
///
@@ -48,17 +76,37 @@ private static IReadOnlyStream GetInputStream(string fileFullPath)
/// Стрим для считывания символов для последующего анализа
/// Коллекция статистик по каждой букве, что была прочитана из стрима.
private static IList FillSingleLetterStats(IReadOnlyStream stream)
- {
+ {
+ var statDict=new Dictionary();
stream.ResetPositionToStart();
while (!stream.IsEof)
{
- char c = stream.ReadNextChar();
- // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый.
+ char c;
+ try
+ {
+ c = stream.ReadNextChar();
+ }
+ catch (EndOfStreamException)
+ {
+ break;
+ }
+
+ if (!char.IsLetter(c))
+ {
+ continue;
+ }
+
+ string key = c.ToString();
+ if (statDict.TryGetValue(key, out var stat))
+ {
+ IncStatistic(stat);
+ }
+ else
+ {
+ statDict[key] = new LetterStats { Letter = key, Count = 1 };
+ }
}
-
- //return ???;
-
- throw new NotImplementedException();
+ return statDict.Values.ToList();
}
///
@@ -70,16 +118,50 @@ private static IList FillSingleLetterStats(IReadOnlyStream stream)
/// Коллекция статистик по каждой букве, что была прочитана из стрима.
private static IList FillDoubleLetterStats(IReadOnlyStream stream)
{
+ var statDict=new Dictionary();
+ char? prevC = null;
stream.ResetPositionToStart();
while (!stream.IsEof)
{
- char c = stream.ReadNextChar();
- // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый.
+ char c;
+ try
+ {
+ c = stream.ReadNextChar();
+ }
+ catch (EndOfStreamException)
+ {
+ break;
+ }
+
+ if(!char.IsLetter(c))
+ {
+ prevC=null;
+ continue;
+ }
+
+ char upperC=char.ToUpper(c);
+ if (prevC.HasValue && prevC.Value == upperC)
+ {
+ string pair = new string(new char[] { upperC, upperC });
+ if (statDict.TryGetValue(pair, out var stat))
+ {
+ IncStatistic(stat);
+ }
+ else
+ {
+ statDict[pair] = new LetterStats { Letter = pair, Count = 1 };
+ }
+ prevC=null;
+ }
+ else
+ {
+ prevC=upperC;
+ }
+
+
}
- //return ???;
-
- throw new NotImplementedException();
+ return statDict.Values.ToList();
}
///
@@ -91,15 +173,40 @@ 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 charStat = letters[i];
+ if (string.IsNullOrEmpty(charStat.Letter))
+ {
+ continue;
+ }
+
+ bool isVowel = IsVowel(charStat.Letter[0] );
+ bool isRemove = true;
+
+ switch (charType)
+ {
+ case CharType.Consonants:
+ isRemove = !isVowel;
+ break;
+ case CharType.Vowel:
+ isRemove = isVowel;
+ break;
+ }
+
+ if (isRemove)
+ {
+ letters.RemoveAt(i);
+ }
+
}
-
+
+
+ }
+
+ private static bool IsVowel(char c)
+ {
+ return _vowels.Contains(char.ToUpper(c));
}
///
@@ -111,8 +218,25 @@ private static void RemoveCharStatsByType(IList letters, CharType c
/// Коллекция со статистикой
private static void PrintStatistic(IEnumerable letters)
{
- // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту!
- throw new NotImplementedException();
+ if (letters == null)
+ {
+ return;
+ }
+
+ var sortLet=letters.OrderBy(x => x.Letter,StringComparer.Ordinal).ToList();
+ if (sortLet.Count == 0)
+ {
+ Console.WriteLine("нет данных");
+ return;
+ }
+
+ int allCount = 0;
+ foreach (var c in sortLet)
+ {
+ Console.WriteLine($"{c.Letter} : {c.Count}");
+ allCount+=c.Count;
+ }
+ Console.WriteLine($"ИТОГО: {allCount}");
}
///
diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs
index a51a61e..4f45b71 100644
--- a/TestTask/ReadOnlyStream.cs
+++ b/TestTask/ReadOnlyStream.cs
@@ -1,11 +1,13 @@
using System;
using System.IO;
+using System.Runtime.Remoting.Channels;
+using System.Text;
namespace TestTask
{
public class ReadOnlyStream : IReadOnlyStream
{
- private Stream _localStream;
+ private StreamReader _localStream;
///
/// Конструктор класса.
@@ -13,12 +15,21 @@ public class ReadOnlyStream : IReadOnlyStream
/// обеспечить ГАРАНТИРОВАННОЕ закрытие файла после окончания работы с таковым!
///
/// Полный путь до файла для чтения
- public ReadOnlyStream(string fileFullPath)
+ ///
+ public ReadOnlyStream(string fileFullPath): this (fileFullPath, Encoding.UTF8) { }
+ public ReadOnlyStream(string fileFullPath, Encoding enc)
{
- IsEof = true;
+ if (string.IsNullOrWhiteSpace(fileFullPath))
+ {
+ throw new ArgumentNullException(nameof(fileFullPath),"Ошибка. Пустой путь до файла");
+ }
+ if(!File.Exists(fileFullPath))
+ {
+ throw new FileNotFoundException("Ошибка. Файл не найден",fileFullPath);
+ }
- // TODO : Заменить на создание реального стрима для чтения файла!
- _localStream = null;
+ _localStream = new StreamReader(fileFullPath, enc);
+ IsEof=_localStream.EndOfStream;
}
///
@@ -26,10 +37,16 @@ public ReadOnlyStream(string fileFullPath)
///
public bool IsEof
{
- get; // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении
+ get;
private set;
}
+ public void Dispose()
+ {
+ _localStream?.Dispose();
+ _localStream= null;
+ }
+
///
/// Ф-ция чтения следующего символа из потока.
/// Если произведена попытка прочитать символ после достижения конца файла, метод
@@ -38,8 +55,24 @@ public bool IsEof
/// Считанный символ.
public char ReadNextChar()
{
- // TODO : Необходимо считать очередной символ из _localStream
- throw new NotImplementedException();
+ if (_localStream == null)
+ {
+ throw new ObjectDisposedException(nameof(ReadOnlyStream));
+ }
+ if (IsEof)
+ {
+ throw new EndOfStreamException("Коней файла");
+ }
+
+ int data = _localStream.Read();
+ if (data == -1)
+ {
+ IsEof = true;
+ throw new EndOfStreamException("Коней файла");
+ }
+
+ IsEof = _localStream.EndOfStream;
+ return (char)data;
}
///
@@ -47,14 +80,14 @@ public char ReadNextChar()
///
public void ResetPositionToStart()
{
- if (_localStream == null)
+ if(_localStream == null)
{
- IsEof = true;
+ IsEof=true;
return;
}
-
- _localStream.Position = 0;
- IsEof = false;
+ _localStream.DiscardBufferedData();
+ _localStream.BaseStream.Seek(0,SeekOrigin.Begin);
+ IsEof = _localStream.EndOfStream;
}
}
}
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 @@
+
+
+
diff --git a/testdata/test1.txt b/testdata/test1.txt
new file mode 100644
index 0000000..bf9afa8
--- /dev/null
+++ b/testdata/test1.txt
@@ -0,0 +1,2 @@
+АаБбВвАБВ555пп77атТ
+ШШ5
\ No newline at end of file
diff --git a/testdata/test2.txt b/testdata/test2.txt
new file mode 100644
index 0000000..0aaaaef
--- /dev/null
+++ b/testdata/test2.txt
@@ -0,0 +1,2 @@
+ААааОоБбССеееСссС676
+*/]рр И ии ООООоО
\ No newline at end of file