Содержание
Задача: составить инвентарную ведомость игрушек, включив следующие данные: название игрушки, ее стоимость (в руб.), возрастные границы детей, для которых предназначена игрушка. Вывести в новый файл информацию о тех игрушках, которые предназначены для детей от N
до M
лет, отсортировав их по стоимости.
Замечания:
- подразумевается, что исходная информация хранится в текстовом файле
input.txt
, каждая строка которого содержит полную информацию о некотором объекте, - результирующая информация должна быть записана в файл
output.txt
, - для хранения данных внутри программы организовать массив структур,
- в типе структура реализуется метод
CompareTo
интерфейсаIComparable
, перегружается методToString
базового классаobject
и необходимые операции отношения, поля данных и дополнительные методы продумайте самостоятельно.
Декомпозиция задачи
Вначале, разберем поставленную задачу на составляющие элементы и разберемся, что необходимо будет в итоге сделать.
- Во-первых, данные по каждой игрушке должны храниться в структуре (
struct
). Так как конкретные типы данных для хранения стоимости, возрастов и т.д. в задаче не задаются, то будем использовать следующие типы данных для описания одной игрушки: название игрушки — строка (string
), стоимость в рублях — целое число (int
), возрастные ограничения — целые числа (int
). - Во-вторых, для хранения информации мы должны использовать массив. Никаких списков и прочих удобств C#.
- В-третьих, наша структура должна реализовывать интерфейс
IComparable
(а значит и использовать в работе методCompareTo
), а также иметь перегруженный метод ToString - В-четвертых, программа должна использовать файлы для получения входной информации и вывода итоговых данных.
В остальном, условиями задачи мы не ограничены и, как минимум, можем реализовать сортировку массива в одну строку, а не реализовывать самостоятельно какой-либо из алгоритмов.
Решение лабораторной работы
Создаем структуру
Для начала создадим полностью структуру (struct
) в которой будем хранить данные по игрушке. Для этого, создадим отдельный файл Toy.cs и размесим в нем следующий код:
using System; using static System.Math; namespace ToysComparer { public struct Toy: IComparable { public string Name { get; set; } public int Price { get; set; } public int AgeMin { get; set; } public int AgeMax { get; set; } public int CompareTo(object other) { if (other is not Toy) throw new ArgumentException("Сравнивать можно только структуры типа Toy"); return Sign(Price - ((Toy)other).Price); } public override string ToString() { return $"{Name};{Price};{AgeMin};{AgeMax}"; } } }
Структура Toy
, как и требуется по условиям задачи, реализует интерфейс IComparable
, а также переопределяет метод базового объекта ToString
. Так как мы реализовали интерфейс IComparable
, а не более удобный его аналог IComparable<Toy>
, то в методе CompareTo
предварительно проводится проверка на предмет того, что сравниваться будет именно структура игрушки, а не что-либо иное, например, обычный object
.
Что касается переопределения метода ToString(), то здесь мы выводим информацию об игрушке в следующем формате:
Название;Цена;МинимальныВозраст;МаксимальныйВозраст
в качестве разделителя используется точка с запятой.
Создаем необходимые методы
Для работы нашей программы можно написать следующие методы:
- Чтение файла с информацией об игрушках
- Запись выходного файла
- Вывод массивов структур в консоль
Напишем эти методы «as is» (как есть) без учёта возможных исключительных ситуаций и их обработки, чтобы был понятен основной алгоритм их работы. Впоследствии в программу можно будет внести необходимые улучшения, проверки и т.д.
Метод чтения файла
Этот метод должен возвращать массив игрушек. Следовательно, можно предложить следующий вариант:
public static Toy[] ReadFile(string fileName) { string[] lines = File.ReadAllLines(fileName); Toy[] toys = new Toy[lines.Length]; int i = 0; foreach (string s in lines) { string[] toyFields = s.Split(new[] { ';' }); toys[i].Name = toyFields[0]; toys[i].Price = Convert.ToInt32(toyFields[1]); toys[i].AgeMin = Convert.ToInt32(toyFields[2]); toys[i].AgeMax = Convert.ToInt32(toyFields[3]); i++; } return toys; }
Во-первых, мы считываем все строки из файла в массив строк lines
. По количеству строк в этом массиве мы создаем массив структур toys
.
Во-вторых, перебирая каждую строку из массива lines
мы используем метод string.Split
для разделения строки на элементы и записываем поля очередной структуры Toy
из массива toys
.
Метод записи строк в файл
Так как мы уже перегрузили метод ToString
у Toy
, то метод записи файла будет по-проще:
public static void WriteFile(string fileName, Toy[] toys) { using (StreamWriter sw = new StreamWriter(fileName)) { foreach (Toy toy in toys) sw.WriteLine(toy.ToString()); } }
Так как по условиям задачи нас никто не ограничивал в использовании тех или иных классов C#, то здесь мы создали объект типа StreamWriter
и, используя его метод WriteLine
, в цикле foreach
записали все игрушки в файл, применив для вывода метод ToString
структуры Toy
.
Метод вывода массивов структур в консоль
Здесь всё достаточно просто:
public static void Display(Toy[] toys) { foreach (Toy toy1 in toys) { Console.WriteLine(toy1.ToString()); } }
Пишем код основной программы
В основном коде (метод Main()
) мы должны реализовать основной алгоритм программы — прочитать входной файл, задать возрастные ограничения, выбрать необходимые игрушки из входного массива, отсортировать массив по возрастанию цены, записать выходной файл. Так как необходимые методы мы уже написали, то остается два интересных момента:
- Проверка того, что игрушка подходит по возрастным ограничениям
- Сортировка массива по возрастанию цены.
Так, для каждой игрушки у нас задан возрастной диапазон — это поля AgeMin
и AgeMax
. Пользователь так же задает свой диапазон с какого по какой возраст выбираются игрушки. Например, игрушка предназначена для возрастов от 1 до 10 лет. Будем считать, что если пользователь задаст возрастные ограничения от 3 до 5 лет, то такая игрушка будет включена в итоговый файл так как ребенок возрастом 3-5 лет вполне может играть такой игрушкой.
Что касается сортировки массива, то, чтобы наш метод мог использоваться, сделаем сортировку так, как показано ниже в коде (хотя, можно было реализовать эту часть и по-другому):
static void Main(string[] args) { string inputFile = "input.txt"; string outputFile = "output.txt"; Toy[] toys = ReadFile(inputFile); //выводим исходный массив игрушек Console.WriteLine("--------Исходный массив--------"); Display(toys); Console.WriteLine("-------------------------------"); Console.WriteLine("Введите минимальный возраст"); int MinAge = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Введите максимальный возраст"); int MaxAge = Convert.ToInt32(Console.ReadLine()); //Выбираем все игрушки с допустимым диапазоном возрастов var selected = toys.Where(w => (Enumerable.Range(w.AgeMin, w.AgeMax).Contains(MinAge)&&(Enumerable.Range(w.AgeMin, w.AgeMax).Contains(MaxAge)))); Toy[] outToys = selected.ToArray(); Console.WriteLine("--------Итоговый массив--------"); Array.Sort(outToys); //сортируем массив WriteFile(outputFile, outToys);//записываем массив в файл Display(outToys); Console.WriteLine("-------------------------------"); } }
Так как по условиям задачи нас никак не ограничивали в части того, как мы организуем выборку игрушек из массива, то выше мы воспользовались возможностями, которые нам предоставляет LINQ:
var selected = toys.Where(w => (Enumerable.Range(w.AgeMin, w.AgeMax).Contains(MinAge)&&(Enumerable.Range(w.AgeMin, w.AgeMax).Contains(MaxAge)))); Toy[] outToys = selected.ToArray();
Здесь мы использовали метод фильтрации в LINQ Where
, а затем, полученную выборку преобразовали в массив outToys
.
Проверка программы
Зададим следующий входной файл input.txt
Машинка 1;100;5;12 Машинка 2;10;5;12 Машинка 3;200;5;12 Машинка 4;50;5;12 Вертолет;75;3;10 Самолет;34;5;7 Трактор;35;1;5
Запустим программу и зададим следующие возрастные ограничения:
3
Введите максимальный возраст
5
Под эти ограничения, исходя из реализованного нами алгоритма, в выходной файл должны попасть две игрушки — Вертолет (с ограничениями от 3 до 10 лет) и Трактор (с ограничениями от 1 до 5 лет). Результат работы программы будет следующий:
Трактор;35;1;5
Вертолет;75;3;10
——————————-
Как видно, итоговый массив отсортирован по возрастанию цены, как и требовалось по условиям задачи.
Улучшение
В качестве улучшения можно предложить доработать метод Display так, чтобы вывод в консоль был более «читабельным» для пользователя. Например, так:
public static void Display(Toy[] toys) { foreach (Toy toy in toys) { Console.WriteLine($"Наименование: {toy.Name}\tСтоимость: {toy.Price} руб.\tВозрастные ограницения: от {toy.AgeMin} до {toy.AgeMax} лет"); } }
В этом случае, вывод массива в консоль будет выглядеть так:
Весь код лабораторной работы
Файл Toy.cs
using System; using static System.Math; namespace ToysComparer { /// <summary> /// Структура, содержащая полную информацию об игрушке /// </summary> public struct Toy: IComparable { public string Name { get; set; } public int Price { get; set; } public int AgeMin { get; set; } public int AgeMax { get; set; } /// <summary> /// Реализация интерфейса IComparable /// </summary> public int CompareTo(object other) { if (other is not Toy) throw new ArgumentException("Сравнивать можно только структуры типа Toy"); return Sign(Price - ((Toy)other).Price); } /// <summary> /// Перегруженный метод ToString базового object /// </summary> public override string ToString() { return $"{Name};{Price};{AgeMin};{AgeMax}"; } } }
Файл Program.cs
using System; using System.Linq; using System.IO; namespace ToysComparer { internal class Program { /// <summary> /// Запись выходного файла с результатами /// </summary> /// <param name="fileName">Имя файла</param> /// <param name="toys">Массив отсортированных игрушек</param> public static void WriteFile(string fileName, Toy[] toys) { using (StreamWriter sw = new StreamWriter(fileName)) { foreach (Toy toy in toys) sw.WriteLine(toy.ToString()); } } /// <summary> /// Чтение входного файла /// </summary> /// <param name="fileName">Имя файла</param> /// <returns>Массив игрушек</returns> public static Toy[] ReadFile(string fileName) { string[] lines = File.ReadAllLines(fileName); Toy[] toys = new Toy[lines.Length]; int i = 0; foreach (string s in lines) { string[] toyFields = s.Split(new[] { ';' }); toys[i].Name = toyFields[0]; toys[i].Price = Convert.ToInt32(toyFields[1]); toys[i].AgeMin = Convert.ToInt32(toyFields[2]); toys[i].AgeMax = Convert.ToInt32(toyFields[3]); i++; } return toys; } public static void Display(Toy[] toys) { foreach (Toy toy in toys) { Console.WriteLine($"Наименование: {toy.Name}\tСтоимость: {toy.Price} руб.\tВозрастные ограницения: от {toy.AgeMin} до {toy.AgeMax} лет"); } } static void Main(string[] args) { string inputFile = "input.txt"; string outputFile = "output.txt"; Toy[] toys = ReadFile(inputFile); //выводим исходный массив игрушек Console.WriteLine("--------Исходный массив--------"); Display(toys); Console.WriteLine("-------------------------------"); Console.WriteLine("Введите минимальный возраст"); int MinAge = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Введите максимальный возраст"); int MaxAge = Convert.ToInt32(Console.ReadLine()); //Выбираем все игрушки с допустимым диапазоном возрастов var selected = toys.Where(w => (Enumerable.Range(w.AgeMin, w.AgeMax).Contains(MinAge)&&(Enumerable.Range(w.AgeMin, w.AgeMax).Contains(MaxAge)))); Toy[] outToys = selected.ToArray(); Console.WriteLine("--------Итоговый массив--------"); Array.Sort(outToys); //сортируем массив WriteFile(outputFile, outToys);//записываем массив в файл Display(outToys); Console.WriteLine("-------------------------------"); } } }
Итого
В представленной лабораторной работе C# используются как знания основ языка — создание и работа со структурой, массивом, чтение и запись текстовых файлов, работа со строками, так и более продвинутые возможности языка, такие как LINQ.