Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Лабораторная работа 9. Программирование обработки структур



Поиск в массиве структур

В текстовом файле хранится база отдела кадров предприятия. На предприятии 100сотрудников. Каждая отрока файла содержит запись ободна.ч сотруднике. Фор­мат записи: фамилия и иниималы 00 поз., фамилия должна начинаться с первой позиции), год рождения (5 поз.), оклад (10 поз.). Написать программу, которая по заданной фамилии выводит на экран сведения о сотруднике, подсчитывая средний оклад всех запрошенных сотрудников.

I. Исходные данные, результаты и промежуточные величины

Исходные данные. База сотрудников находится в текстовом файле- Прежде всего надо решить, хранить ли в оперативной памяти одновременно всю информацию из файла или можно обойтись буфером на одну строку. Если бы сведения о со­труднике запрашивались однократно, можно было бы остановиться на втором ва­рианте, но поскольку поиск по базе будет выполняться более одного раза, всю информацию желательно хранить в оперативной памяти, поскольку многократное чтение из файла крайне нерационально.

Максимальное количество строк файла по условию задачи ограничено, поэтому можно выделить для их хранения массив из 100 элементов. Каждый элемент мас­сива будет содержать сведения об одном сотруднике. Поскольку эти сведения раз­нородные, удобно организовать их в виде структуры.

В программу по условию требуется также вводить фамилии искомых сотрудни­ков. Для хранения фамилии опишем строку символов той же длимы, что и в базе.

Результаты. В результате работы программы требуется вывести на экран требу­емые элементы исходного массива. Поскольку эти результаты представляют со­бой выборку из исходных данных, дополнительная память для них не отводится. Кроме того, необходимо подсчитать средний оклад для найденных сотрудников. Для этого необходима переменная вещественного типа.

Промежуточные величины. Для поиска среднего оклада необходимо подсчитать количество сотрудников, для которых выводились сведения. Заведем для этого переменную целого типа. Для описания формата входного файла будем использо­вать именованные константы.

II. Алгоритм решения задачи очевиден:

1. Ввести из файла в массив сведения осотрудииках.

2. Организовать цикл вывода сведений о сотруднике:

· ввести с клавиатуры фамилию;

· выполнить поиск сотрудника в массиве;

· увеличить суммарный оклад и счетчик количества сотрудников;

· вывести сведения о сотруднике или сообщение об отсутствии;

3. Вывести средний оклад.

Необходимо решить, каким образом будет пронзводиться выход из цикла вывода сведений о сотрудниках. Для простоты условимся, что для выхода из цикла вмес­то фамилии следует ввести слово end.

III. Программа и тестовые примеры

#include < fstream>

#include < string.h>

#include < iomanip>

#include < stdlib.h>

#include < windows.h> //0

using std:: cin;

using std:: cout;

using std:: endl;

using std:: setw;

int main(){

const int l_name = 30, l_year = 5, l_pay = 10,

l_buf = l_name +l_year + l_pay; // 1

struct Man {

int birth_year; char name[l_name + 1]; float pay;

};

const int l_dbase = 100;

Man dbase[l_dbase]; // 3

char buf[l_buf ♦ 1]; //4

char name[l_name + 1]; // 5

ifstream fin(" dbase.txt", ios:: in | ios:: nocreate); // 6

if (! fin) { cout < < " Ошибка открытия файла "; return 1; }

int i = 0;

while (fin.getline(buf, l_buf)) { // 7

if (i > = l_dbase) { cout < < " Слишком длинный файл ": return 1; }

strncpy(dbase[i).name, buf, l_name):

dbase[i].name[l_name] ='\0';

dbase[i].birth_year = atoi(& buf[l_name]);

dbase[i].pay = atof(& buf[l_name + l_year]);

i++;

}

int n_record =i, n_man = 0; //8

float mean_pay = 0;

while (true) { // 9

cout < < " Введите фамилию или end: " ; cin> > name;

// 0emTcChar(name. name): //10

1f (strcmp(name,. " end" ) == 0) break; //11

bool not_found = true; //12

for (i = 0; i < n_record; i++); { //13

if (strstr(dbase[i].name, name)) //14

if (dbase[i].name[strlen(name)]==' '){ //15

strcpy(name, dbase[i].name);

// CharToOer(name. name): //16

cout < < name < < dbase[i].birth_year < < ' '< < dbase[i].pay< < endl;

not_found = false;

}

}

cin » nare:

tf (not _ound) cout < < " Такого сотрудника нет" < < endl;

}

If (n_man > 0) cout < < " Средний оклад: " < < mean_рау / n_man< <

endl; //17

return 0;

}

 

Рассмотрим приведенную выше программу подробно. В операторе 1 заданы именованные константы, в которых хранится формат входного файла, то есть длина каждого из полей записи (строки файла). Такой подход позволяет при необходимости легко вносить в программу изменения. Длина буфера, в который будет счи-тываться каждая строка файла, вычисляется как сумма длин указанных полей.

В операторе 2 определяется структура Man для хранения сведений об одном сотруднике. Длина поля, в котором будет находиться фамилия, задана с учетом завершающего нуль-символа. В операторе 3 определяется массив структур dbase для хранения всей базы. Его размерность также задается именованной константой. В операторах 4 и 5 задаются промежуточные переменные: буфер buf для ввода строки из файла и строка name для фамилии запрашиваемого сотрудника.

В операторе 6 выполняется открытие файла dbase. txt для чтения. Предполагается. что этот файл находится в том же каталоге, что и текст программы, иначе следует указать полный путь. Входной файл следует создать в любом текстовом редакторе до первого запуска программы в соответствии с форматом, заданным в условии задачи. Файл для целей тестирования должен состоять из нескольких строк, причем необходимо предусмотреть случай, когда одна фамилия является частью другой (например. Иванов и Ивановский). Не забудьте проверить, выдается ли диагностическое сообщение, если файл не найден.

Цикл 7 выполняет построчное считывание из файла в строку buf и заполнение очередного элемента массива dbase. Счетчик 1 хранит индекс первого свободного элемента массива. Для формирования полей структуры используются функции копирования строк strncpy. преобразования из строки в целое число atoi и преобразования из строки в вещественное число atof. Обратите внимание на то, что завершающий нуль-символ в поле фамилии заносится «вручную», поскольку функция: strncpy делает это только в случае, если строка-источник короче строки-приемника. В функцию atoi передается адрес начала подстроки, в которой находится год рождения. Обратите внимание на то. что при каждом проходе цикла выполняется проверка, не превышает ли считанное количество строк размерность массива. При тестировании программы в этот цикл следует добавить контрольный вывод на экран счи­танной строки, а также сформированных полей структуры. Для проверки выдачи диагностического сообщения следует временно задать константу l_dbase равной, а затем меньшей фактического количества строк в файле.

 

В операторе 8 определяются две переменные: n_record для хранения фактического количества записей о сотрудниках и n_man - для подсчета сотрудников, о которых будут выдаваться сведения. Следует также не забыть обнулить переменную mean_рау. в которой в следующем цикле будет накапливаться сумма окладов.

Цикл поиска сотрудников по фамилии организован как бесконечный (оператор 9) с принудительным выходом (оператор 11).

В операторе 12 определяется переменная-флаг not_found для того, чтобы после окончания цикла поиска было известно, завершился ли он успешно. Обратите внимание на имя переменной: его следует выбирать таким образом, чтобы по нему было ясно, какое значение является истинным. Как видите, в этом случае оператор

if (not_found) cout < < ' Такого согоуднкка нет " < < endl: хорошо понятен без дополнительных комментариев.

В операторе 13 организуется цикл просмотра массива структур (просматриваются только заполненные при вводе элементы). Проверка совпадения фамилии сотруд­ника производится в два этапа. В операторе 14 с помощью функции Strstr поиска подстроки определяется, содержится ли в поле базы паве искомая последовательность букв, а в операторе 15 проверяется, есть ли непосредственно после фамилии пробел (если пробела нет, то искомая фамилия является частью другой, и эта строка нам не подходит). Такая простая проверка возможна из-за условия задачи, по которому фамилия должна начинаться с первой позиции каждой строки.

Когда вы дойдете до отладки этой части программы, то, вполне возможно, столкнетесь с непонятной на первый взгляд проблемой: программа не может найти запись с заданной фамилией. Впрочем, этот эффект проявляется, только если вы работаете в среде Windows и только на фамилиях, записанных н базе на русском языке; стоит только перейти на латиницу, как все начинает работать нормально.

Продолжим анализ программы. Мы остановились на том. что алгоритм составлен в предположении, что фамилия начинается с первой позиции записи в базе данных. Измените программу так. чтобы это ограничение можно было снять. Для это­го придется проверить, стоит ли перед фамилией пробел в том случае, если она не начинается с первой позиции. Внесите в тестовый пример изменения, необходи­мые для тестирования этой части программы.

Проверка переменной n_manв операторе 17 необходима для того, чтобы в случае. если пользователь не введет ни одной фамилии, совпадающей с фамилией в базе, не выполнялось деление на 0.

Крупным недостатком нашей программы является то, что вводить фамилию со­трудника требуется именно в том регистре, в котором она присутствует в базе. Для преодоления этого недостатка необходимо перед сравнением фамилий переводить все символы в один регистр. Для символов латинского алфавита в библиотеке есть функции tolower(c) и toupper(c), переводящие переданный им символ с в нижний и верхний регистр соответственно, аналогичные функции для символов русского алфавита придется написать самостоятельно.

Если в базе есть несколько сотрудников с одной и той же фамилией, программа выдаст сведения обо всех.

Задача 6.2. Сортировка массива структур

Написать программу, которая упорядочивает описанный в предыдущей задаче файл по году рождения сотрудников.

Изменим предыдущую программу таким образом, чтобы она вместо поиска упо­рядочивала массив, а затем записывала его в файл г тем же именем, что исходный.

Для сортировки применим метод выбора. При всей своей простоте он достаточно эффективен. Основная идея этого метода: из массива выбирается наименьший элемент и меняется местами с первым элементом, затем рассматриваются элементы, начиная со второго, наименьший из них меняется местами со вторым элементом, и так далее. Для упорядочивайия требуется количество про­смотров, на единицу меньшее, чем количество элементов в массиве (при последнем проходе цикла при необходимости меняются местами предпоследний и по­следний элементы массива).

#include < fstream>

#include < string.h>

#include < iomanip>

#include < stdlib.h>

#include < windows.h> //0

using std:: cin;

using std:: cout;

using std:: endl;

using std:: setw;

int main(){

const int l_name = 30, l_year = 5, l_pay = 10,

l_buf = l_name +l_year + l_pay;

struct Man {

int birth_year; char name[l_name + 1]; float pay;

};

const int l_dbase = 100;

Man dbase[l_dbase];

char buf[l_buf ♦ 1];

char name[l_name + 1];

ifstream fin(" dbase.txt", ios:: in | ios:: nocreate);

if (! fin) { cout < < " Ошибка открытия файла "; return 1; }

int i = 0;

while (fin.getline(buf, l_buf)) {

strncpy(dbase[i).name, buf, l_name);

dbase[i].name[l_name] ='\0';

dbase[i].birth_year = atoi(& buf[l_name]);

dbase[i].pay = atoi(& buf[l_name + l_year]);

i++;

if (i > l_dbase) { cout < < " Слишком длинный файл ": return 1; }

 

}

int n_record =i;

fin.close();

ofstream fout(" dbase.txt" );

if (! fout){cout < < " ошибка открытия файла " < < endl; return 1; }

float mean_pay = 0;

for(i=0; i< n_record-1; i++){

//принимаем за наименьший первый из рассматриваемых элементов

int imin=1;

//поиск номера минимального элемента из неупорядоченных

for(int j=i+1; j< n_record; j++)

if (dbase[j].birth_year < dbase[imin].birth_year) imin=j;

//обмен двух элементов массива структур

Man a=dbase[i];

dbase[i]=dbase[imin];

dbase[imin]=a;

}

for (i=0; i< n_record; i++){

fout< < dbase[i].name< < dbase[i].bitth_year< < " " < < dbase[i].pay< <

endl;

fout.close();

cout< < " сортировка базы завершена" < < endl;

return 0;

}

 

Элементами массива в данной задаче являются структуры. Для структур одного типа определена операция присваивания, поэтому обмен двух элементов массива структур выглядит точно так же. как для основных типов данных.

Для того чтобы записать результаты в файл с тем же именем, файл, открытый для чтения, закрывается» а затем открывается файл с тем же именем для записи (говоря более строго, создается объект выходного потока ostream с именем fout). При этом старый файл на диске уничтожается и создается новый, пустой файл, в кото­рый и производится запись массива.

Будьте аккуратны при отладке программ, изменяющих входные файлы: следует либо перед запуском программы создать копию исходного файла, либо открывать выходной файл с другим именем, а замснять его на имя, требуемое по заданию, только после того, как удалось убедиться в полной работоснособностн программы.


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-08-31; Просмотров: 920; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.041 с.)
Главная | Случайная страница | Обратная связь