You are on page 1of 14

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ

НАЦІОНАЛЬНИЙ АВІАЦІЙНИЙ УНІВЕРСИТЕТ


Факультет кібербезпеки, комп’ютерної та програмної інженерії
Кафедра інженерії програмного забезпечення

Лабораторна робота №1
з дисципліни «Об’єктно-орієнтовне програмування»
«Дослідження роботи з файлами»

Виконав студент:
групи ПІ-223
Черпак Володимир
Перевірив викладач:
Васильєва М.Д.

Київ 2020
Мета роботи – дослідити роботу з файлами з використанням потоків у мові С#
при роботі з різними пов’язаними сутностями.
Завдання
1. Дослідити структуру класів потокового введення/виведення для роботи з
файлами у мові С#.
2. Спроектувати, представити у вигляді діаграми класів функціонал згідно з
варіантом.
3. Розробити програму на мові C#, яка відповідає вимогам у завданні та варіанті.
Для демонстрації роботи використати текстові повідомлення на консолі через
операції вводу-
виводу. Програма повинна показати застосування класів потокового введення/
виведення для роботи з файлами шляхом запису, читання та маніпуляцій над
даними спроектованих сутностей та зв’язків між ними.
4. Діаграма(-и) та вихідний код повинні відповідати базовим принципам
проектування: OOП, composition over inheritance, loose coupling – high cohesion,
inversion of control (IoC).
5. Для отримання балів, що відповідають «задовільно» необхідно реалізувати
завдання, відповідно варіанту. Операції роботи із файлами, вводу-виводу повинні
бути в окремих класах, а не в класах бізне-сутностей.
6. Для отримання балів, що відповідають «добре» повинно бути реалізовано усе, що
на «задовільно». А також:
a) В окремий проект (project) рішення (solution) виділити операції читання-
запису у файл. Тут не повинно бути ніяких маніпуляцій з даними чи ввід-
вивід на консоль. Тобто в рішенні буде мінімум 2 проекти
b) Операції вводу-виводу на екран – в окремому класі ConsoleMenu.
c) Також необхідно використати абстракції (абстрактні класи та інтерфейси),
для демонстрації зв’язків між сутностями у варіанті.
7. Для отримання балів, що відповідають «відмінно», спроектувати зв’язки між
сутностями таким чином, щоб можна було легко додавати інші пов’язані типи
(наприклад, Pupil чи Musician) без необхідності змінювати існуючі. А також – нову
поведінку. Наприклад, коли операцію Play() може виконувати музикант та студент.
8. *Завдання підвищеної складності – працювати з одинм єдиним файлом-джерелом
даних, записуючи та читаючи дані різних типів та сутностей.

Варіант №12
Лістинг коду
Інтерфейс IHuman

public interface IHuman


{
string LastName { get; set; }
string Country { get; set; }
string Passport { get; set; }

bool Study();
bool Teach();
bool Drive();
bool Play();

}
}
Class Musician

public class Musician : IHuman


{
public string LastName { get; set; }
public string MusicInstrument { get; set; }
public string Passport { get; set; }
public string Country { get; set; }

public bool Drive()


{
return false;
}

public bool Play()


{
return true;
}

public bool Study()


{
return false;
}

public bool Teach()


{
return false;
}

}
}

Class Student
public class Student : IHuman
{
File file = new File();

private string lastname;


public string Passport { get; set; }
public string Country { get; set; }
public int Course { get; set; }
public double AVG { get; set; }
public long id { get; set; }

public string LastName


{
get
{

Regex regex = new Regex(@"Фамилия: \w* ");


lastname = regex.Match(s).Value.Split(' ')[1];
return lastname;
}
set { }
}

#region Поведінка
private string s;
public Student(string str)
{
Drive();
Play();
Study();
Teach();

s = str;
}

public bool Drive()


{
return false;
}

public bool Play()


{
return false;
}

public bool Study()


{
return true;
}

public bool Teach()


{
return false;
}
#endregion
}

Class Taxidriver
{
public class TaxiDriver : IHuman
{
public string LastName { get; set; }
public int Experience { get; set; }
public string CarBrand { get; set; }
public string Passport { get; set; }
public string Country { get; set; }

public bool Drive()


{
return true;
}

public bool Play()


{
return false;
}

public bool Study()


{
return false;
}

public bool Teach()


{
return false;
}

}
}
Class Teacher
{
public class Teacher : IHuman
{
public string LastName { get; set; }
public int Experience { get; set; }
public string BranchOfTeach { get; set; }
public string Passport { get; set; }
public string Country { get; set; }

public bool Drive()


{
return false;
}

public bool Play()


{
return false;
}

public bool Study()


{
return false;
}

public bool Teach()


{
return true;
}
}
Class Cons
{
public class Cons
{
File Text = new File();
public void ReadAllItems()
{
Console.WriteLine(Text.ReadAll());
}
public void AddItem(string s)
{
Console.ForegroundColor = ConsoleColor.White;

string line = s + ": ";

Console.WriteLine("Введите фамилию {0}: ", s);


line = line + " Фамилия: " + Console.ReadLine();
if (s == "Студент")
{

Console.WriteLine("Курс {0}: ", s);


line = line + " Курс: " + Console.ReadLine();

Console.WriteLine("Введите студак {0}ID: ", s);


line = line + " " + s + " ID: " + Console.ReadLine();

Console.WriteLine("Оценка {0}(Если отличник то 5): ", s);


line = line + " Оценка: " + Console.ReadLine();

Console.WriteLine("Где проживает студент? 1 - укр 2 - другой вариант");


string switcher = Console.ReadLine();
switch (switcher)
{
case "1":
line = line + "\nМесто проживания : " + "Украина";
break;
case "2":
Console.WriteLine("Введите страну проживания: ");
line = line + "\n Страна : " + Console.ReadLine();

break;

}
else if (s == "Учитель")
{
Console.WriteLine("Опыт работы {0}: ", s);
line = line + " Опыт: " + Console.ReadLine() + " лет";
Console.WriteLine("Направление {0}: ", s);
line = line + " Направление: " + Console.ReadLine();
}
else if (s == "Таксист")
{
Console.WriteLine("Опыт работы {0}: ", s);
line = line + " Опыт: " + Console.ReadLine() + " лет";
Console.WriteLine("Машина {0}: ", s);
line = line + " Машина: " + Console.ReadLine();
}
else if (s == "Музыкант")
{
Console.WriteLine("Музыкальный инструмент {0}: ", s);
line = line + " Муз.инструмент: " + Console.ReadLine();
}
Console.WriteLine("Данные паспорта {0}: ", s);
line = line + " Паспорт: " + Console.ReadLine();

Text.Write(line);

}
}
}
Class File
{
public class File
{

public string Read()


{
FileStream file = new FileStream("input.txt", FileMode.OpenOrCreate,
FileAccess.ReadWrite);
StreamReader reader = new StreamReader(file);
string s = "1", temp = " ";
reader.BaseStream.Seek(0, SeekOrigin.Begin);
while (s != null)
{
temp = s;
s = reader.ReadLine();
}
s = temp;
reader.Close();
return s;
}

public void Write(string s)


{
FileStream file = new FileStream("input.txt", FileMode.Append, FileAccess.Write);
StreamWriter writer = new StreamWriter(file);
writer.BaseStream.Seek(0, SeekOrigin.End);
writer.WriteLine(s);
writer.Close();
}

public string ReadAll()


{
FileStream file = new FileStream("input.txt", FileMode.OpenOrCreate,
FileAccess.ReadWrite);
StreamReader reader = new StreamReader(file);
string temp = reader.ReadToEnd();
reader.Close();
return temp;
}

public string[] ReadAllLines()


{

FileStream file = new FileStream("input.txt", FileMode.OpenOrCreate,


FileAccess.ReadWrite);
StreamReader reader = new StreamReader(file);
string temp = reader.ReadToEnd();
string[] s = temp.Split('\n');
reader.Close();
return s;
}
}
}
Class Program
{
class Program
{
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.White;

Cons console = new Cons();


File file = new File();
Student[] st = new Student[] { };

string switcher = " ";


do
{

Console.WriteLine("1. Показать всех");


Console.WriteLine("2. Добавить");
Console.WriteLine("3. Подсчитать индивидуальное");
Console.WriteLine("0. Выход");
Console.Write("Введите операцию: ");
Console.WriteLine();

switcher = Console.ReadLine();
switch (switcher)
{
case "1":
Console.Clear();
console.ReadAllItems();
break;

case "2":
Console.Clear();
Console.WriteLine("Кого хотите добавить?");
Console.WriteLine("1. Студент");
Console.WriteLine("2. Учитель");
Console.WriteLine("3. Таксист");
Console.WriteLine("4. Музыкант");

int switcher2 = 0;
do
{

Console.WriteLine("Выберите вариант: ");


Int32.TryParse(Console.ReadLine(), out switcher2);
Console.Clear();
switch (switcher2)
{
case 1:
console.AddItem("Студент");
Student temp = new Student(file.Read());
Student[] temp1 = new Student[st.Length + 1];
temp1[temp1.Length - 1] = temp;
int i = 0;
while (i < st.Length)
{
temp1[i] = st[i];
i++;
}
st = temp1;
break;
case 2:
console.AddItem("Учитель");
Teacher teacher = new Teacher();
break;
case 3:
console.AddItem("Таксист");
TaxiDriver driver = new TaxiDriver();
break;
case 4:
console.AddItem("Музыкант");
Musician musician = new Musician();
break;
default:
Console.WriteLine("Неправильный выбор!");
break;

} while (switcher2 < 1 || switcher2 > 4);

break;

case "3":
string[] str = new string[file.ReadAllLines().Length];
Regex regex4 = new Regex(@"Студент");
int k = 0;
for (int j = 0; j < file.ReadAllLines().Length; j += 2)
{

if (regex4.IsMatch(file.ReadAllLines()[j]))
{
str[k] = file.ReadAllLines()[j] + file.ReadAllLines()[j + 1];
}
else
{
str[k] = file.ReadAllLines()[j];
j--;
}
k++;

}
Regex regex1 = new Regex(@" Страна :");
Regex regex2 = new Regex(@"Курс: 1");
Regex regex3 = new Regex(@" Оценка: 5");
int amount = 0;
int s = 0;
while (str[s] != null)
{

if (regex1.IsMatch(str[s]) && regex2.IsMatch(str[s]) &&


regex3.IsMatch(str[s]))
{
Console.WriteLine(str[s]);
amount++;
}
s++;

}
Console.WriteLine("Количество: {0}", amount);

break;

} while (switcher != "0");


}

}
}

Контрольні питання:
1. Що таке потік?.
Потік - це джерело введення та/або виведення даних, зазвичай байтів,
пов'язаних з файлом, пристроєм або іншим процесом.
Режими відкриття FileMode:
2. Наведіть приклади режимів відкриття файлів.
- Append – відкриває файл (якщо існує) і переводить вказівник в кінець
файлу (дані будуть дописуватися в кінець), або створює новий файл. Даний режим
можливий тільки при режимі доступу FileAccess.Write.
- Create - створює новий файл (якщо існує – заміняє);
- CreateNew – створює новий файл (якщо існує – генерується виключення);
- Open - открывает файл (якщо не існує – генерується виключення);
- OpenOrCreate – відкриває файл, або створює новий, якщо його не існує;
- Truncate – відкриває файл, але всі дані всередині файлу затирає (якщо файлу
не існує – генерирується виключення).
3. Поясніть різницю між файлами з послідовним та довільним доступом.
Нерідко бінарні файли представляють певну структуру. І, знаючи цю
структуру, ми можемо взяти з файлу потрібну порцію інформації або навпаки
записати в певному місці файлу певний набір байтів. Наприклад, в wav-файлах
безпосередньо звукові дані починаються з 44 байту, а до 44 байту йдуть різні
метадані - кількість каналів аудіо, частота дискретизації і т. д.
За допомогою методу Seek () ми можемо управляти положенням курсору потоку,
починаючи з якого відбувається зчитування або запис в файл. Цей метод приймає
два параметри: offset (зміщення) і позиція в файлі. Позиція в файлі описується
трьома значеннями:
SeekOrigin.Begin: початок файлу
SeekOrigin.End: кінець файлу
SeekOrigin.Current: поточна позиція в файлі
Курсор потоку, з якого починається читання або запис, зміщується вперед на
значення offset відносно позиції, зазначеної в якості другого параметра. Зміщення
може бути негативним, тоді курсор зсувається назад, якщо позитивне - то вперед.
4. Поясніть, як реалізується довільний доступ у файлах.
Файли з довільним доступом – файли, що зберігають інформацію в
структурованому (для пошуку і звернення) вигляді. Пошук в таких файлах
здійснюється в області адрес (ключів) і завершується зверненням безпосередньо до
шуканої ділянки.
5. Опишіть ієрархію класів потокового введення/виведення в С#.
BinaryWriter/Reader – двійкові потоки.
StreamWriter/Reader – символьні потоки.
Stream – байтові потоки.
6. Наведіть методи читання/запису для потоку FileStream в C#.
Метод Read(): зчитує дані з файлу в масив байтів. Приймає параметри:
int Read(byte[] array, int offset, int count)
Повертає кількість успішно зчитаних байтів.
array – масив байтів, куди будуть поміщені зчитувані з файлу дані.
offset – зміщення в байтах в масиві array, в який зчитані байти будуть поміщені.
count – максимальне число байтів, призначених для читання. Якщо у файлі
знаходиться менша кількість байтів, то будуть прочитані всі байти.
Метод Write(): зчитує дані з файлу в масив байтів. Приймає параметри:
Write(byte[] array, int offset, int count)
array – масив байтів, з якого дані записуються в файл.
offset – зміщення в байтах в масиві array, звідки починається запис байтів в потік.
count – максимальне число байтів, призначених для запису.
7. Поясніть, як створити символьний потік в C#.
Для створення символьного потоку достатньо заключити байтовий потік в один з
класів-оболонок символьних потоків. На вершині ієрархії класів символьних
потоків знаходяться абстрактні класи TextReader і TextWriter.
Читання з файлу та StreamReader
Клас StreamReader дозволяє нам легко зчитувати весь текст або окремі рядки з
текстового файлу. Серед його методів можна виділити наступні:
Close: закриває зчитування файл і звільняє всі ресурси
Peek: повертає наступний доступний символ, якщо символів більше немає, то
повертає -1
Read: зчитує і повертає наступний символ в чисельному поданні. Має
перевантажену версію: Read (char [] array, int index, int count), де array - масив, куди
зчитуються символи, index - індекс в масиві array, починаючи з якого записуються
зчитувальні символи, і count - максимальна кількість зчитувальних символів
ReadLine: зчитує один рядок в файлі
ReadToEnd: зчитує весь текст з файлу
Запис в файл та StreamWriter
Для запису в текстовий файл використовується клас StreamWriter. Свою
функціональність він реалізує через такі методи:
Close: закриває записується файл і звільняє всі ресурси
Flush: записує в файл залишилися в буфері дані і очищає буфер.
Write: записує в файл дані найпростіших типів, як int, double, char, string і т.д.
WriteLine: також записує дані, тільки після запису додає в файл символ закінчення
рядка.
8. Поясніть, як виконується перенаправлення потоків в C#.
Для переадресації введення викликається метод SetIn() із зазначенням необхідного
потоку. З цією метою може бути використаний будь-який потік введення, за умови,
що він є похідним від класу TextReader. А для переадресації виведення
викликається метод SetOut() із зазначенням необхідного потоку виведення, який
повинен бути також похідним від класу TextWriter. Так, для переадресації
виведення в файл досить вказати об'єкт класу FileStream, заключений в оболонку
класу StreamWriter
9. Наведіть приклади методів для читання/запису значень наперед визначених типів
у двійкові потоки в C#.
Для роботи з бінарними файлами призначена пара класів BinaryWriter і
BinaryReader. Ці класи дозволяють читати і записувати дані в двійковому форматі.
Основні методи класу BinaryWriter
Close(): закриває потік і звільняє ресурси
Flush(): очищає буфер, дописуючи з нього дані, які лишилися в файл
Seek(): встановлює позицію в потоці
Write(): записує дані в потік
Основні методу класу BinaryReader
Close (): закриває потік і звільняє ресурси
ReadBoolean (): зчитує значення bool і переміщає покажчик на один байт
ReadByte (): зчитує один байт і переміщає покажчик на один байт
ReadChar (): зчитує значення char, тобто один символ, і переміщує покажчик на
стільки байтів, скільки займає символ в поточній кодуванні
ReadDecimal (): зчитує значення decimal і переміщає покажчик на 16 байт
ReadDouble (): зчитує значення double і переміщає покажчик на 8 байт
ReadInt16 (): зчитує значення short і переміщає покажчик на 2 байта
ReadInt32 (): зчитує значення int і переміщає покажчик на 4 байта
ReadInt64 (): зчитує значення long і переміщає покажчик на 8 байт
ReadSingle (): зчитує значення float і переміщає покажчик на 4 байта
ReadString (): зчитує значення string. Кожен рядок передує значенням довжини
рядка, яке представляє 7-бітове ціле число
10. Посніть використання регулярних виразів.
Регулярні вирази надають потужний, гнучкий і ефективний спосіб обробки тексту.
Комплексна нотація зіставлення шаблонів регулярних виразів дозволяє швидко
аналізувати великі обсяги тексту для пошуку певних шаблонів символів, перевіряти
текст на відповідність визначеним шаблоном (наприклад, адресу електронної
пошти), вилучати, змінювати, замінювати і видаляти текстові підрядки, а також
додавати вилучені рядки в колекцію для створення звіту. Для багатьох додатків, які
працюють з рядками або аналізують великі блоки тексту, регулярні вираз
11. В чому відмінність між композицією та наслідуванням. Який зв’язок і коли
варто використовувати? Чому?
Композиція визначає ставлення HAS A, тобто відношення "має". Наприклад, в клас
автомобіля містить об'єкт класу електричного двигуна
При цьому клас автомобіля повністю управляє життєвим циклом об'єкта двигуна.
При знищенні об'єкта автомобіля в області пам'яті разом з ним буде знищений і
об'єкт двигуна. І в цьому плані об'єкт автомобіля є головним, а об'єкт двигуна -
залежною.
12. Поясніть твердження «composition over inheritance». Чому воно виникло?
Наслідування є найсильнішим зв'язком між двома класами, який неможливо
розірвати під час виконання. Наслідування порушує інкапсуляцію. Зміни в
батьківському класі впливають на бізнес-логіку дочірніх класів.
Композиція ж дозволяє динамічно визначати поведінку об'єкта під час виконання, і
тому є більш гнучкою. При композиції зберігається інкапсуляція.
13. В чому різниця між абстрактним класом та інтерфейсом?
Абстрактний клас - це клас, який може використовуватися лише в якості базового
класу для деякого іншого класу. Клас є абстрактним, якщо він містить хоча б одну
чисту віртуальну функцію. Абстрактний клас не можна вживати в якості типу
об'єктів, типу параметрів функцій, типу значення, що повертається функцією або як
тип при явному приведенні типу. Можна, однак, оголошувати покажчики і
посилання на абстрактний клас.
Модифікатор abstract вказує на те, що даний клас можна використовувати тільки як
базовий клас для наслідування
У самому по собі абстрактному класі, від якого ніхто не успадковується, сенсу
немає, так як не можна створювати його екземпляри. В абстрактному класі зазвичай
реалізується деяка загальна частина декількох сутностей або іншими словами -
абстрактна сутність, яка, як об'єкт, не може існувати, і ця частина необхідна в
класах спадкоємців.
Оголошуємо абстрактний клас Figure, від якого проводимо два класу — Rectangle
(клас прямокутника) і Triangle (трикутника). У класі Figure є два абстрактних
методу — square (для підрахунку площі) і perimeter (для периметра). Так як для
довільної фігури формули для площі і для периметра не існує, то ці методи
оголошені в класі Figure і перевизначені в похідних класах (з ключовим словом
override).
Інтерфейси - це ще один інструмент реалізації поліморфізму в Сі-шарп.
За допомогою інтерфейсів можна, наприклад, включити в клас поведінку з
декількох джерел. Крім того, необхідно використовувати інтерфейс, якщо потрібно
імітувати успадкування для структур, оскільки вони не можуть фактично
успадковувати від іншої структури або класу.
З точки зору синтаксису інтерфейси подібні абстрактним класах. Але в інтерфейсі
ні у одного з методів не повинно бути тіла. Це означає, що в інтерфейсі взагалі не
надається ніякої реалізації. Тому всі методи інтерфейсу повинні бути реалізовані в
кожному класі, що включає в себе цей інтерфейс. У самому ж інтерфейсі методи
неявно вважаються відкритими, тому доступ до них не потрібно вказувати явно.У
ньому вказується тільки, що саме слід робити, але не як це робити. Як тільки
інтерфейс буде визначено, він може бути реалізований в будь-якій кількості класів.
Крім того, в одному класі може бути реалізовано будь-яку кількість інтерфейсів.
Інтерфейси оголошуються за допомогою ключового слова interface
Як тільки інтерфейс буде визначено, він може бути реалізований в одному або
декількох класах. Для реалізації інтерфейсу досить вказати його ім'я після імені
класу, аналогічно базовому класу. загальна форма реалізації інтерфейсу в класі.
class ім'я_класу: імя_інтерфейса
{
// тіло класу
}
де імя_інтерфейса - це конкретне ім'я реалізованого інтерфейсу. Якщо вже
інтерфейс реалізується в класі, то це повинно бути зроблено повністю. Зокрема,
реалізувати інтерфейс вибірково і тільки по частинах не можна.
14. В чому полягає користь використання абстракцій замість конкретних
реалізацій? Наведіть приклади.
У класі допускається реалізовувати кілька інтерфейсів. В цьому випадку всі
реалізовані в класі інтерфейси вказуються списком через кому. У класі можна
успадковувати базовий клас і в той же час реалізувати один або більше інтерфейс.
У такому випадку ім'я базового класу має бути зазначено перед списком
інтерфейсів, що розділяються комою. Методи, що реалізують інтерфейс, повинні
бути оголошені як public. Справа в тому, що в самому інтерфейсі ці методи неявно
маються на увазі як відкриті, тому їх реалізація також повинна бути відкритою.
Крім того, повертається тип і сигнатура реалізованого методу повинні точно
відповідати що повертається типу і сигнатуре, зазначеним у визначенні інтерфейсу.

15. В чому полягає принцип інверсії контролю (inversion of control – IoC)?


Інверсія контролю - це принцип побудови програми, при якому її частини
отримують потік керування (викликаються) із загальної спільновикористовуваної
бібліотеки. Це ніби звичайне процедурне програмування вивернуте навиворіт
(inversed). Також це називають «голлівудським принципом»: «Не дзвоніть нам, ми
подзвонимо вам».

You might also like