Кашина_СП_ЛБ9
.docxМинистерство науки и высшего образования Российской Федерации Федеральное государственное бюджетное образовательное учреждение высшего образования
«Владимирский государственный университет
имени Александра Григорьевича и Николая Григорьевича Столетовых» (ВлГУ)
Колледж инновационных технологий и предпринимательства
КАФЕДРА ФИЗИКИ И ПРИКЛАДНОЙ МАТЕМАТИКИ
Лабораторная работа № 9
по дисциплине «Системное программирование»
на тему: «Обработка исключений»
Выполнил
студент группы ИПсп-123
Кашина Д. А.
Принял
Красильщикова В. В.
Владимир, 2026
Цель работы: освоение возможностей языка C++ по обработке исключительных ситуаций.
Задание:
4. Описать в заголовочном файле person.h тип структуры Person для хранения данных о человеке (фамилия, имя, возраст, пол). Этот файл подключать в обеих программах лабораторной работы.
Написать программу для ввода списка людей и сохранения его в файл.
a. В главной функции создать одномерный массив People структур Person. Размер массива вводит пользователь. Заполнить массив в диалоговом режиме.
b. Описать функцию save(), получающую в качестве параметров массив людей и имя файла. Функция должна сохранять список людей в указанный файл. Формат файла выбрать самостоятельно. Функция ничего не возвращает.
c. Вызвать функцию save() в главной функции, передав туда заполненный массив людей и имя файла, вводимое пользователем.
Написать программу для чтения списка людей из файла и сортировки этого списка.
a. Описать функцию load(), получающую в качестве параметра имя файла и возвращающую массив структур Person.
b. Описать функцию sort(), получающую в качестве параметра массив структур Person и сортирующую его по сочетанию двух свойств: фамилия+имя. Сортировка должна менять не локальную копию массива, а массив в области видимости функции, вызвавшей функцию sort(). Функция sort() ничего не возвращает.
c. В главной функции с использованием функций load() и sort() организовать чтение массива людей из файла и сортировку. Вывести результат.
В обеих программах предусмотреть генерацию (оператор throw) и обработку (оператор try) исключительных ситуаций, связанных с событиями различных типов:
a. ошибки работы с файлами (использовать try);
b. выход за диапазон допустимых значений (использовать throw и try).
Ход работы:
1 задание
Заголовочный файл
#ifndef PERSON_H
#define PERSON_H
#include <string>
using namespace std;
struct Person {
string lastName;
string firstName;
int age;
char gender; // 'M' - мужской, 'F' - женский
};
#endif // PERSON_H
Код программы:
#include <iostream>
#include <fstream>
#include <string>
#include <clocale>
#include <cctype>
#include <windows.h>
#include <limits>
#include "person.h"
using namespace std;
// Функция для проверки, состоит ли строка только из букв (включая русские)
bool isOnlyLetters(const string& str) {
if (str.empty()) return false;
// Проверяем, что в строке нет пробелов и других разделителей
for (char c : str) {
if (isspace(static_cast<unsigned char>(c))) {
return false; // Запрещаем пробелы внутри строки
}
}
for (char c : str) {
unsigned char uc = static_cast<unsigned char>(c);
// Русские буквы в CP1251: А-Я (192-223), а-я (224-255), Ё (168), ё (184)
bool isRussian = (uc >= 192 && uc <= 255) || uc == 168 || uc == 184;
// Английские буквы
bool isEnglish = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
// Дефис (только не в начале и не в конце, но это проверим позже)
bool isHyphen = (c == '-');
if (!isRussian && !isEnglish && !isHyphen) {
return false;
}
}
// Проверка, что дефис не в начале и не в конце
if (str[0] == '-' || str[str.length() - 1] == '-') {
return false;
}
// Проверка, что нет двух дефисов подряд
for (size_t i = 0; i < str.length() - 1; ++i) {
if (str[i] == '-' && str[i + 1] == '-') {
return false;
}
}
return true;
}
// Новая функция для проверки имени (разрешены пробелы и дефисы)
bool isValidName(const string& str) {
if (str.empty()) return false;
for (char c : str) {
unsigned char uc = static_cast<unsigned char>(c);
// Русские буквы в CP1251
bool isRussian = (uc >= 192 && uc <= 255) || uc == 168 || uc == 184;
// Английские буквы
bool isEnglish = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
// Пробел (разрешен в имени)
bool isSpace = (c == ' ');
// Дефис
bool isHyphen = (c == '-');
if (!isRussian && !isEnglish && !isSpace && !isHyphen) {
return false;
}
}
// Проверка, что дефис не в начале и не в конце (если есть дефис)
if (str[0] == '-' || str[str.length() - 1] == '-') {
return false;
}
// Проверка, что пробел не в начале и не в конце
if (str[0] == ' ' || str[str.length() - 1] == ' ') {
return false;
}
// Проверка, что нет двух дефисов подряд
for (size_t i = 0; i < str.length() - 1; ++i) {
if (str[i] == '-' && str[i + 1] == '-') {
return false;
}
}
// Проверка, что нет двух пробелов подряд
for (size_t i = 0; i < str.length() - 1; ++i) {
if (str[i] == ' ' && str[i + 1] == ' ') {
return false;
}
}
return true;
}
// Функция для проверки, состоит ли строка только из цифр
bool isOnlyDigits(const string& str) {
if (str.empty()) return false;
// Проверяем, что в строке нет пробелов
for (char c : str) {
if (isspace(static_cast<unsigned char>(c))) {
return false;
}
}
for (char c : str) {
if (!isdigit(static_cast<unsigned char>(c))) {
return false;
}
}
return true;
}
// Очистка буфера ввода
void clearInputBuffer() {
cin.clear();
cin.ignore(10000, '\n');
}
// Функция для безопасного ввода целого числа (одно число, без лишних символов)
int getValidInt(const string& prompt, int minVal, int maxVal) {
string input;
int value;
while (true) {
cout << prompt;
// Считываем всю строку целиком
getline(cin, input);
// Удаляем пробелы в начале и конце
size_t start = input.find_first_not_of(" \t");
if (start == string::npos) {
cout << "Ошибка: Введите число!" << endl;
continue;
}
size_t end = input.find_last_not_of(" \t");
input = input.substr(start, end - start + 1);
// Проверяем, нет ли пробелов внутри строки
if (input.find(' ') != string::npos || input.find('\t') != string::npos) {
cout << "Ошибка: Введите только одно число, без пробелов!" << endl;
continue;
}
// Проверяем, состоит ли строка только из цифр
if (!isOnlyDigits(input)) {
cout << "Ошибка: Введите только цифры!" << endl;
continue;
}
// Преобразуем строку в число (может быть очень большим)
try {
value = stoi(input);
}
catch (const out_of_range&) {
cout << "Ошибка: Число слишком большое!" << endl;
continue;
}
// Проверяем диапазон
if (value < minVal || value > maxVal) {
cout << "Ошибка: Значение должно быть в диапазоне от "
<< minVal << " до " << maxVal << "!" << endl;
continue;
}
break;
}
return value;
}
// Функция для безопасного ввода фамилии (только буквы, дефис, без пробелов)
string getValidSurname(const string& prompt) {
string input;
while (true) {
cout << prompt;
// Считываем всю строку целиком
getline(cin, input);
// Удаляем пробелы в начале и конце
size_t start = input.find_first_not_of(" \t");
if (start == string::npos) {
cout << "Ошибка: Введите фамилию!" << endl;
continue;
}
size_t end = input.find_last_not_of(" \t");
input = input.substr(start, end - start + 1);
// Проверяем, нет ли пробелов внутри строки (фамилия не может содержать пробелы)
if (input.find(' ') != string::npos || input.find('\t') != string::npos) {
cout << "Ошибка: Фамилия не должна содержать пробелов!" << endl;
cout << " Введите одно слово (например: Иванов или Петров-Водкин)" << endl;
continue;
}
// Проверяем, состоит ли строка только из букв и дефиса
if (!isOnlyLetters(input)) {
cout << "Ошибка: Фамилия должна содержать только буквы и дефис!" << endl;
cout << " Разрешены: русские буквы, английские буквы, дефис" << endl;
continue;
}
break;
}
return input;
}
// Функция для безопасного ввода имени (разрешены пробелы и дефисы)
string getValidName(const string& prompt) {
string input;
while (true) {
cout << prompt;
// Считываем всю строку целиком
getline(cin, input);
// Удаляем пробелы в начале и конце
size_t start = input.find_first_not_of(" \t");
if (start == string::npos) {
cout << "Ошибка: Введите имя!" << endl;
continue;
}
size_t end = input.find_last_not_of(" \t");
input = input.substr(start, end - start + 1);
// Проверяем, состоит ли строка только из букв, пробелов и дефисов
if (!isValidName(input)) {
cout << "Ошибка: Имя должно содержать только буквы, пробелы и дефис!" << endl;
cout << " Разрешены: русские буквы, английские буквы, пробел, дефис" << endl;
cout << " Примеры: Анна, Анна-Мария, Анна Павловна" << endl;
continue;
}
break;
}
return input;
}
// Функция для безопасного ввода пола
char getValidGender() {
string input;
char gender;
while (true) {
cout << "Введите пол (М/Ж или M/F): ";
// Считываем всю строку целиком
getline(cin, input);
// Удаляем пробелы
size_t start = input.find_first_not_of(" \t");
if (start == string::npos) {
cout << "Ошибка: Введите пол!" << endl;
continue;
}
size_t end = input.find_last_not_of(" \t");
input = input.substr(start, end - start + 1);
// Проверяем, что введен один символ
if (input.length() != 1) {
cout << "Ошибка: Введите один символ!" << endl;
continue;
}
gender = toupper(static_cast<unsigned char>(input[0]));
// Проверяем, что символ является допустимым
if (gender == 'М' || gender == 'M' || gender == 'Ж' || gender == 'F') {
if (gender == 'M') gender = 'М';
if (gender == 'F') gender = 'Ж';
break;
}
else {
cout << "Ошибка: Пол должен быть 'М' или 'Ж' (или M/F)!" << endl;
}
}
return gender;
}
// Функция для получения имени файла с автоматическим добавлением .txt
string getValidFilename() {
string filename;
while (true) {
cout << "Введите имя файла для сохранения (без расширения): ";
getline(cin, filename);
// Удаляем пробелы в начале и конце
size_t start = filename.find_first_not_of(" \t");
if (start == string::npos) {
cout << "Ошибка: Введите имя файла!" << endl;
continue;
}
size_t end = filename.find_last_not_of(" \t");
filename = filename.substr(start, end - start + 1);
// Проверяем, что имя файла не пустое
if (filename.empty()) {
cout << "Ошибка: Имя файла не может быть пустым!" << endl;
continue;
}
// Проверяем, что имя файла не содержит недопустимые символы
bool valid = true;
for (char c : filename) {
// Разрешаем буквы, цифры, дефис, подчеркивание, точку (но точку потом заменим)
if (!isalnum(static_cast<unsigned char>(c)) && c != '-' && c != '_' && c != '.') {
valid = false;
break;
}
}
if (!valid) {
cout << "Ошибка: Имя файла может содержать только буквы, цифры, дефис и подчеркивание!" << endl;
continue;
}
break;
}
// Автоматически добавляем расширение .txt
// Если пользователь ввел .txt, то не добавляем повторно
if (filename.length() >= 4 && filename.substr(filename.length() - 4) == ".txt") {
return filename;
}
return filename + ".txt";
}
// Функция для сохранения массива в файл
void save(const Person* people, int size, const string& filename) {
ofstream outFile(filename);
if (!outFile.is_open()) {
throw runtime_error("Невозможно открыть файл для записи: " + filename);
}
for (int i = 0; i < size; ++i) {
outFile << people[i].lastName << "|"
<< people[i].firstName << "|"
<< people[i].age << "|"
<< people[i].gender << endl;
}
outFile.close();
}
int main() {
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian");
cout << "Выполнила Кашина Д. ИПсп-123 \n";
try {
// Очищаем буфер перед началом работы
clearInputBuffer();
// Ввод количества людей с валидацией
int size = getValidInt("Введите количество людей: ", 1, 1000000);
// Создаем динамический массив
Person* people = new Person[size];
// Заполнение массива
for (int i = 0; i < size; ++i) {
cout << "\n--- Ввод данных для человека " << (i + 1) << " ---" << endl;
people[i].lastName = getValidSurname("Введите фамилию: ");
people[i].firstName = getValidName("Введите имя: ");
people[i].age = getValidInt("Введите возраст (0-150): ", 0, 150);
people[i].gender = getValidGender();
}
// Получаем имя файла с автоматическим добавлением .txt
string filename = getValidFilename();
save(people, size, filename);
cout << "Данные успешно сохранены в файл " << filename << endl;
delete[] people;
}
catch (const out_of_range& e) {
cerr << "Ошибка: " << e.what() << endl;
return 1;
}
catch (const runtime_error& e) {
cerr << "Ошибка: " << e.what() << endl;
return 1;
}
catch (const exception& e) {
cerr << "Ошибка: " << e.what() << endl;
return 1;
}
return 0;
}
Результат работы:
Запись в файле:
Блок-схема
Блок-схема обработки исключений
2 задание:
Код программы:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <clocale>
#include <sstream>
#include <windows.h>
#include "person.h"
using namespace std;
// Функция загрузки массива из файла
Person* load(const string& filename, int& size) {
ifstream inFile(filename);
if (!inFile.is_open()) {
throw runtime_error("Невозможно открыть файл для чтения: " + filename);
}
// Сначала подсчитываем количество строк в файле
size = 0;
string line;
while (getline(inFile, line)) {
if (!line.empty()) {
size++;
}
}
if (size == 0) {
throw runtime_error("Файл пуст");
}
// Возвращаемся в начало файла
inFile.clear();
inFile.seekg(0);
// Выделяем память
Person* people = new Person[size];
// Читаем данные
int index = 0;
while (getline(inFile, line) && index < size) {
if (line.empty()) continue;
stringstream ss(line);
string temp;
// Читаем фамилию
getline(ss, people[index].lastName, '|');
// Читаем имя
getline(ss, people[index].firstName, '|');
// Читаем возраст
getline(ss, temp, '|');
people[index].age = stoi(temp);
// Читаем пол
getline(ss, temp, '|');
people[index].gender = temp[0];
// Проверка допустимых значений
if (people[index].age < 0 || people[index].age > 100) {
delete[] people;
throw out_of_range("Некорректный возраст в файле");
}
if (people[index].gender != 'М' && people[index].gender != 'Ж') {
delete[] people;
throw out_of_range("Некорректный пол в файле");
}
index++;
}
inFile.close();
return people;
}
// Функция сортировки массива по фамилии и имени
void sort(Person* people, int size) {
for (int i = 0; i < size - 1; ++i) {
for (int j = 0; j < size - i - 1; ++j) {
// Сравниваем по фамилии
if (people[j].lastName > people[j + 1].lastName) {
swap(people[j], people[j + 1]);
}
// Если фамилии одинаковые, сравниваем по имени
else if (people[j].lastName == people[j + 1].lastName &&
people[j].firstName > people[j + 1].firstName) {
swap(people[j], people[j + 1]);
}
}
}
}
// Функция для вывода массива
void printPeople(const Person* people, int size) {
cout << "\n--- Список людей ---" << endl;
for (int i = 0; i < size; ++i) {
cout << (i + 1) << ". "
<< people[i].lastName << " "
<< people[i].firstName << ", "
<< "Возраст: " << people[i].age << ", "
<< "Пол: " << people[i].gender << endl;
}
}
int main() {
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
cout << "Выполнила Кашина Д. ИПсп-123 \n";
try {
string filename;
cout << "Введите имя файла для чтения: ";
cin >> filename;
int size;
Person* people = load(filename, size);
cout << "\nИсходный список:" << endl;
printPeople(people, size);
// Сортируем массив
sort(people, size);
cout << "\nОтсортированный список (по фамилии и имени):" << endl;
printPeople(people, size);
delete[] people;
}
catch (const out_of_range& e) {
cerr << "Ошибка: " << e.what() << endl;
return 1;
}
catch (const runtime_error& e) {
cerr << "Ошибка: " << e.what() << endl;
return 1;
}
catch (const exception& e) {
cerr << "Ошибка: " << e.what() << endl;
return 1;
}
return 0;
}
Результат работы:
Блок-схема
Вывод: в ходе лабораторной работы были освоены возможности языка C++ по обработке исключительных ситуаций.
