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
7 changes: 4 additions & 3 deletions TestTask/IReadOnlyStream.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
namespace TestTask
using System;

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

void ResetPositionToStart();
Expand Down
125 changes: 87 additions & 38 deletions TestTask/Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace TestTask
{
public class Program
{
private static readonly HashSet<char> Vowels = new HashSet<char>(
"аеёиоуыэюяАЕЁИОУЫЭЮЯaeiouAEIOU"
);

/// <summary>
/// Программа принимает на входе 2 пути до файлов.
Expand All @@ -16,19 +20,38 @@ 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);
if (args == null || args.Length < 2)
{
Console.WriteLine("Ошибка: Недостаточно аргументов или файлы не найдены");

RemoveCharStatsByType(singleLetterStats, CharType.Vowel);
RemoveCharStatsByType(doubleLetterStats, CharType.Consonants);
Console.WriteLine("\nНажмите любую клавишу для закрытия консоли...");
Console.ReadKey();
return;
}

PrintStatistic(singleLetterStats);
PrintStatistic(doubleLetterStats);
try
{
// Используем блоки using для автоматического закрытия файлов
using (IReadOnlyStream inputStream1 = GetInputStream(args[0]))
using (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);
}
}
catch (Exception ex)
{
Console.WriteLine($"Произошла ошибка при обработке файлов: {ex.Message}");
}

// TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы.
Console.WriteLine("\nНажмите любую клавишу для закрытия консоли...");
Console.ReadKey();
}

/// <summary>
Expand All @@ -49,16 +72,21 @@ private static IReadOnlyStream GetInputStream(string fileFullPath)
/// <returns>Коллекция статистик по каждой букве, что была прочитана из стрима.</returns>
private static IList<LetterStats> FillSingleLetterStats(IReadOnlyStream stream)
{
var statsMap = new Dictionary<string, int>();
stream.ResetPositionToStart();

while (!stream.IsEof)
{
char c = stream.ReadNextChar();
// TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый.
if (char.IsLetter(c))
{
string key = c.ToString();
if (!statsMap.ContainsKey(key)) statsMap[key] = 0;
statsMap[key]++;
}
}

//return ???;

throw new NotImplementedException();
return statsMap.Select(kvp => new LetterStats { Letter = kvp.Key, Count = kvp.Value }).ToList();
}

/// <summary>
Expand All @@ -70,16 +98,36 @@ private static IList<LetterStats> FillSingleLetterStats(IReadOnlyStream stream)
/// <returns>Коллекция статистик по каждой букве, что была прочитана из стрима.</returns>
private static IList<LetterStats> FillDoubleLetterStats(IReadOnlyStream stream)
{
var statsMap = new Dictionary<string, int>();
stream.ResetPositionToStart();

char? prevChar = null;

while (!stream.IsEof)
{
char c = stream.ReadNextChar();
// TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый.
char current = stream.ReadNextChar();

if (char.IsLetter(current))
{
if (prevChar.HasValue && char.ToLower(prevChar.Value) == char.ToLower(current))
{
string key = (current.ToString() + current.ToString()).ToLower();
if (!statsMap.ContainsKey(key)) statsMap[key] = 0;
statsMap[key]++;

// После нахождения пары сбрасываем, чтобы "aaa" считалось как одна пара "aa"
prevChar = null;
continue;
}
prevChar = current;
}
else
{
prevChar = null;
}
}

//return ???;

throw new NotImplementedException();
return statsMap.Select(kvp => new LetterStats { Letter = kvp.Key, Count = kvp.Value }).ToList();
}

/// <summary>
Expand All @@ -91,15 +139,19 @@ private static IList<LetterStats> FillDoubleLetterStats(IReadOnlyStream stream)
/// <param name="charType">Тип букв для анализа</param>
private static void RemoveCharStatsByType(IList<LetterStats> letters, CharType charType)
{
// TODO : Удалить статистику по запрошенному типу букв.
switch (charType)
for (int i = letters.Count - 1; i >= 0; i--)
{
case CharType.Consonants:
break;
case CharType.Vowel:
break;
bool isVowel = Vowels.Contains(letters[i].Letter.ToLower()[0]);

if (charType == CharType.Vowel && isVowel)
{
letters.RemoveAt(i);
}
else if (charType == CharType.Consonants && !isVowel)
{
letters.RemoveAt(i);
}
}

}

/// <summary>
Expand All @@ -111,19 +163,16 @@ private static void RemoveCharStatsByType(IList<LetterStats> letters, CharType c
/// <param name="letters">Коллекция со статистикой</param>
private static void PrintStatistic(IEnumerable<LetterStats> letters)
{
// TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту!
throw new NotImplementedException();
}

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

foreach (var stat in sorted)
{
Console.WriteLine($"{stat.Letter} : {stat.Count}");
total += stat.Count;
}

Console.WriteLine($"ИТОГО : {total}");
}
}
}
42 changes: 27 additions & 15 deletions TestTask/ReadOnlyStream.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.IO;
using System.Runtime.Remoting.Messaging;

namespace TestTask
{
public class ReadOnlyStream : IReadOnlyStream
{
private Stream _localStream;
private StreamReader _localStream;
private readonly string _filePath;

/// <summary>
/// Конструктор класса.
Expand All @@ -15,18 +17,17 @@ public class ReadOnlyStream : IReadOnlyStream
/// <param name="fileFullPath">Полный путь до файла для чтения</param>
public ReadOnlyStream(string fileFullPath)
{
IsEof = true;

// TODO : Заменить на создание реального стрима для чтения файла!
_localStream = null;
_filePath = fileFullPath;
// Инициализируем стрим сразу
ResetPositionToStart();
}

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

Expand All @@ -38,23 +39,34 @@ public bool IsEof
/// <returns>Считанный символ.</returns>
public char ReadNextChar()
{
// TODO : Необходимо считать очередной символ из _localStream
throw new NotImplementedException();
if (IsEof) throw new EndOfStreamException("Достигнут конец файла");

int charCode = _localStream.Read();
if (charCode == -1)
{
IsEof = true;
return '\0';
}

return (char)charCode;
}

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

_localStream.Position = 0;
_localStream?.Dispose();
_localStream = new StreamReader(_filePath);
IsEof = false;
}

/// <summary>
/// Освобождаем память
/// </summary>
public void Dispose()
{
_localStream?.Dispose();
}
}
}