Міністерство освіти і науки України
Національний університет "Львівська політехніка"
Кафедра "Інформаційні системи та мережі "
Р О З Р А Х У Н К О В А Р О Б О Т А
з дисципліни "Проблемно-орієнтовані мови програмування"
на тему:
Робота зі структурованими даними на мові Turbo C
студенту групи КН-17 Зубику В.І.
Тема: « Робота зі структурованими даними на мові Turbo C»
Завдання:
Ввести з клавіатури масив рядків символів з даними про продукцію на складі: назва виробу, код виробу, кількість виробів, ціна 1 виробу, дата. Виділити складові частини рядків та записати їх у відповідні поля масиву структур. Переписати масив структур в двонаправлений лінійний список динамічної пам’яті. Впорядкувати список по назвах виробів. Вивести список на екран у прямому та зворотному порядку. Переписати список у двійковий файл. Знайти вартість продукції заданого коду. Вартість = кількість виробів*ціна 1 виробу. Вивести файл на екран у формі таблиці. Усі дії оформити у вигляді окремих функцій.
ЗМIСТ ЗАВДАННЯ ТА КАЛЕНДАРНИЙ ПЛАН ЙОГО ВИКОНАННЯ
1.
Провести вивчення лiтературних джерел по заданій темі.
2.
Розробити алгоритм розв’язування задачі
3
Написати програму на мові Turbo C
4.
Підготувати вхiднi дані для контрольного прикладу, реалізувати та вiдлагодити програму
5.
Оформити записку до розрахункової роботи згідно вимог
Міжнародних стандартів, дотримуючись такого змісту:
- вступ;
- формулювання задачі;
- алгоритм розв'язування задачі
- опис програми;
- інструкція користувачеві;
- контрольний приклад та аналіз результатів;
- висновки;
- література;
- додатки
ЗАВДАННЯ ПРИЙНЯТО ДО ВИКОНАННЯ: ____________
пiдпис студента
Керівник роботи: _______________ Кравець П.О.
Формулювання задачі.
Формулювання задачі: Програма може виконуватися на операційній системі DOS, та її емуляцією під Windows. Для її виконання потрібні мінімальні характеристики комп’ютера, головне обладнання: клавіатура, пристрій виведення інформації, та будь-який носій даних (з файловою системою FAT для операційної системи DOS). Вхідні дані передаються в програму за допомогою клавіатури, введення супроводжується підказками. Програма оформлена кольоровим інтерфейсом. Для коректного сприйняття даних в стрічках потрібно вживати пробіл, як роздільник. Числові дані вводяться в десятковій системі. Вихідні дані виводяться у вигляді таблиці.
Методи та засоби розв’язування задачі.
Для розв’язування задачі потрібно добре знати такі головні теми: робота з рядками символів, масивами, структурами, лінійними списками, двійковими файлами.
Рядки. У мові програмування С не визначено спеціального типу для опрацювання рядків. Рядок символів розглядається як масив елементів типу char, який закінчується символом ‘\0’(нуль-символ), що є ознакою кінця рядка. Сталі типу рядок записуються у лапках. У сталих рядках нуль-символ записується автоматично.
Підчас оголошення символьного масиву необхідно до фактичної довжини рядка додати одиницю для нульового символа (не у всіх компіляторах). Якщо масив символів оголошують й ініціалізують одночасно, то довжину можна не зазначати, компілятор визначить її. Оскільки рядки є масивами символів, то назва рядка вказівником на його початок (на його перший елемент). Для опрацювання масивів символів у мові С є стандартні функції, які описуються у модулі string.h.
Масиви. Масив – впорядкований скінчений набір даних одного типу, які зберігаються в послідовно розташованих комірках оперативної пам’яті і мають спільну назву. Назву масиву дає користувач.
Масив складається з елементів. Кожен елемент має індекс, за яким його можна знайти у масиві. Кількість Індексів визначає розмірність масиву. Розрізняють одно та багатовимірні масиви. Двовимірний масив – це таблиця яка складається з декількох рядків і стовпців. У математиці поняттю масив відповідає поняття вектора та матриці.
Розмір – це кількість елементів масиву. Розмір масиву треба задати заздалегідь, оскільки компілятор має зарезервувати для нього необхідний обсяг пам’яті. Розміром може бути лише стала величина.
Звернутись до елементів масиву можна двома способами: за допомогою імені масиву та використовуючи вказівники. Нумерація елементів масиву починається від нуля.
Проініціалізувати масив можна одним із способів:
Використовуючи принцип замовчування;
Безпосередньо підчас його оголошення;
Застосовуючи команду присвоювання;
Підчас введення даних з клавіатури.
За замовчанням усім елементам масиву надається значення 0.
Підчас компіляції програмного коду для статичного оголошення масивів надається пам'ять. Для ефективного використання пам'яті призначене динамічне оголошення масивів. Динамічною змінною можна використовувати операції, визначені для даних відповідного базового типу.
Якщо елемент масиву має не один а декілька індексів, то такі масиви називаються багатовимірними. Прикладами багатовимірних масивів можуть бути різноманітні табличні дані. Двовимірному масиву у математиці відповідає поняття матриці.
Кількість індексів визначає вимірність масиву. Усі двовимірні масиви можна опрацьовавути як одновимірні.
Елементи двовимірного масиву визначаються іменем масиву та двома індексами. Перший індекс означає номер рядка а другий номер стовпця. Двовимірні масиви компілятор розглядає як послідовність одновимірних, тому до елементів можна звертатись і через вказівники. В такому випадку це вказівника на вказівник одновимірного масиву.
Структури. Структури дозволяють об'єднувати в єдиному об'єктi сукупнiсть значень, якi можуть мати рiзнi типи. Оголошення структури здiйснюється за допомогою ключового слова struct.
Синтаксис опису структури виглядає так :
struct [iм'я_структури]
{
тип1 елемент1;
тип2 елемент2;
........................
типN елементN;
} [список описiв];
З метою ознайомлення з цим типом даних розглянемо найпростiший приклад представлення поняття "дата", що складається з декiлькох частин: число (день, мiсяць, рiк), назва тижня та мiсяця:
struct date {
int day ;
int month ;
int year;
char day_name[15];
char mon_name[14];
} arr[100],*pd,data,new_data;
В даному прикладi оголошуються:
data, new_data - змiннi типу структури date;
pd - вказiвник на тип data
arr - масив iз 100 елементiв, кожний елемент якого має тип date.
Потрiбно вiдзначити, що на вiдмiну вiд описiв iнших типiв даних, опис структури не видiляє мiсця у пам'ятi пiд елементи структури. Її опис визначає лише так званий шаблон, що описує характеристики змiнних, що будуть розмiщуватися у конкретнiй структурi. Щоб ввести змiннi та зарезервувати для них пам'ять необхiдно або пiсля фiгурної дужки, що завершує опис структури, вказати список iдентифiкаторiв.
Як i звичайними масивами простих типiв, так само можна оперувати масивами структур, елементи якого мають структурований тип. Розглянемо наочний зразок, який iлюструє оголошення масиву структур:
typedef struct Date
{
int d; /* день */
int m; /* мiсяць */
int y; /* рiк */
} Date;
Date arr[100];
Вище було оголошено масив arr, що складається iз 100 елементiв, кожний з яких має тип Data. Кожний елемент масиву - це о крема змiнна типу Data, що складається iз трьох цiлих елементiв - d, m, y.
Доступ до полiв структури аналогiчний доступу до звичайних змiнних, плюс використання iндексу номеру елементу у квадратних дужках:
arr[25].d=24;
arr[12].m=12;
Лінійні списки. Лінійний список - це скінченна послідовність однотипних елементів (вузлів). Кількість елементів у цій послідовності називається довжиною списку. Наприклад :
F=(1,2,3,4,5,6) - лінійний список, його довжина 6.
При роботі зі списками дуже часто доводиться виконувати такі операції :
• додавання елемента в початок списку;
• вилучення елемента з початку списку;
• додавання елемента в будь-яке місце списку;
• вилучення елемента з будь-якого місця списку;
• перевірку, чи порожній список;
• очистку списку;
• друк списку.
Основні методи зберігання лінійних списків поділяються на методи послідовного та зв'язаного зберігання.
Послідовне зберігання списків. Метод послідовного зберігання списків ґрунтується на використанні масиву елементів деякого типу та змінної, в якій зберігається поточна кількість елементів списку.
#define MAX 100 /* максимально можлива довжина списку */
typedef struct
{
int x; /* тут потрібно описати структуру елементів списку*/
} elementtype;
typedef struct
{
elementtype elements[MAX];
int count;
} listtype;
У вищенаведеному фрагменті програми описуються типи даних elementtype (визначає структуру елемента списку) та listtype (містить масив для зберігання елементів та змінну для зберігання поточного розміру списку).
Наводимо приклади реалізації функцій для виконання основних операцій над списками.
1. Ініціалізація списку (робить список порожнім).
void list_reset(listtype *list)
{
list->count=0;
};
2. Додавання нового елементу у кінець списку.
void list_add(listtype *list,elementtype element)
{
if (list->count==MAX) return;
list->elements[list->count++]=element;
};
3. Додавання нового елементу в позицію pos.
void list_insert(listtype *list,int pos,elementtype element)
{
int j;
if (pos<0||pos>list->count||pos>=MAX) return;
for (j=list->count;j>pos;j--)
{
list->elements[j]=list->elements[j-1];
};
list->elements[j]=element;
list->count++;
};
4. Вилучення елемента з номером pos.
void list_delete(listtype *list,int pos)
{
int j;
if (pos<0||pos>list->count) return;
for (j=pos+1;j<list->count;j++)
{
list->elements[j-1]=list->elements[j];
};
list->count--;
};
5. Отримання елемента з номером pos.
int list_get(listtype *list,int pos,elementtype *element)
{
if (pos<0||pos>list->count)
{
return 0;
};
*element=list->elements[pos];
return 1;
};
При послідовному зберіганні списків за допомогою масивів елементи списку зберігаються в масиві. Така організація дозволяє легко переглядати вміст списку та додавати нові елементи в його кінець. Але такі операції, як вставка нового елемента в середину списку чи вилучення елементу з середини списку потребують зсуву всіх наступних елементів. При збільшенні елементів масиву кількість операцій, потрібна для впорядкування списку, стрімко зростає.
Зв'язане зберігання лінійних списків. Найпростіший спосіб зв'язати множину елементів - зробити так, щоб кожний елемент містив посилання на наступний. Такий список називається односпрямованим (однозв'язаним). Якщо додати в такий список ще й посилання на попередній елемент, то отримаємо двозв'язаний список. А список, перший та останній елементи якого зв'язані, називається кільцевим.
Структуру даних для зберігання односпрямованого лінійного списку можна описати таким чином :
typedef long elemtype;
typedef struct node
{
elemtype val;
struct node *next;
} list;
В даному фрагменті програми описуються декілька типів даних :
elemtype - визначає тип даних лінійного списку. Можна використовувати будь-який стандартний тип даних, включаючи структури.
list - визначає структуру елемента лінійного списку (val - значення, яке зберігається у вузлі, next - покажчик на наступний вузол).
Схематично лінійний односпрямований список виглядає так :
Реалізація основних операцій :
1. Включення елемента в початок списку.
list *addbeg(list *first, elemtype x)
{
list *vsp;
vsp = (list *) malloc(sizeof(list));
vsp->val=x;
vsp->next=first;
first=vsp;
return first;
}
2. Видалення елемента з початку списку.
list *delbeg(list *first)
{
list *vsp;
vsp=first->next;
free(first);
return vsp;
}
3. Включення нового елемента у список.
list *add(list *pred, elemtype x)
{
list *vsp;
vsp = (list *) malloc(sizeof(list));
vsp->val=x;
vsp->next=pred->next;
pred->next=vsp;
return vsp;
}
4. Видалення елемента зі списку.
elemtype del(list *pred)
{
elemtype x;
list *vsp;
vsp=pred->next;
pred->next=pred->next->next;
x=vsp->val;
free(vsp);
return x;
}
5. Друк значень списку.
void print(list *first)
{
list *vsp;
vsp=first;
while (vsp)
{
printf("%i\n",vsp->val);
vsp=vsp->next;
}
}
6. Перевірка, чи порожній список
int pust(list *first)
{
return !first;
}
7. Знищення списку
list *kill(list *first)
{
while (!pust(first)) first=delbeg(first);
return first;
}
Двійкові файли. Двійковий файл представляє собою просто послідовність символів. Що саме і в якій послідовності зберігається в двійковому файлі - повинна знати програма.
Двійкові файли мають переваги, порівняно з текстовими при зберіганні числових даних. Операції читання і запису з такими файлами виконуються набагато швидше, ніж з текстовими, так як відсутня необхідність форматування (переведення в текстове представлення та навпаки). Двійкові файли зазвичай мають менший розмір, ніж аналогічні текстові файли. В двійкових файлах можна переміщуватися в будь-яку позицію і читати або записувати дані в довільній послідовності, в той час, як в текстових файлах практично завжди виконується послідовна обробка інформації.
Про те, як відкривати двійкові файли було згадано раніше. Запис і читання в двійкових файлах виконується відповідно функціями fwrite і fscanf.
size_t fwrite(const void *ptr, size_t size, size_t n, FILE*stream);
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);
В обидві функції повинен передаватися покажчик ptr на дані, які вводяться або виводяться. Параметр size задає розмір в байтах даних, які читаються або записуються.
#include<stdio.h>
#include<conio.h>
struct mystruct
{
int i;
char ch;
};
int main(void)
{
FILE *stream;
struct mystruct s;
if ((stream = fopen("test.txt", "wb")) == NULL)
{
fprintf(stderr, "Неможливо відкрити файл\n");
return 1;
}
s.i = 0;
s.ch = 'A';
fwrite(&s, sizeof(s), 1, stream);
fclose(stream);
return 0;
}
Тепер розглянемо особливості записування і читання рядків.
char s[10];
strcpy(s, "Example");
…
fwrite(s,strlen(s)+1,sizeof(char),stream);
Записування рядків відбувається посимвольно. В даному прикладі число символів, які записуються - strlen(s)+1 (одиниця додається на нульовий символ в кінці). Читається рядок аналогічно:
fread(s,strlen(s)+1,sizeof(char),stream);
При цьому читання проходить теж посимвольно.
Дуже часто доводиться працювати з рядками різних довжин. В таких випадках можна перед рядком записати у файл ціле число, яке рівне числу символів у рядку.
…
int i=strlen(s)+1;
fwrite(&i,1,sizeof(int),stream);
fwrite(s,i,1,stream);
…
fread(&i,1,sizeof(int),stream);
fread(s,i,1,stream)
В усіх наведених вище прикладах читання даних проходило послідовно. Але, працюючи з двійковими файлами, можна організувати читання даних в довільному порядку. Для цього використовується "покажчик файла" (курсор), який визначає поточну позицію у файлі. При читанні даних курсор автоматично зміщується на число прочитаних байтів. Отримати поточну позицію курсору файла можна за допомогою функції ftell().
long ftell(FILE *stream);
А встановлюється поточна позиція курсору у файлі за допомогою функції fseek():
int fseek(FILE *stream, long offset, int whence);
Ця функція задає зміщення на число байтів offset від точки відліку, яка визначається параметром whence. Цей параметр може приймати значення 0, 1, 2.
Алгоритм розв'язування задачі.
Написання програми я почав з підключення директив препроцесора. Після чого оголосив головну функцію void main(). Оголосив масив стрічк. Кількість елементів масиву я визначив директивою препроцесора define. Далі організував виведення підказок для користувача. Розробив функцію введення масиву стрічок void vved(char p[n][m]). Далі оголосив структуру Ttovar. Розробив функцію переписування масиву стрічок в масив структур void vstr(char p[n][m], Ttovar *tov). Структура Ttovar містить 3 поля типу char: nazva, kod, data, одне типу int kstj і одне типу float cina.
Поле nazva – назва товару, kod – код товару, data – дата, kstj – кількість одиниць продукції, cina – ціна одного товару.
Для виділення ключових полів структури в стрічці я використав функцію введення sscanf. Далі відповідно до умови завдання розробив функцію переписування структури в лінійний двонаправлений список void vsps(LDsps *sp, Ttovar *tov), попередньо розробивши функцію формування нового елемента списку з елемента структури. LDsps *Csps(Ttovar tov). В лінійному списку є ті самі поля що і в структурі, і крім того ще два поля: *next – вказівник на наступний елемент списку, *prew – вказівник на попередній елемент списку. Також я оголосив дві глобальні змінні *head – вказівник на початок списку, *tail – вказівник на кінець списку. Далі я розробив функцію виведення списку в прямому порядку void vyvh(LDsps *sp), і зворотному порядку void vyvt(LDsps *sp). Після чого розробив функцію сортування лінійного двонаправленого списку. Попередньо розробив функцію присвоювання одного елемента списку іншому void copy(LDsps *tm, LDsps *nd). Сортування проводив по назвах виробів. Використав прямий метод сортування. Для порівняння назв використав функцію порівняння стрічок strcmp. Після чого розробив функцію запису лінійного двонаправленого списку у двійковий файл void vfail(LDsps *sp, FILE *f). Згідно з умовою наступним кроком було розроблення функції для знаходження вартостості продукції заданого коду. Передбачив у функції виведення помилки, якщо користувач введе неіснуючий код виробу. Останнім кроком стала розробка функції виведення даних з файлу у формі таблиці. Далі здійснював виклик функцій у головній програмі передаючи відповідні параметри. Після завершення роботи програми передбачив затримку роботи програми для перегляду результатів. Добавив кольоровий інтерфейс програми використовуючи функцію textcolor(…). Перед виходом з програми після затримки я викликав функцію виходу з програми без помилки (exit(0);) Через виведення помилок при завершенні програми, коли в програмі більше трьох елементів.
Перед початком роботи програми я передбачив функцію виведення прізвища автора програми, дати створення, теми розрахункової роботи.
Графічна схема програми:
Головна програма:
Функція void vved(char p[n][m]):
Функція void vstr(char p[n][m], Ttovar *tov):
Функція void vyvt(LDsps *sp):
Функція void vyvh(LDsps *sp):
Функція void vsps(LDsps *sp, Ttovar *tov):
Функція void sort(LDsps *sp):
Функція void vfail(LDsps *sp, FILE *f):
Функція float zdk(FILE *f,char zkod[7]):
Функція void fvyv(FILE *f):
Опис програми.
Назва програми:
RZV.cpp;
Призначення програми:
Формування даних про товари, визначення вартості продукції заданого коду;
Мови програмування на яких написана програма:
Сі — мінімалістична мова програмування. Серед її головних цілей: можливість прямолінійної реалізації компіляції, використовуючи відносно простий компілятор, забезпечити низькорівневий доступ до оперативної пам'яті, формувати лише декілька інструкцій машинної мови для кожного елементу мови, і не вимагати обширної динамічної підтримки. У результаті, код Сі придатний для більшості системного програмного забезпечення, котре традиційно писалося на аcемблері.
Незважаючи на її низькорівневі можливості, мова проектувалася для машинно-незалежного програмування. Сумісна зі стандартами та машинно-незалежно написана програма на Сі може легко компілюватися на великій кількості апаратних платформ та операційних систем з мінімальними змінами. Мова стала доступною для великої кількості платформ, від вбудованих мікроконтроллерів до суперкомп'ютерів.
Логічна структура програми:
Дії у програмі оформлені у вигляді окремих функцій. Є функція для введення масиву стрічок, перетворення в структуру, переписування в лінійний двонаправлений список. Функція сортування списку по назвах виробів. Ця функція також використовує спеціально розроблену функцію для присвоювання одному елементу списку іншого. Ця функція використовується тільки у функції сортування. Також є ще одна функція яка не використовується головною програмою. Вона пизначена для формування елемента лінійного двонаправленого списку з елемента структури. Також є дві функції для виведення списку в прямому і зворотньому порядку. Для таких операцій були введені глобальні змінні – вказівники типу лінійного списку, які показують на початок і відповідно на кінець списку.
У функції запису у файл було вибрано ім’я файлу FILE з розширенням BIN, який розміщується в корені диску С. Після запису файл закривається для збереження даних. У функції виведення вмісту файлу у формі таблиці вводиться додаткова змінна р типу лінійного списку. У функції введення передється сам масив стрічок. У функцю перетворення в структуру передається масив стрічок і масив структур, в який буде здійснюватись перетворення. У функцію формування елемента списку передається елемент структури. У функціях виведення лінійного двонаправленого списку у прямому і зворотньому порядку передається тільки вказівник на список. У функції переписування структури в лінійний двонаправлений список здійснюється передача списку в який буде записано відповідні елементи масиву структур і сам масив структур. У функції сортування списку передається тільки список який потрібно посортувати. У функції запису в файл передається список і файл в який він буде записаний. У функці виведення з файлу передається тільки сам файл з якого будуть виведені дані у формі таблиці.
Вхідні та вихідні дані:
Вхідними даними є стрічка яку можна вводити як латиницею так і кирилицею з допомогою використання програми UKRDOS.
Вихідні дані виводяться з файлу на екран назва, код, дата по специфікації стрічки, кількість по специфікації цілого числа а ціна по специфікації дійсного. Визначена ширина поля виведення для представлення даних у формі таблиці.
Програмні засоби:
Програма буде працювати під керуванням всіх операційних систем Windows включаючи Windows Vista і операційною системою DOS.
Технічні засоби:
Програма буде працювати при мінімальних характеристиках комп’ютера. З Основними технічними засобами комп’ютера: монітор, клавіатура, постійно запам’ятовуючий пристрій, дисковий накопичувач.
Аналіз результатів комп’ютерної реалізації програми.
При тестуванні програми спочатку було введено три елементи масиву рядків. Вони були успішно переписані в масив структур, який у свою чергу був переписаний у лінійний двонаправлений список. Посортований список успішно був виведений на екран у прямому та зворотному порядку. Було виведене повідомлення-підказка про те що потрібно ввести код товару, щоб знайти його вартість. Після здійснення введення коду одного з товарів і з точністю до сотих було виведено вартість продукції. Після очищення екрану на екран було виведено усі дані без помилок у формі таблиці. При завершені програми помилок не виникло.
При повторному тестуванні було введено з клавіатури елементів масиву рядків. Всі підказки і повідомлення також було виведено. Списки теж було виведено на екран у прямому та зворотному порядку при запиті на введення код продукції було введено з клавіатури неіснуючий код і як задумано було виведено повідомлення про помилку про неіснуючий код. Після очищення екрану всі дані успішно були зчитані з файлу і виведені на екран у формі таблиці. При завершенні програми виникла серія помилок. Проблема була вирішена викликом після затримки екрану функції виходу з параметром 0 (завершення без помилки).
Коли програма тестувалась втретє було введено 8 елементів масиву рядків. Всі етапи програми пройшли добре. Помилок при завершенні програми не виникло.
В програмі передбачений кольоровий графічний інтерфейс. Всі кольори було виведено без помилок. Час розв’язування задачі – менше одної секунди. Об’єм оперативної пам'яті – достатній для збереження статичних і динамічних даних підчас роботи програми. Об’єм зовнішньої пам'яті – достатній для збереження даних у формі двійкового файлу.