При редактировании записи о продукте с помощью функции editProduct возникает проблема последующего вывода списка в консоль. Приведу пример: я хочу изменить запись под индексом 1, ввожу новые данные, затем хочу посмотреть изменилась ли запись. Вывожу в консоль список с помощью функции viewProducts, вижу следующую картину: все записи о товарах, что были после индекса 1 не выводятся в консоль, хотя в бинарном файле они есть. В чём может быть проблема?
Сравнение размерности полей должно быть по заданию: при изменении размерности записи она переписывается в конец файла.
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
// Класс Product
class Product {
public:
Product();
Product(const string& _name, const string& _category, int _quantity,
const string& _arrivalDate, double _price, double _markupPercentage);
Product(const Product& obj); // Конструктор копирования
~Product();
// Геттеры
string getName() const;
string getCategory() const;
int getQuantity() const;
string getArrivalDate() const;
double getPrice() const;
double getMarkupPercentage() const;
// Сеттеры
void setName(const string& _name);
void setCategory(const string& _category);
void setQuantity(int _quantity);
void setArrivalDate(const string& _arrivalDate);
void setPrice(double _price);
void setMarkupPercentage(double _markupPercentage);
// Сериализация
void serialize(std::ostream& os) const;
void deserialize(std::istream& is);
long next;
long prev;
private:
string name;
string category;
int quantity;
string arrivalDate;
double price;
double markupPercentage;
};
#define MAXnameLength 100
#define MAXcategoryLength 100
Product::Product()
: name(""), category(""), quantity(0), arrivalDate(""),
price(0.0), markupPercentage(0.0), next(-1), prev(-1) {}
Product::Product(const string& _name, const string& _category, int _quantity,
const string& _arrivalDate, double _price, double _markupPercentage)
: name(_name), category(_category), quantity(_quantity),
arrivalDate(_arrivalDate), price(_price), markupPercentage(_markupPercentage),
next(-1), prev(-1) {}
Product::Product(const Product& obj)
: name(obj.name), category(obj.category), quantity(obj.quantity),
arrivalDate(obj.arrivalDate), price(obj.price),
markupPercentage(obj.markupPercentage), next(obj.next), prev(obj.prev) {}
Product::~Product() {}
string Product::getName() const { return name; }
string Product::getCategory() const { return category; }
int Product::getQuantity() const { return quantity; }
string Product::getArrivalDate() const { return arrivalDate; }
double Product::getPrice() const { return price; }
double Product::getMarkupPercentage() const { return markupPercentage; }
void Product::setName(const string& _name) { name = _name; }
void Product::setCategory(const string& _category) { category = _category; }
void Product::setQuantity(int _quantity) { quantity = _quantity; }
void Product::setArrivalDate(const string& _arrivalDate) { arrivalDate = _arrivalDate; }
void Product::setPrice(double _price) { price = _price; }
void Product::setMarkupPercentage(double _markupPercentage) { markupPercentage = _markupPercentage; }
void Product::serialize(std::ostream& os) const {
size_t nameLength = name.size();
os.write(reinterpret_cast<const char*>(&nameLength), sizeof(nameLength));
os.write(name.c_str(), nameLength);
size_t categoryLength = category.size();
os.write(reinterpret_cast<const char*>(&categoryLength), sizeof(categoryLength));
os.write(category.c_str(), categoryLength);
os.write(reinterpret_cast<const char*>(&quantity), sizeof(quantity));
size_t arrivalDateLength = arrivalDate.size();
os.write(reinterpret_cast<const char*>(&arrivalDateLength), sizeof(arrivalDateLength));
os.write(arrivalDate.c_str(), arrivalDateLength);
os.write(reinterpret_cast<const char*>(&price), sizeof(price));
os.write(reinterpret_cast<const char*>(&markupPercentage), sizeof(markupPercentage));
os.write(reinterpret_cast<const char*>(&prev), sizeof(prev));
os.write(reinterpret_cast<const char*>(&next), sizeof(next));
}
void Product::deserialize(std::istream& is) {
size_t nameLength;
is.read(reinterpret_cast<char*>(&nameLength), sizeof(nameLength));
if (nameLength > MAXnameLength) {
throw std::runtime_error("Длина имени превышает допустимое значение.");
}
name.resize(nameLength);
is.read(&name[0], nameLength);
size_t categoryLength;
is.read(reinterpret_cast<char*>(&categoryLength), sizeof(categoryLength));
if (categoryLength > MAXcategoryLength) {
throw std::runtime_error("Длина категории превышает допустимое значение.");
}
category.resize(categoryLength);
is.read(&category[0], categoryLength);
is.read(reinterpret_cast<char*>(&quantity), sizeof(quantity));
size_t arrivalDateLength;
is.read(reinterpret_cast<char*>(&arrivalDateLength), sizeof(arrivalDateLength));
arrivalDate.resize(arrivalDateLength);
is.read(&arrivalDate[0], arrivalDateLength);
is.read(reinterpret_cast<char*>(&price), sizeof(price));
is.read(reinterpret_cast<char*>(&markupPercentage), sizeof(markupPercentage));
is.read(reinterpret_cast<char*>(&prev), sizeof(prev));
is.read(reinterpret_cast<char*>(&next), sizeof(next));
}
// Класс BinaryFile
class BinaryFile {
public:
BinaryFile(const char* filename);
~BinaryFile();
void addProduct(const Product& product);
void deleteProduct(size_t index);
void insertProduct(size_t index, const Product& product);
void editProduct(size_t index, const Product& product);
void sortProducts();
void viewProducts(size_t pageSize, size_t pageNumber);
void createInvoice();
void compressFile();
Product getProductByIndex(size_t index);
private:
std::fstream file;
const char* filename;
long head; // Указатель на голову списка
long tail; // Указатель на хвост списка
size_t getProductCount();
Product readProduct(long offset);
void writeProduct(long offset, const Product& product);
};
BinaryFile::BinaryFile(const char* filename) : filename(filename), head(-1), tail(-1) {
file.open(filename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
if (!file.is_open()) {
file.open(filename, std::ios::binary | std::ios::out | std::ios::trunc);
file.close();
file.open(filename, std::ios::binary | std::ios::in | std::ios::out);
}
}
BinaryFile::~BinaryFile() {
file.close();
}
size_t BinaryFile::getProductCount() {
file.clear();
file.seekg(0, std::ios::beg);
size_t count = 0;
while (file.peek() != EOF) {
Product product;
product.deserialize(file);
count++;
}
return count;
}
Product BinaryFile::readProduct(long offset) {
file.clear();
file.seekg(offset, std::ios::beg);
Product product;
product.deserialize(file);
return product;
}
void BinaryFile::writeProduct(long offset, const Product& product) {
file.clear();
file.seekp(offset, std::ios::beg);
product.serialize(file);
}
void BinaryFile::addProduct(const Product& product) {
long newOffset = file.tellp();
Product newProduct = product;
// Установка указателей
if (tail != -1) {
newProduct.prev = tail;
Product tailProduct = readProduct(tail);
tailProduct.next = newOffset;
writeProduct(tail, tailProduct);
}
else {
newProduct.prev = -1; // Это первый элемент
head = newOffset;
}
newProduct.next = -1; // Указывает на конец списка
writeProduct(newOffset, newProduct);
tail = newOffset; // Обновляем хвост
}
void BinaryFile::deleteProduct(size_t index) {
long currentOffset = head;
long productOffset = -1;
long prevOffset = -1;
// Находим продукт по индексу
for (size_t i = 0; i < index && currentOffset != -1; ++i) {
prevOffset = currentOffset; // Сохраняем предыдущий продукт
Product currentProduct = readProduct(currentOffset);
currentOffset = currentProduct.next;
}
if (currentOffset == -1) {
throw std::out_of_range("Index out of range");
}
// Удаляем продукт
Product productToDelete = readProduct(currentOffset);
// Обновляем указатели prev и next для соседних продуктов
if (prevOffset != -1) {
Product prevProduct = readProduct(prevOffset);
prevProduct.next = productToDelete.next; // Пропускаем удаляемый продукт
writeProduct(prevOffset, prevProduct); // Записываем обновленный продукт
}
else {
head = productToDelete.next; // Если удаляемый продукт был головой
}
if (productToDelete.next != -1) {
Product nextProduct = readProduct(productToDelete.next);
nextProduct.prev = prevOffset; // Обновляем указатель prev для следующего продукта
writeProduct(productToDelete.next, nextProduct);
}
else {
tail = prevOffset; // Если удаляемый продукт был хвостом
}
}
void BinaryFile::insertProduct(size_t index, const Product& product) {
long currentOffset = head;
long newOffset = file.tellp(); // Получаем текущую позицию для нового продукта
for (size_t i = 0; i < index && currentOffset != -1; ++i) {
Product currentProduct = readProduct(currentOffset);
currentOffset = currentProduct.next;
}
Product newProduct = product;
newProduct.prev = (currentOffset != -1) ? (currentOffset != head ? readProduct(currentOffset).prev : head) : tail;
newProduct.next = currentOffset;
// Обновляем указатель prev для предыдущего продукта
if (newProduct.prev != -1) {
Product prevProduct = readProduct(newProduct.prev);
prevProduct.next = newOffset; // Указатель на новый продукт
writeProduct(newProduct.prev, prevProduct);
}
else {
head = newOffset; // Новый продукт становится головой
}
writeProduct(newOffset, newProduct); // Записываем новый продукт
// Обновляем указатель prev для текущего продукта
if (currentOffset != -1) {
Product currentProduct = readProduct(currentOffset);
currentProduct.prev = newOffset;
writeProduct(currentOffset, currentProduct);
}
else {
tail = newOffset; // Если вставляем в конец, обновляем хвост
}
}
void BinaryFile::editProduct(size_t index, const Product& product) {
long currentOffset = head;
long previousOffset = -1;
// Находим продукт по индексу
for (size_t i = 0; i < index && currentOffset != -1; ++i) {
previousOffset = currentOffset;
Product currentProduct = readProduct(currentOffset);
currentOffset = currentProduct.next;
}
if (currentOffset == -1) {
throw std::out_of_range("Index out of range");
}
Product productToEdit = readProduct(currentOffset);
// Сравниваем размеры полей
size_t newNameLength = product.getName().size();
size_t newCategoryLength = product.getCategory().size();
size_t newArrivalDateLength = product.getArrivalDate().size();
size_t oldNameLength = productToEdit.getName().size();
size_t oldCategoryLength = productToEdit.getCategory().size();
size_t oldArrivalDateLength = productToEdit.getArrivalDate().size();
// Проверяем, изменился ли размер хотя бы одного поля
bool sizeChanged = (newNameLength != oldNameLength) ||
(newCategoryLength != oldCategoryLength) ||
(newArrivalDateLength != oldArrivalDateLength);
if (sizeChanged) {
// Удаляем старый продукт
deleteProduct(index);
// Добавляем новый продукт в конец файла
addProduct(product);
} else {
// Если размеры не изменились, просто перезаписываем
writeProduct(currentOffset, product);
}
}
void BinaryFile::sortProducts() {
size_t productCount = getProductCount();
std::vector<Product> products;
// Считываем все продукты в вектор
long currentOffset = head;
while (currentOffset != -1) {
products.push_back(readProduct(currentOffset));
currentOffset = products.back().next;
}
// Выбор параметра сортировки
int sortChoice;
std::cout << "Выберите параметр для сортировки:" << std::endl;
std::cout << "1. Имя" << std::endl;
std::cout << "2. Категория" << std::endl;
std::cout << "3. Количество" << std::endl;
std::cout << "4. Дата поступления" << std::endl;
std::cout << "5. Цена" << std::endl;
std::cout << "6. Процент наценки" << std::endl;
std::cout << "Введите номер параметра: ";
std::cin >> sortChoice;
// Сортировка вектора в зависимости от выбора пользователя
switch (sortChoice) {
case 1:
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.getName() < b.getName();
});
break;
case 2:
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.getCategory() < b.getCategory();
});
break;
case 3:
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.getQuantity() < b.getQuantity();
});
break;
case 4:
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.getArrivalDate() < b.getArrivalDate();
});
break;
case 5:
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.getPrice() < b.getPrice();
});
break;
case 6:
std::sort(products.begin(), products.end(), [](const Product& a, const Product& b) {
return a.getMarkupPercentage() < b.getMarkupPercentage();
});
break;
default:
std::cout << "Неверный выбор. Сортировка отменена." << std::endl;
return;
}
// Запись отсортированных продуктов обратно в файл
file.clear();
file.seekp(0, std::ios::beg);
head = -1;
tail = -1;
for (const auto& product : products) {
addProduct(product);
}
}
void BinaryFile::viewProducts(size_t pageSize, size_t pageNumber) {
long currentOffset = head;
size_t count = 0;
size_t startIndex = (pageNumber - 1) * pageSize;
// Заголовок таблицы
std::cout << std::left << std::setw(15) << "Имя"
<< std::setw(20) << "Категория"
<< std::setw(15) << "Количество"
<< std::setw(20) << "Дата поступления"
<< std::setw(15) << "Цена"
<< std::setw(15) << "Процент наценки" << std::endl;
// Разделительная линия
std::cout << std::string(100, '-') << std::endl;
while (currentOffset != -1 && count < startIndex + pageSize) {
Product product = readProduct(currentOffset);
if (count >= startIndex) {
std::cout << std::left << std::setw(15) << product.getName()
<< std::setw(20) << product.getCategory()
<< std::setw(15) << product.getQuantity()
<< std::setw(20) << product.getArrivalDate()
<< std::setw(15) << product.getPrice()
<< std::setw(15) << product.getMarkupPercentage() << std::endl;
}
currentOffset = product.next;
count++;
}
// Разделительная линия в конце
std::cout << std::string(100, '-') << std::endl;
}
void BinaryFile::compressFile() {
// Создаем временный файл для сжатых данных
const char* tempFilename = "compressed_products.bin";
std::fstream tempFile(tempFilename, std::ios::binary | std::ios::out);
// Проверяем, удалось ли открыть временный файл
if (!tempFile.is_open()) {
throw std::runtime_error("Не удалось открыть временный файл для сжатия.");
}
// Считываем все продукты и записываем их в новый файл
long currentOffset = head;
std::streampos prevPosition = -1; // Используем std::streampos для prev
while (currentOffset != -1) {
Product product = readProduct(currentOffset);
product.prev = (prevPosition == -1) ? std::streampos(-1) : prevPosition; // Приводим к std::streampos
product.next = -1; // Изначально указываем на конец списка
// Записываем продукт в временный файл
product.serialize(tempFile);
// Обновляем prevPosition для следующего продукта
prevPosition = tempFile.tellp(); // Получаем текущую позицию в файле
// Переходим к следующему продукту
currentOffset = product.next;
}
// Закрываем временный файл
tempFile.close();
std::cout << "Сжатый файл сохранен как " << tempFilename << std::endl;
}
void BinaryFile::createInvoice() {
std::vector<Product> selectedProducts;
double totalAmount = 0.0;
double totalMarkup = 0.0;
while (true) {
size_t productIndex;
std::cout << "Введите индекс продукта для добавления в фактуру (или -1 для завершения): ";
std::cin >> productIndex;
if (productIndex == static_cast<size_t>(-1)) {
break; // Завершение выбора продуктов
}
long currentOffset = head;
for (size_t i = 0; i < productIndex && currentOffset != -1; ++i) {
Product currentProduct = readProduct(currentOffset);
currentOffset = currentProduct.next;
}
if (currentOffset == -1) {
std::cout << "Продукт с таким индексом не найден." << std::endl;
continue; // Повторить выбор
}
Product selectedProduct = readProduct(currentOffset);
int quantityToBuy;
std::cout << "Введите количество для покупки (доступно: " << selectedProduct.getQuantity() << "): ";
std::cin >> quantityToBuy;
if (quantityToBuy > selectedProduct.getQuantity()) {
std::cout << "Недостаточно товара на складе." << std::endl;
continue; // Повторить выбор
}
// Уменьшение количества на складе
selectedProduct.setQuantity(selectedProduct.getQuantity() - quantityToBuy);
writeProduct(currentOffset, selectedProduct); // Обновление продукта в файле
// Подсчет суммы и торговой надбавки
double amount = selectedProduct.getPrice() * quantityToBuy;
double markup = amount * (selectedProduct.getMarkupPercentage() / 100);
totalAmount += amount;
totalMarkup += markup;
selectedProducts.push_back(selectedProduct); // Сохранение выбранного товара
}
// Вывод фактуры
std::cout << "----- Фактура -----" << std::endl;
for (const auto& product : selectedProducts) {
double amount = product.getPrice() * (product.getQuantity() + 1); // Возвращаем количество до уменьшения
std::cout << "Продукт: " << product.getName()
<< ", Количество: " << (product.getQuantity() + 1)
<< ", Цена: " << product.getPrice()
<< ", Сумма: " << amount << std::endl;
}
std::cout << "Общая сумма: " << totalAmount << std::endl;
std::cout << "Общая торговая надбавка: " << totalMarkup << std::endl;
}
Product BinaryFile::getProductByIndex(size_t index) {
long currentOffset = head;
for (size_t i = 0; i < index && currentOffset != -1; ++i) {
Product currentProduct = readProduct(currentOffset);
currentOffset = currentProduct.next;
}
if (currentOffset == -1) {
throw std::out_of_range("Index out of range");
}
return readProduct(currentOffset);
}
void displayMenu() {
std::cout << "Выберите операцию:" << std::endl;
std::cout << "1. Добавить продукт" << std::endl;
std::cout << "2. Редактировать продукт" << std::endl;
std::cout << "3. Вставить продукт" << std::endl;
std::cout << "4. Просмотреть продукты" << std::endl;
std::cout << "5. Сортировать продукты" << std::endl;
std::cout << "6. Сжать файл" << std::endl;
std::cout << "7. Составить фактуру" << std::endl;
std::cout << "8. Извлечь продукт по индексу" << std::endl;
std::cout << "9. Удалить продукт по индексу" << std::endl;
std::cout << "0. Выход" << std::endl;
}
int main() {
setlocale(LC_ALL, "Russian");
BinaryFile binFile("products.bin");
// Автоматическое добавление товаров
std::vector<Product> products = {
Product("Apples", "Fruits", 50, "10-10-2023", 1.20, 15.5),
Product("Bananas", "Fruits", 40, "10-10-2023", 0.50, 8.0),
Product("Bread", "Bakery", 30, "10-10-2023", 2.50, 10.0),
Product("Milk", "Dairy", 20, "10-10-2023", 1.00, 5.0),
Product("Chicken", "Meat", 15, "10-10-2023", 5.75, 20.0),
Product("Rice", "Grains", 25, "10-10-2023", 0.80, 12.5),
Product("Cheese", "Dairy", 10, "10-10-2023", 3.00, 7.5),
Product("Eggs", "Dairy", 12, "10-10-2023", 2.00, 6.0)
};
for (const auto& product : products) {
binFile.addProduct(product);
}
int choice;
while (true) {
displayMenu();
std::cout << "Введите номер операции: ";
std::cin >> choice;
switch (choice) {
case 1: {
std::string name, category, arrivalDate;
int quantity;
double price, markupPercentage;
std::cout << "Введите имя продукта: ";
std::cin >> name;
std::cout << "Введите категорию продукта: ";
std::cin >> category;
std::cout << "Введите количество: ";
std::cin >> quantity;
std::cout << "Введите дату поступления (YYYY-MM-DD): ";
std::cin >> arrivalDate;
std::cout << "Введите цену: ";
std::cin >> price;
std::cout << "Введите процент наценки: ";
std::cin >> markupPercentage;
Product newProduct(name, category, quantity, arrivalDate, price, markupPercentage);
binFile.addProduct(newProduct);
break;
}
case 2: {
size_t index;
std::cout << "Введите индекс продукта для редактирования: ";
std::cin >> index;
std::string name, category, arrivalDate;
int quantity;
double price, markupPercentage;
std::cout << "Введите новое имя продукта: ";
std::cin >> name;
std::cout << "Введите новую категорию продукта: ";
std::cin >> category;
std::cout << "Введите новое количество: ";
std::cin >> quantity;
std::cout << "Введите новую дату поступления (YYYY-MM-DD): ";
std::cin >> arrivalDate;
std::cout << "Введите новую цену: ";
std::cin >> price;
std::cout << "Введите новый процент наценки: ";
std::cin >> markupPercentage;
Product updatedProduct(name, category, quantity, arrivalDate, price, markupPercentage);
binFile.editProduct(index, updatedProduct);
break;
}
case 3: {
size_t index;
std::cout << "Введите индекс для вставки продукта: ";
std::cin >> index;
std::string name, category, arrivalDate;
int quantity;
double price, markupPercentage;
std::cout << "Введите имя продукта: ";
std::cin >> name;
std::cout << "Введите категорию продукта: ";
std::cin >> category;
std::cout << "Введите количество: ";
std::cin >> quantity;
std::cout << "Введите дату поступления (YYYY-MM-DD): ";
std::cin >> arrivalDate;
std::cout << "Введите цену: ";
std::cin >> price;
std::cout << "Введите процент наценки: ";
std::cin >> markupPercentage;
Product newProduct(name, category, quantity, arrivalDate, price, markupPercentage);
binFile.insertProduct(index, newProduct);
break;
}
case 4: {
size_t pageSize, pageNumber;
std::cout << "Введите размер страницы: ";
std::cin >> pageSize;
std::cout << "Введите номер страницы: ";
std::cin >> pageNumber;
binFile.viewProducts(pageSize, pageNumber);
break;
}
case 5: {
binFile.sortProducts();
std::cout << "Продукты отсортированы." << std::endl;
break;
}
case 6: {
binFile.compressFile();
std::cout << "Файл сжат." << std::endl;
break;
}
case 7: {
binFile.createInvoice();
std::cout << "Фактура создана." << std::endl;
break;
}
case 8: {
size_t index;
std::cout << "Введите индекс продукта для извлечения: ";
std::cin >> index;
try {
Product product = binFile.getProductByIndex(index);
std::cout << "Извлеченный продукт:" << std::endl;
std::cout << "Имя: " << product.getName() << std::endl;
std::cout << "Категория: " << product.getCategory() << std::endl;
std::cout << "Количество: " << product.getQuantity() << std::endl;
std::cout << "Дата поступления: " << product.getArrivalDate() << std::endl;
std::cout << "Цена: " << product.getPrice() << std::endl;
std::cout << "Процент наценки: " << product.getMarkupPercentage() << std::endl;
}
catch (const std::out_of_range& e) {
std::cout << e.what() << std::endl;
}
break;
}
case 9: {
size_t index;
std::cout << "Введите индекс продукта для удаления: ";
std::cin >> index;
try {
binFile.deleteProduct(index);
std::cout << "Продукт успешно удален." << std::endl;
}
catch (const std::out_of_range& e) {
std::cout << e.what() << std::endl;
}
break;
}
case 0:
return 0;
default:
std::cout << "Неверный выбор. Попробуйте снова." << std::endl;
}
}
return 0;
}