Файл: Контрольная работа 1 3 Решение 3 Результаты 5 контрольная работа 2 7 Решение 7 Результаты 10 контрольная работа 3 14.doc
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 07.11.2023
Просмотров: 26
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
СОДЕРЖАНИЕ 2
1. КОНТРОЛЬНАЯ РАБОТА №1 3
Решение 3
Результаты 5
2. КОНТРОЛЬНАЯ РАБОТА №2 7
Решение 7
Результаты 10
3. КОНТРОЛЬНАЯ РАБОТА №3 14
Часть 1 – решение 14
Часть 1 – результаты 16
Часть 2 – решение 16
Часть 2 – результаты 19
4. КОНТРОЛЬНАЯ РАБОТА №4 21
Решение 21
Результаты 24
5. КОНТРОЛЬНАЯ РАБОТА №5 25
Часть 1 – решение 25
Часть 1 – результаты 27
Часть 2 – решение 27
Часть 2 – результаты 32
1. КОНТРОЛЬНАЯ РАБОТА №1
Тема работы: работа со строками.
Целью работы является изучение основ работы со строками и составление алгоритмов с их использованием.
Задание.
Решение
Следующая программа решает задачу двумя способами:
1) учебным – через самодельный алгоритм, вычленяющий в строке даты день, месяц, год и по оным определяющий количество прошедших дней от начала года с учетом длительностей месяцев;
2) высокоуровневым – получает значение типа DateTime по входной строке и маске с помощью метода ParseExact, после чего использует свойство DayOfYear для переменной DateTime.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization; // см. CultureInfo в ParseExact
namespace VelikijSensej1
{
class Program
{
// получение числа, соответствующего символу
// c в переданной маске strMask
static int NumberBySymbols(string strDate, string strMask, char c)
{
// диапазон индексов подстроки из символов с в строке strDate
int start = strMask.IndexOf(c);
int end = strMask.LastIndexOf(c);
// подстрока strDate, соответствующая индексам от start до end
// end - start + 1 - количество символов в подстроке
string sub = strDate.Substring(start, end - start + 1);
return int.Parse(sub);
}
// сколько дней прошло от начала года в заданной дате?
static int DaysCome(int day, int month, int year)
{
// число дней в месяцах
int[] months_dur = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// для случая февраля високосного года
if (year % 4 == 0) months_dur[1]++;
// для подсчета числа прошедших дней
int sum_days = 0;
// учет всех месяцев ранее month
for (int k = 1; k < month; k++)
// k-1: нумерация в массиве - с 0
sum_days += months_dur[k - 1];
// учет прошедших дней в самом месяце month
sum_days += (day - 1);
return sum_days;
}
// получение, сколько дней прошло от начала года:
// strDate - переданная дата, strMark - маска даты
static int DaysCome(string strDate, string strMask)
{
// предполагается, что в маске есть символы d, m, y
// получаем день, месяц, год
int day = NumberBySymbols(strDate, strMask, 'd');
int month = NumberBySymbols(strDate, strMask, 'm');
int year = NumberBySymbols(strDate, strMask, 'y');
// вызываем вспомогательный метод подсчета числа прошедших дней года
return DaysCome(day, month, year);
}
static void Main(string[] args)
{
Console.WriteLine("Введите маску даты (например, dd.mm.yyyy, yyyymmdd): ");
string strMask = Console.ReadLine();
Console.WriteLine("Введите саму дату: ");
string strDate = Console.ReadLine();
// вызов нашего метода подсчета
int days = DaysCome(strDate, strMask);
Console.WriteLine("Прошло полных дней с начала года: " + days);
// способ через возможности DateTime
// 1) заменяем в маске мелкие m на большие
strMask = strMask.Replace('m', 'M');
// 2) парсим strDate как DateTime
DateTime dt = DateTime.ParseExact(strDate, strMask,
CultureInfo.InvariantCulture);
// 3) прошло_дней = номер_дня_в_году - 1
Console.WriteLine("Проверка через DateTime: " + (dt.DayOfYear - 1));
Console.ReadKey();
}
}
}
Результаты
Проверка:
Прошло за январь | 31 день |
Прошло за февраль | 28 дней (год не високосный) |
Прошло с 1 марта | 14 дней (15-ое марта еще не прошло!) |
Всего | 31+28+14=73 |
Для високосного года и 15 марта ответ должен быть на 1 больше:
Для не високосного года и 31 декабря ответ должен быть 364 (365-1), для 31 декабря високосного года ответ должен быть 365.
Особый тест – для самого начала года:
2. КОНТРОЛЬНАЯ РАБОТА №2
Тема работы: работа с коллекциями.
Целью работы является изучение основ классов, предназначенных для работы с коллекциями, а также овладение практическими навыками составления алгоритмов с их использованием.
Задание.
Решение
Следующий код использует высокоуровневые возможности работы с коллекциями, тем самым упрощается решение задачи. Например, для поиска используются такие методы для List, как FindIndex, FindAll.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VelikijSensej2
{
struct OrgTech // структура МЕБЕЛЬ ИЛИ ОРГТЕХНИКА
{
public string Number; // инвентарный номер
public string Kind; // вид (стул, принтер, ...)
public string Room; // кабинет
public string ResponPerson; // ответственное лицо
// конструктор
public OrgTech(string Number, string Kind, string Room, string ResponPerson)
{
// this - чтобы отличить поле от одноимённого параметра
this.Number = Number;
this.Kind = Kind;
this.Room = Room;
this.ResponPerson = ResponPerson;
}
}
class Program
{
// поиск по инвентарному номеру
public static int FindByNumber(List
{
return Database.FindIndex(
// если номер совпадает с заданным, это то, что надо
x => (x.Number == Number));
}
// поиск по инвентарному номеру, вводимому с клавиатуры
public static int FindByNumber(List
{
Console.Write("Введите инвентарный номер: ");
string Number = Console.ReadLine();
return FindByNumber(Database, Number);
}
// добавление записи
public static void Addition(List
{
Console.Write("Введите инвентарный номер новой техники: ");
string Number = Console.ReadLine();
// Возврат -1 означает отсутствие записи с инвентарным номером Number
if (FindByNumber(Database, Number) != -1)
{
Console.WriteLine("Техника с номером " + Number + " уже есть");
return;
}
Console.Write("Введите вид: ");
string Kind = Console.ReadLine();
Console.Write("Введите кабинет: ");
string Room = Console.ReadLine();
Console.Write("Введите ответственное лицо: ");
string Person = Console.ReadLine();
// создать структуру данных и занести в список
Database.Add(new OrgTech(Number, Kind, Room, Person));
}
// удаление записи
public static void Removing(List
{
int index = FindByNumber(Database);
if (index == -1)
{
Console.WriteLine("Дело плохо: запись уже удалена или её никогда и не было");
return;
}
Database.RemoveAt(index); // удаление index-го элемента списка
}
// редактирование
public static void Edition(List
{
// получить инвентарный номер и найти запись по нему
int index = FindByNumber(Database);
// если в списке нет записей с введенным инвентарным номером
if (index == -1)
{
Console.WriteLine("Дело плохо: запись не найдена");
return;
}
Console.Write("Хотите ли вы изменить ответственное лицо? 1 - да, другое число - нет: ");
OrgTech t = Database[index];
if (int.Parse(Console.ReadLine()) == 1)
{
Console.Write("Введите новое отв. лицо: ");
t.ResponPerson = Console.ReadLine();
}
Console.Write("Хотите ли вы переместить в другой кабинет? 1 - да, другое число - нет: ");
if (int.Parse(Console.ReadLine()) == 1)
{
Console.Write("Введите новый кабинет: ");
t.Room = Console.ReadLine();
}
// нельзя сделать так: Database[index].поле = Console.ReadLine();
// поэтому мы заводили вспомогательную переменную
Database[index] = t;
}
// вывод содержимого списка на экран
public static void PrintList(List
{
if (Database.Count == 0) // Count - число записей в списке
{
Console.WriteLine("Нет записей");
return;
}
// {0, 3} - отвести 3 позиции под параметр 0,
// {1, 10} - отвести 10 позиций под параметр 1 и т.д.
Console.WriteLine("{0,3}{1,11}{2,20}{3,8}{4,25}",
"№", "Инв. номер", "Тип", "Кабинет", "Отв. лицо");
for (int i = 0; i < Database.Count; i++)
// вывести содержимое i-го элемента списка Database
Console.WriteLine("{0,3}{1,11}{2,20}{3,8}{4,25}",
i + 1, Database[i].Number,
Database[i].Kind, Database[i].Room,
Database[i].ResponPerson);
}
// поиск записей
public static void Search(List
{
Console.Write("Введите кабинет для поиска: ");
string Room = Console.ReadLine();
// FindAll - найти все записи по заданному условию поиска
List
x => (x.Room == Room)); // условие поиска
// если список результатов поиска пустой
if (Found.Count == 0) Console.WriteLine("Ничего не найдено");
else PrintList(Found); // если он непустой
}
static void Main(string[] args)
{
List
bool flag = true;
while (flag == true)
{
Console.WriteLine("1-добавление, 2-удаление, 3-редактирование, 4-поиск, 5-вывод всех записей, 6-выход");
int cmd = int.Parse(Console.ReadLine());
switch (cmd)
{
case 1: Addition(Database); break;
case 2: Removing(Database); break;
case 3: Edition(Database); break;
case 4: Search(Database); break;
case 5: PrintList(Database); break;
case 6: flag = false; break; // выбить прогу из цикла
}
}
}
}
}
Результаты
Примеры добавления отдельных записей и вывода всех записей:
Примеры поиска:
Примеры редактирования:
Примеры удаления:
3. КОНТРОЛЬНАЯ РАБОТА №3
Тема работы: работа с перечислениями и структурами.
Целью работы является приобретение практических навыков при работе с перечислениями и структурами данных.
Задание.
Написать две программы на языке C#, которые демонстрируют работу:
-
с перечислениями; -
со структурами.
Необходимо выполнить следующие операции с перечислениями: 1) описать перечисление; 2) объявить переменную перечисляемого типа данных; 3) инициализировать переменную значением с клавиатуры; 4) вывести все значения перечисляемого типа данных, при этом введенное с клавиатуры значение подсветить другим цветом.
Необходимо выполнить следующие операции со структурами: 1) описать структуру; 2) объявить переменную структурного типа; 3) описать конструктор, инициализирующий поля структуры; 4) описать метод PrintStruct, который выводит на экран значения полей структуры в формате <название поля> - <значение>; 5) инициализировать поля структуры значениями, введенными с клавиатуры; 6) вывести поля структуры на экран с помощью PrintStruct.
Часть 1 – решение
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VelikijSensej31
{
enum AltMusic // перечисляемый тип ВИД АЛЬТЕРНАТИВНОЙ МУЗЫКИ
{
брит_поп,
гранж,
дрим_поп,
инди
}
class Program
{
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.White;
Console.Write("Введите направление музыки: ");
// инициализация: чтобы компиляторы поздних версий Visual Studio
// не имели претензий за неинициализированную
// переменную
AltMusic vhod_znach = AltMusic.брит_поп;
// читаем строку, заменяем "-" на "_",
// избавляемся от букв верхнего регистра
string InputValue = Console.ReadLine();
InputValue = InputValue.Replace("-", "_").ToLower();
// пробуем привести входное значение к типу AltMusic
bool IsCorrect = Enum.TryParse(InputValue, out vhod_znach);
if (IsCorrect == true) // если значение существует
{
// все корректные значения AltMusic
Array AllStyles = Enum.GetValues(typeof(AltMusic));
// цикл по массиву всех значений AltMusic
foreach (AltMusic CurValue in AllStyles)
{
// если текущее значение - введённое пользователем
if (CurValue == vhod_znach)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(CurValue);
Console.ForegroundColor = ConsoleColor.White;
}
// если это другое значение
else Console.WriteLine(CurValue);
}
}
// если значение оказалось некорректным
else Console.WriteLine("Некорректное значение");
Console.ReadKey();
}
}
}
Часть 1 – результаты
Часть 2 – решение
В следующем коде вывод полей основывается на возможностях System.Reflection (FieldInfo – оттуда). Недостаток подхода – имена полей выводятся как объявлены в коде (что не всегда удобно для пользователя – например, из-за объявления латиницей). Однако преимуществ метода больше, если сравнивать с ситуацией, когда вывод каждого поля мы прописываем отдельно вручную (например, сколько полей – столько и Console.WriteLine):
1) мы не упустим никакого поля из-за невнимательности;
2) код все же получится удобнее, когда полей много (например, в сфере системного программирования некоторые структуры имеют и по 15-20 полей);
3) если мы добавили или удалили поле, изменили имя либо тип некоторого поля, код нашего метода PrintStruct не поменяется – метод Type.GetFields автоматически собирает данные обо всех объявленных полях структуры.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection; // используем в PrintStruct
namespace VelikijSensej32
{
enum PlaneKind // вид самолета
{
штурмовик,
бомбардировщик,
истребитель,
транспортный,
разведчик
}
struct Plane
{
public string Mark; // марка самолета
public PlaneKind Kind; // вид
public int YearCreation; // год создания
public string Creator; // авиаконструктор
public string Country; // страна
public int SpeedKmH; // скорость (км/ч)
// конструктор
public Plane(string Mark, PlaneKind KindOfPlane, int Year,
string Creator, string Country, int SpeedKmH)
{
// this обязателен там, где поле и параметр
// одноименные (например, для this.Kind = KindOfPlane
// он необязателен, написан лишь для единства стилей)
this.Mark = Mark;
this.Kind = KindOfPlane;
this.SpeedKmH = SpeedKmH;
this.YearCreation = Year;
this.Country = Country;
this.Creator = Creator;
}
// вывести поля - метод на основе System.Reflection,
// его код не меняется при добавлении/удалении полей
public void PrintStruct()
{
// получить список данных о полях для типа Plane
FieldInfo[] fieldsList = typeof(Plane).GetFields();
// строка из 73 подчеркиваний (для имитации вывода
// горизонтальной черты)
string line = new string('_', 78);
// меняем цвет выводимого текста
Console.ForegroundColor = ConsoleColor.Yellow;
// имитируем вывод горизонтальной черты
Console.WriteLine(line);
// выводим шапку таблицы
Console.WriteLine("|{0,15}|{1, 60}|", "Поле", "Значение");
Console.WriteLine(line);
Console.ForegroundColor = ConsoleColor.Gray;
foreach (FieldInfo item in fieldsList) // проход по полям
{
// получение значения для поля
string value = item.GetValue(this).ToString();
// item.Name - имя поля (в том виде, в каком мы объявили в коде)
Console.WriteLine("|{0,15}|{1, 60}|", item.Name, value);
Console.WriteLine(line);
}
}
}
class Program
{
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write("Введите марку самолета: ");
string Mark = Console.ReadLine();
Console.Write("Введите вид самолета: ");
string Kind = Console.ReadLine();
PlaneKind pKind = PlaneKind.бомбардировщик;
// пробуем распознать введенную строку как PlaneKind;
// 2-ой параметр - чтобы игнорировать регистр строки Kind
if (Enum.TryParse(Kind, true, out pKind) == false)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Не знаю такой вид самолета");
}
else // если вид самолета распознан успешно
{
// продолжаем ввод исходных данных
Console.Write("Введите год создания: ");
int Year = int.Parse(Console.ReadLine());
Console.Write("Введите авиаконструктора: ");
string Creator = Console.ReadLine();
Console.Write("Введите страну: ");
string Country = Console.ReadLine();
Console.Write("Введите макс. скорость (км/ч): ");
int Speed = int.Parse(Console.ReadLine());
// создание структурной переменной с инициализацией
// полей через конструктор
Plane first = new Plane(Mark, pKind, Year, Creator, Country, Speed);
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Выводим содержимое структуры first");
Console.ForegroundColor = ConsoleColor.Gray;
// вызов метода вывода содержимого структуры
first.PrintStruct();
// создание структурной переменной без вызова
// нашего конструктора
Plane second;
// заполнение полей путем непосредственной работы с полями
second.Mark = Mark;
second.Kind = pKind;
second.YearCreation = Year;
second.Creator = Creator;
second.Country = Country;
second.SpeedKmH = Speed;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Выводим содержимое структуры second");
Console.ForegroundColor = ConsoleColor.Gray;
second.PrintStruct();
}
Console.ReadKey();
}
}
}
Часть 2 – результаты
4. КОНТРОЛЬНАЯ РАБОТА №4
Тема работы: работа с пользовательскими методами класса.
Целью работы является изучение общей структуры методов класса, а также овладение практическими навыками их использования при решении задач.
Задание.
Необходимо описать два метода, которые реализуют один и тот же алгоритм. Первый метод для возврата результата должен использовать имя метода, а второй должен возвращать результат через параметры.
Решение
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VelikijSensej4
{
class Program
{
// метод построения таблицы умножения -
// результат возвращается через имя
static string BuildTable(int min, int max)
{
string str_table = "";
int i, j, n, maxL;
// количество цифр в max
maxL = max.ToString().Length;
// получить левую часть верхней строки таблицы
str_table = string.Format("{0, " + (maxL + 1) + "}", "*|");
// получить остальную часть верхней строки таблицы
for (i = min; i <= max; i++)
{
// если произведение i*max влезает при заданной
// ширине столбца, то в столбец влезут все произведения
// i*j, j=min, min+1, ..., max
n = (i * max).ToString().Length;
str_table += string.Format("{0, " + (n + 2) + "}", i);
}
// универсальный перевод строки ('\n' работает не для
// всех типов программ)
str_table += Environment.NewLine;
// провести линию, отделяющую верхнюю строку
for (i = 0; i <= maxL; i++) str_table += "_";
for (i = min; i <= max; i++)
{
n = (i * max).ToString().Length;
for (j = 0; j < n + 2; j++)
str_table += "_";
}
// построение остальной части таблицы
for (i = min; i <= max; i++)
{
// при заданном i требуется выдать все произведения
// с первым множителем i
str_table += Environment.NewLine + Environment.NewLine;
str_table += string.Format("{0, " + (maxL + 1) + "}", i + "|");
for (j = min; j <= max; j++)
{
n = (j * max).ToString().Length;
str_table += string.Format("{0, " + (n + 2) + "}", (i * j));
}
}
return str_table; // возврат значения
}
// метод построения таблицы умножения -
// результат через параметр
static void BuildTable(int min, int max, out string str_table)
{
// алгоритм как у первого метода, но не нужна локальная
// строковая переменная и оператор return
str_table = "";
int i, j, n, maxL;
maxL = max.ToString().Length;
str_table = string.Format("{0, " + (maxL + 1) + "}", "*|");
for (i = min; i <= max; i++)
{
n = (i * max).ToString().Length;
str_table += string.Format("{0, " + (n + 2) + "}", i);
}
str_table += Environment.NewLine;
for (i = 0; i <= maxL; i++) str_table += "_";
for (i = min; i <= max; i++)
{
n = (i * max).ToString().Length;
for (j = 0; j < n + 2; j++)
str_table += "_";
}
for (i = min; i <= max; i++)
{
str_table += Environment.NewLine + Environment.NewLine;
str_table += string.Format("{0, " + (maxL + 1) + "}", i + "|");
for (j = min; j <= max; j++)
{
n = (j * max).ToString().Length;
str_table += string.Format("{0, " + (n + 2) + "}", (i * j));
}
}
}
static void Main(string[] args)
{
Console.WriteLine("Задайте диапазон значений множителей");
// ввод исходных данных
Console.Write("Введите минимальное значение: ");
int min = int.Parse(Console.ReadLine());
Console.Write("Введите максимальное значение: ");
int max = int.Parse(Console.ReadLine());
// применяем метод, возвращающий результат через имя
string table = BuildTable(min, max);
// показываем результаты работы метода
Console.WriteLine("Проверка метода, возвращающего результат через имя");
Console.WriteLine("Результат:\n" + table);
string OutTable = ""; // для 2-го метода, чтобы запомнить результат
// применяем метод, возвращающий результат через параметры
BuildTable(min, max, out OutTable);
// отделим текст от предыдущего пустыми строками
Console.WriteLine();
Console.WriteLine();
// показываем результаты работы метода
Console.WriteLine("Проверка метода, возвращающего результат через параметры");
Console.WriteLine("Результат:\n" + OutTable);
Console.ReadKey();
}
}
}
Примечание: за счет использования Environment.NewLine в BuildTable для перевода строки вместо привычного для консольного приложения «\n» мы можем в теории использовать методы BuildTable и в графических приложениях – например, перенести коды методов в Windows Forms-приложение и вывести таблицу в многострочное текстовое поле (с моноширинным шрифтом наподобие Courier New).
Результаты
5. КОНТРОЛЬНАЯ РАБОТА №5
Тема работы: работа с файлами.
Целью работы является изучение основ классов, предназначенных для работы с файлами, а также овладение практическими навыками составления алгоритмов с их использованием.
Задание.
А. Работа с текстовыми файлами.
Б. Работа с типизированными файлами.
Часть 1 – решение
Следующий код основан на такой интерпретации задания: в новом файле первая строка должна совпадать с последней строкой исходного, вторая строка – с предпоследней строкой исходного и т.д.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace VelikijSensej51
{
class Program
{
// чтение строк из файла с сохранением в список
static List
{
// особый случай - если файла нет
if (File.Exists(FileName) == false) return null;
List
// открываем файл (2-ой параметр - кодировка)
StreamReader sr = new StreamReader(FileName, Encoding.Default);
string buffer;
// sr.ReadLine() == null - признак конца файла
while ((buffer = sr.ReadLine()) != null)
ReadStrings.Add(buffer);
sr.Close(); // закрываем файл
return ReadStrings;
}
// запись строк из списка в файл
static void WriteToFile(string FileName, List
{
// false: если файл существует, стереть старое содержимое
StreamWriter sw = new StreamWriter(FileName, false, Encoding.Default);
for (int i = 0; i < Spisok.Count; i++)
{
sw.Write(Spisok[i]);
// если строка не последняя, поставить перевод строки
if (i < Spisok.Count - 1) sw.WriteLine();
}
sw.Close();
}
// вывод содержимого списка на экран (отдельным цветом!)
static void ShowList(List
{
Console.ForegroundColor = ConsoleColor.Cyan;
foreach (string str in Spisok)
Console.WriteLine(str);
Console.ForegroundColor = ConsoleColor.Gray;
}
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write("Введите имя исходного файла: ");
string FileName = Console.ReadLine();
List
if (SourceLines == null)
Console.WriteLine("Файл не найден");
else // если файл существует
{
Console.Write("Задайте имя файла результатов: ");
FileName = Console.ReadLine();
Console.WriteLine("Исходные строки");
ShowList(SourceLines);
// переворачивает порядок следования строк в списке
SourceLines.Reverse();
WriteToFile(FileName, SourceLines);
Console.WriteLine("Контрольное чтение из файла " + FileName);
List
ShowList(Reversed);
}
Console.ReadKey();
}
}
}
Часть 1 – результаты
Часть 2 – решение
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace VelikijSensej52
{
class Program
{
[Serializable]
struct Worker // структура РАБОТНИК
{
public string FIO;
public DateTime BirthDay;
public DateTime StartDay; // дата принятия на работу
public string Speciality; // специальность/должность
public override string ToString() // выгрузка в строку
{
string s = FIO + " (д.р. - " + BirthDay.ToString("dd.MM.yyyy") + "), ";
s += " принят " + StartDay.ToString("dd.MM.yyyy") + ", " + Speciality;
return s;
}
};
// распарсивание даты в формате день.месяц.год
// примеры: 1.10.1944, 20.8.2003
static DateTime ParseDate(string DMY)
{
string[] DayMonthYear = DMY.Split('.');
return new DateTime(int.Parse(DayMonthYear[2]), int.Parse(DayMonthYear[1]),
int.Parse(DayMonthYear[0]));
}
static Worker GetWorkersData() // ввод данных о работнике с консоли
{
Worker s;
Console.Write("Введите ФИО: ");
s.FIO = Console.ReadLine();
Console.Write("Введите дату рождения: ");
s.BirthDay = ParseDate(Console.ReadLine());
Console.Write("Введите дату принятия на работу: ");
s.StartDay = ParseDate(Console.ReadLine());
Console.Write("Введите специальность/должность: ");
s.Speciality = Console.ReadLine();
return s;
}
// ввод данных о работниках с записью их файл
static void WriteWorkers(string FileName)
{
Console.Write("Введите количество работников: ");
int n = int.Parse(Console.ReadLine()); // ввести число работников
// файловый поток; назначение - запись
FileStream fs = new FileStream(FileName, FileMode.Create, FileAccess.Write);
BinaryFormatter bf = new BinaryFormatter();
// объект, предоставляющий возможности сериализации
for (int i = 1; i <= n; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Введите информацию о работнике номер " + i + ":");
Console.ForegroundColor = ConsoleColor.White;
Worker s = GetWorkersData();
// сериализация (преобразуем s в поток байтов и записываем в fs)
bf.Serialize(fs, s);
fs.Flush(); // записываем содержимое потока в файл
}
fs.Close(); // закрываем поток
}
// внешняя сортировка файла работников
static void SortWorkers(string FileName)
{
FileStream fs = new FileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
// поток для записи во вспомогательный файл
FileStream f_temp = new FileStream("temp.bin", FileMode.OpenOrCreate, FileAccess.Write);
BinaryFormatter bf = new BinaryFormatter();
int n = 0; // счётчик записей
Worker temp;
while (true)
{
// преобразуем поток байтов в объект типа Student
try { temp = (Worker)bf.Deserialize(fs); }
// исключение возникнет, когда дошли до конца файла
catch { break; }
// если не возникло исключение, значит, была прочитана запись
n++;
}
int index_of_min = -1;
// индексы записей, уже включенных в отсортированную последовательность
// с точки зрения практической задачи, индекс занимает меньше памяти
// по сравнению с целой записью, поэтому может быть ситуация,
// когда список записей не влезут в оперативную память, но список индексов - влезет
List
// cur_min - запись с минимальным возрастом работника из ещё не отсортированных записей
Worker t, cur_min = new Worker();
for (int i = 0; i < n; i++)
{
// устанавливаем положение потока на начало исходного файла
fs.Seek(0, SeekOrigin.Begin);
index_of_min = -1;
// аналог машинной минус бесконечности
cur_min.BirthDay = new DateTime(1900, 1, 1);
for (int j = 0; j < n; j++)
{
// читаем j-ую запись
t = (Worker)bf.Deserialize(fs);
// если j-ая запись ещё не включена в результаты сортировки
if (indexes.Contains(j) == false)
{
// является ли запись t записью с минимальным возрастом?
bool IsNewMin = false;
// если она первая из не отсортированных, считаем - является
if (index_of_min == -1) IsNewMin = true;
else // если нет, то только если возраст
// меньше минимального из найденных ранее
{
if (t.BirthDay.CompareTo(cur_min.BirthDay) > 0)
IsNewMin = true;
}
if (IsNewMin == true) // если требуется обновить минимум возраста
{
cur_min = t; // запоминаем запись с минимумом возраста
index_of_min = j; // запоминаем позицию минимума
}
}
}
// заносим запись с минимумом во вспомогательный файл
bf.Serialize(f_temp, cur_min);
indexes.Add(index_of_min); // запоминаем индекс записи
Console.WriteLine((i + 1) + "-ый по молодости работник: " + cur_min);
}
fs.Close();
f_temp.Close();
File.Delete(FileName); // уничтожаем неотсортированный файл - он не нужен более нам
File.Move("temp.bin", FileName); // переименовываем temp.bin в FileName
}
// чтение списка работников из файла
static List
{
List
BinaryFormatter bf = new BinaryFormatter();
FileStream f = File.OpenRead(FileName);
Worker temp;
while (true)
{
// преобразуем поток байтов в объект типа Worker
try { temp = (Worker)bf.Deserialize(f); }
// исключение возникнет, когда дошли до конца файла
catch { break; }
// если не возникло исключение, добавляем работника в список
lst.Add(temp);
}
f.Close();
return lst; // возвращаем список
}
// показ содержимого списка
static void ShowWorkersList(List
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("{0, 30}{1, 15}{2, 15}{3, 15}", "ФИО", "Д/Р", "Дата принятия", "Должность");
Console.ForegroundColor = ConsoleColor.White;
foreach (Worker p in lst)
{
Console.WriteLine("{0, 30}{1, 15}{2, 15}{3, 15}", p.FIO, p.BirthDay.ToString("dd.MM.yyyy"),
p.StartDay.ToString("dd.MM.yyyy"), p.Speciality);
}
}
// вывод строки s отдельным (голубым) цветом
static void ColourfulComment(string s)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(s);
// после вывода строки снова устанавливаем белый
Console.ForegroundColor = ConsoleColor.White;
}
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.White;
// название типизированного файла
string FileName = "test.bin";
ColourfulComment("Ввод данных о работниках с записью в " + FileName);
// пользователь вводит неотсортированные данные о работниках
// плюс мы записываем их в типизированный файл
WriteWorkers(FileName);
// цветной вывод строки (для лучшего восприятия текста
// в окне нашей программы)
ColourfulComment("\nКонтрольное чтение из неотсортированного файла " + FileName);
// читаем записи из FileName, загружаем их в список
List
ShowWorkersList(lst); // выводим этот список
Console.WriteLine("Нажмите любую клавишу для продолжения");
Console.ReadKey();
ColourfulComment("Сортировка файла");
SortWorkers(FileName); // вызываем метод сортировки
ColourfulComment("\nКонтрольное чтение из отсортированного файла " + FileName);
// как и выше, загружаем данные из файла в список + выводим список
lst = ReadWorkers(FileName);
ShowWorkersList(lst);
Console.ReadKey();
}
}
}
В коде реализована внешняя сортировка сотрудников по возрасту. Можно было просто внести сотрудников в список в оперативной памяти (типа List
Мы же пошли другим путем. Создаем вспомогательный файл и на каждой итерации делаем следующее:
1) находим в исходном файле самого младшего работника из еще не занесенных во вспомогательный файл;
2) заносим этого работника во вспомогательный файл;
3) в отдельный список индексов добавляем индекс этого работника в исходном файле (за счет этого списка мы отличаем, кого занесли во вспомогательный файл, а кого нет).
По завершении всех итераций исходный файл удаляется, а имя вспомогательного меняется на имя исходного.
Такой подход хорош, когда мы не уверены, что нам хватит оперативной памяти для всех хранящихся работников. Список индексов займет намного меньше оперативной памяти, чем список всех работников (элемент списка – число в 4 байта против минимум нескольких десятков байт у структуры Worker). Потому с размещением в оперативной памяти списка индексов гораздо меньше проблем.
Недостаток же этого подхода: больше операций с внешней памятью, чем при варианте с List
Часть 2 – результаты
Введем исходные данные:
Ознакомимся с файлом test.bin в Notepad++, выбрав кодировку UTF8. Типизированные файлы не предназначены для вольного чтения человеком, как текстовые, но это – один из способов хотя бы частично изучить наш файл. Можно отследить число записей (на скриншоте ниже индикаторы несохраненных изменений – потому что мы выбирали кодировку; сохранять изменения не нужно!):
Также можно увидеть строковые значения:
Теперь закроем типизированный файл в Notepad++ (отклонив запрос на сохранение изменений!), после чего вернемся к работе с программой:
Аналогичным способом ознакомимся с test.bin: