Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions TestTask/IReadOnlyStream.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
namespace TestTask
using System;

namespace TestTask
{
/// <summary>
/// Интерфейс для работы с файлом в сильно урезаном виде.
/// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало.
/// Интерфейс для работы с потоком файла в сильно урезаном виде.
/// Обеспечивает всего 2 задачи: прочитать символ и перемотать стрим на начало.
/// </summary>
internal interface IReadOnlyStream
internal interface IReadOnlyStream : IDisposable
{
// TODO : Необходимо доработать данный интерфейс для обеспечения гарантированного закрытия файла, по окончанию работы с таковым!
/// <summary>
/// Флаг окончания файла
/// </summary>
bool IsEof { get; }

/// <summary>
/// Читает следующий символ из потока.
/// </summary>
char ReadNextChar();

/// <summary>
/// Сбрасывает текущую позицию потока на начало.
/// </summary>
void ResetPositionToStart();

bool IsEof { get; }
}
}
2 changes: 1 addition & 1 deletion TestTask/LetterStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public struct LetterStats
/// <summary>
/// Буква/Пара букв для учёта статистики.
/// </summary>
public string Letter;
public char Letter;

/// <summary>
/// Кол-во вхождений буквы/пары.
Expand Down
131 changes: 35 additions & 96 deletions TestTask/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;

namespace TestTask
{
public class Program
{

/// <summary>
/// Программа принимает на входе 2 пути до файлов.
/// Анализирует в первом файле кол-во вхождений каждой буквы (регистрозависимо). Например А, б, Б, Г и т.д.
Expand All @@ -16,90 +14,43 @@ public class Program
/// Второй параметр - путь до второго файла.</param>
static void Main(string[] args)
{
IReadOnlyStream inputStream1 = GetInputStream(args[0]);
IReadOnlyStream inputStream2 = GetInputStream(args[1]);

IList<LetterStats> singleLetterStats = FillSingleLetterStats(inputStream1);
IList<LetterStats> doubleLetterStats = FillDoubleLetterStats(inputStream2);

RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);

PrintStatistic(singleLetterStats);
PrintStatistic(doubleLetterStats);

// TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы.
}

/// <summary>
/// Ф-ция возвращает экземпляр потока с уже загруженным файлом для последующего посимвольного чтения.
/// </summary>
/// <param name="fileFullPath">Полный путь до файла для чтения</param>
/// <returns>Поток для последующего чтения.</returns>
private static IReadOnlyStream GetInputStream(string fileFullPath)
{
return new ReadOnlyStream(fileFullPath);
}

/// <summary>
/// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения каждой буквы.
/// Статистика РЕГИСТРОЗАВИСИМАЯ!
/// </summary>
/// <param name="stream">Стрим для считывания символов для последующего анализа</param>
/// <returns>Коллекция статистик по каждой букве, что была прочитана из стрима.</returns>
private static IList<LetterStats> FillSingleLetterStats(IReadOnlyStream stream)
{
stream.ResetPositionToStart();
while (!stream.IsEof)
if (args.Length == 2)
{
char c = stream.ReadNextChar();
// TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый.
string firstFilePath = args[0];
string secondFilePath = args[1];

var singleLetterTextScanner = new TextScanner(firstFilePath)
{
CaseSensitive = true,
SearchKind = SearchKind.SingleLetters
};
var doubleLetterTextScanner = new TextScanner(secondFilePath)
{
CaseSensitive = false,
SearchKind = SearchKind.DoubleLetters
};

// считываем из входящего потока все буквы и возвращаем статистику вхождения каждой буквы (статистика РЕГИСТРОЗАВИСИМАЯ)
var singleLetterSearchResult = singleLetterTextScanner.Search();
// считываем из входящего потока все буквы и возвращаем статистику вхождения парных букв (статистика НЕ регистрозависимая)
var doubleLetterSearchResult = doubleLetterTextScanner.Search();

// исключаем из статистики вхождения каждой буквы данные по всем гласным
singleLetterSearchResult.ExcludeLettersByCharType(CharType.Vowel);
// исключаем из статистики вхождения парных букв данные по всем согласным
doubleLetterSearchResult.ExcludeLettersByCharType(CharType.Consonants);

PrintStatistic(singleLetterSearchResult);
PrintStatistic(doubleLetterSearchResult);
}

//return ???;

throw new NotImplementedException();
}

/// <summary>
/// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения парных букв.
/// В статистику должны попадать только пары из одинаковых букв, например АА, СС, УУ, ЕЕ и т.д.
/// Статистика - НЕ регистрозависимая!
/// </summary>
/// <param name="stream">Стрим для считывания символов для последующего анализа</param>
/// <returns>Коллекция статистик по каждой букве, что была прочитана из стрима.</returns>
private static IList<LetterStats> FillDoubleLetterStats(IReadOnlyStream stream)
{
stream.ResetPositionToStart();
while (!stream.IsEof)
else
{
char c = stream.ReadNextChar();
// TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый.
Console.WriteLine("Приложение запущено некорректно - в аргументах запуска приложения необходимо указать два пути к текстовым файлам");
}

//return ???;

throw new NotImplementedException();
}

/// <summary>
/// Ф-ция перебирает все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы.
/// (Тип букв для перебора определяется параметром charType)
/// Все найденные буквы/пары соответствующие параметру поиска - удаляются из переданной коллекции статистик.
/// </summary>
/// <param name="letters">Коллекция со статистиками вхождения букв/пар</param>
/// <param name="charType">Тип букв для анализа</param>
private static void RemoveCharStatsByType(IList<LetterStats> letters, CharType charType)
{
// TODO : Удалить статистику по запрошенному типу букв.
switch (charType)
{
case CharType.Consonants:
break;
case CharType.Vowel:
break;
}

// Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы.
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}

/// <summary>
Expand All @@ -108,22 +59,10 @@ private static void RemoveCharStatsByType(IList<LetterStats> letters, CharType c
/// Выводить на экран необходимо предварительно отсортировав набор по алфавиту.
/// В конце отдельная строчка с ИТОГО, содержащая в себе общее кол-во найденных букв/пар
/// </summary>
/// <param name="letters">Коллекция со статистикой</param>
private static void PrintStatistic(IEnumerable<LetterStats> letters)
/// <param name="searchResult">результат со статистикой поиска</param>
private static void PrintStatistic(SearchResult searchResult)
{
// TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту!
throw new NotImplementedException();
Console.WriteLine(searchResult.ToString());
}

/// <summary>
/// Метод увеличивает счётчик вхождений по переданной структуре.
/// </summary>
/// <param name="letterStats"></param>
private static void IncStatistic(LetterStats letterStats)
{
letterStats.Count++;
}


}
}
74 changes: 47 additions & 27 deletions TestTask/ReadOnlyStream.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,80 @@
using System;
using System.IO;
using System.Text;

namespace TestTask
{
/// <summary>
/// Класс для работы с потоком чтения из текстового файла
/// </summary>
public class ReadOnlyStream : IReadOnlyStream
{
private Stream _localStream;
private StreamReader _localStreamReader;

/// <summary>
/// Конструктор класса.
/// Т.к. происходит прямая работа с файлом, необходимо
/// обеспечить ГАРАНТИРОВАННОЕ закрытие файла после окончания работы с таковым!
/// Конструктор класса <see cref="ReadOnlyStream"/>.
/// Должен обеспечивать ГАРАНТИРОВАННОЕ закрытие файла после окончания работы с ним.
/// </summary>
/// <param name="fileFullPath">Полный путь до файла для чтения</param>
public ReadOnlyStream(string fileFullPath)
{
IsEof = true;
/// <param name="fileFullPath">полный путь к файлу для чтения</param>
public ReadOnlyStream(string fileFullPath) : this(fileFullPath, Encoding.Default) { }

// TODO : Заменить на создание реального стрима для чтения файла!
_localStream = null;
/// <summary>
/// Конструктор класса <see cref="ReadOnlyStream" /> с возможностью указать кодировку файла.
/// Должен обеспечивать ГАРАНТИРОВАННОЕ закрытие файла после окончания работы с ним.
/// </summary>
/// <param name="fileFullPath">полный путь к текстовому файлу для чтения</param>
/// <param name="encoding">кодировка текстового файла</param>
/// <exception cref="ArgumentNullException">Source file path can't be NULL or empty</exception>
/// <exception cref="ArgumentException">Source file doesn't exist"</exception>
public ReadOnlyStream(string fileFullPath, Encoding encoding)
{
if (string.IsNullOrEmpty(fileFullPath))
{
throw new ArgumentNullException(nameof(fileFullPath), "Source file path can't be NULL or empty");
}
if (!File.Exists(fileFullPath))
{
throw new ArgumentException($"Source file \"{fileFullPath}\" doesn't exist", nameof(fileFullPath));
}
this._localStreamReader = new StreamReader(fileFullPath, encoding);
}

/// <summary>
/// Флаг окончания файла.
/// Флаг окончания файла
/// </summary>
public bool IsEof
{
get; // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении
private set;
get { return this._localStreamReader.EndOfStream; }
}

/// <summary>
/// Ф-ция чтения следующего символа из потока.
/// Если произведена попытка прочитать символ после достижения конца файла, метод
/// должен бросать соответствующее исключение
/// Читает следующий символ из потока.
/// Выбрасыает <see cref="T:System.IO.IOException"/>, если произведена попытка прочитать символ после достижения конца файла.
/// </summary>
/// <returns>Считанный символ.</returns>
/// <returns>считанный символ</returns>
public char ReadNextChar()
{
// TODO : Необходимо считать очередной символ из _localStream
throw new NotImplementedException();
return (char)this._localStreamReader.Read();
}

/// <summary>
/// Сбрасывает текущую позицию потока на начало.
/// </summary>
public void ResetPositionToStart()
{
if (_localStream == null)
{
IsEof = true;
return;
}
this._localStreamReader.BaseStream.Position = 0;
}

#region IDisposable

_localStream.Position = 0;
IsEof = false;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this._localStreamReader?.Dispose();
}

#endregion
}
}
35 changes: 35 additions & 0 deletions TestTask/SearchKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.ComponentModel;

namespace TestTask
{
/// <summary>
/// Вид поиска
/// </summary>
public enum SearchKind
{
/// <summary>
/// одиночные буквы
/// </summary>
SingleLetters,
/// <summary>
/// парные буквы
/// </summary>
DoubleLetters
}

/// <summary>
/// Расширение для типа <see cref="SearchKind"/>
/// </summary>
public static class SearchKindExtension
{
/// <summary>
/// Возвращает человекопонятное описание заданного элемента перечисления видов поиска
/// </summary>
/// <param name="searchKind">вид поиска</param>
/// <returns>человекопонятное описание</returns>
public static string ToFriendlyString(this SearchKind searchKind)
{
return searchKind == SearchKind.DoubleLetters ? "парные буквы" : "одиночные буквы";
}
}
}
Loading