A telephone directory program, upon request, you can add a new contact, delete a contact, find a contact by phone number, find a contact by name and address, list the entire database. Problems arise only in the function that removes the contact (udalit_kontakt), since after deleting the contact, all fields of the other contacts are filled with arbitrary characters. How can I fix this error? I apologize for the wild mix of C and C ++

#include "stdafx.h" #include<cstring> #include<iostream> #include<cstdio> #include<stdio.h> using namespace std; struct contact { char nomer[12]; char adress[50]; char sname[20]; }; int vivod_bazi() { system("cls"); FILE *f; f = fopen("telbook.dat", "r+b"); fseek(f, 0, SEEK_END); int size = ftell(f); int kol_el = size / sizeof(contact); contact *buf = new contact[kol_el]; fseek(f, 0, SEEK_SET); fread(buf, sizeof(contact), kol_el, f); cout << "Полный список контактов базы данных\n"; cout << "-----------------------------------------------------------\n"; for (int i = 0; i < kol_el; i++) { cout << (i + 1) << ".) " << buf[i].sname; cout << "\n" << buf[i].nomer; cout << "\n" << buf[i].adress; cout << "\n\n"; } cout << "-----------------------------------------------------------\n"; fclose(f); system("pause"); return 0; } int poisk_po_nomeru() { system("cls"); char otvet = 'y'; do{ FILE *f; f = fopen("telbook.dat", "r+b"); fseek(f, 0, SEEK_END); int size = ftell(f); int kol_el = size / sizeof(contact); contact *buf = new contact[kol_el]; fseek(f, 0, SEEK_SET); fread(buf, sizeof(contact), kol_el, f); char poisk_nomer[12]; cout << "Введите номер абонента:"; fflush(stdin); cin >> poisk_nomer; int k = 0; for (int i = 0; ((i < kol_el) && k != -1); i++) { if ((strcmp(poisk_nomer, buf[i].nomer)) == 0) { system("cls"); cout << "Результаты запроса.\n"; cout << "Фамилия:" << buf[i].sname; cout << "\nНомер телефона:" << buf[i].nomer; cout << "\nАдрес:" << buf[i].adress; k = -1; }; } if (k == 0) cout << "\nКонтакт не найден."; cout << "\nПовторить запрос?[y/n]"; fflush(stdin); otvet = getchar(); } while (otvet != 'n'); return 0; } int poisk_po_fam() { system("cls"); char otvet = 'y'; do{ FILE *f; f = fopen("telbook.dat", "r+b"); fseek(f, 0, SEEK_END); int size = ftell(f); int kol_el = size / sizeof(contact); contact *buf = new contact[kol_el]; fseek(f, 0, SEEK_SET); fread(buf, sizeof(contact), kol_el, f); char poisk_sname[20]; char poisk_adress[50]; cout << "Введите фамилию абонента:"; fflush(stdin); cin >> poisk_sname; cout << "Введите адрес абонента:"; fflush(stdin); cin >> poisk_adress; int k = 0; for (int i = 0; ((i < kol_el) && k != -1); i++) { if ((strcmp(poisk_sname, buf[i].sname)) == 0) if ((strcmp(poisk_adress, buf[i].adress)) == 0) { system("cls"); cout << "Результаты запроса.\n"; cout << "Фамилия:" << buf[i].sname; cout << "\nНомер телефона:" << buf[i].nomer; cout << "\nАдрес:" << buf[i].adress; k = -1; }; } if (k == 0) cout << "\nКонтакт не найден."; cout << "\nПовторить запрос?[y/n]"; fflush(stdin); otvet=getchar(); } while (otvet != 'n'); return 0; } int dobavit_kontakt() { system("cls"); contact buf; unsigned int n; char otvet = 'y'; FILE *f; f = fopen("telbook.dat", "ab"); do { cout << "\nВведите фамилию:"; fflush(stdin); cin >> buf.sname; cout << "\nВведите номер телефона"; fflush(stdin); cin >> buf.nomer; cout << "\nВведите адрес абонента:"; fflush(stdin); cin >> buf.adress; fwrite(&buf, sizeof(contact), 1, f); cout << "\nКонтакт добавлен.\nХотите добавить ещё контакт?[y/n]"; fflush(stdin); otvet = getchar(); } while (otvet != 'n'); fclose(f); return 0; } int udalit_kontakt() { system("cls"); char otvet = 'y'; do{ FILE *f; f = fopen("telbook.dat", "r+b"); fseek(f, 0, SEEK_END); int size = ftell(f); int kol_el = size / sizeof(contact); contact *buf = new contact[kol_el]; fseek(f, 0, SEEK_SET); fread(buf, sizeof(contact), kol_el, f); fclose(f); f = fopen("telbook.dat", "w+b"); char udalit_name[12]; cout << "Введите номер контакта,который хотите удалить"; fflush(stdin); cin >> udalit_name; int k = 0; for (int i = 0; ((i < kol_el)&&k!=-1); i++) { if ((strcmp(udalit_name, buf[i].nomer)) == 0) { buf[i] = buf[kol_el-1]; fwrite(&buf, sizeof(contact), (kol_el-1), f); cout << "\nКонтакт успешно удалён!\n"; k = -1; } } if (k == 0) { cout << "\nКонтакт не найден.\n"; fwrite(&buf, sizeof(contact), kol_el, f); } cout << "Хотите удалить другой контакт?[y/n]"; fflush(stdin); otvet = getchar(); system("pause"); fclose(f); } while (otvet != 'n'); return 0; } int _tmain(int argc, _TCHAR* argv[]) { setlocale(LC_ALL, "RUS"); unsigned int rezh = 0; do{ system("cls"); const int NotUsed = system("color 03"); cout << "Выберите действие:\n"; cout << "1.Узнать адрес и фамилию по телефонному номеру.\n"; cout << "2.Узнать номер телефона по фамилии и адреу.\n"; cout << "3.Добавить новый контакт в базу данных.\n"; cout << "4.Удалить контакт.\n"; cout << "5.Показать всю базу контактов.\n"; cout << "6.Выйти из программы.\n"; cin >> rezh; switch (rezh) { case 1: { poisk_po_nomeru(); break; } case 2: { poisk_po_fam(); break; } case 3: { dobavit_kontakt(); break; } case 4: { udalit_kontakt(); break; } case 5: { vivod_bazi(); break; } } } while (rezh != 6); system("cls"); system("pause"); return 0; } 
  • 2
    Just make a field in the structure-sign of removal. It is deleted without copying from the tail to the beginning. It is possible to implement a recovery. In a normal search, skip these entries. So in all bases do. To get rid of deleted records is a separate packaging procedure. Or when adding new records to take the place of the deleted - Sergey
  • It is impossible to add code with functions in the style of "vivod_bazi ()" - Arkady
  • @Sergey, and why is it bad to move the last element to the place of the deleted, while leaving the others in place? Anyway, the author does a complete search, that is, the order of entries is unimportant. Yes, and the field is a sign of removal is not required. - ߊߚߤߘ
  • @Arhad is faster and easier - Sergey

1 answer 1

To begin with, the counter question is: why not use file databases for such purposes? For example: sqlite.

Then look at this answer: Working with data structures in a binary file , it may lead you to some thoughts.

Then, purely for the sake of style: well, do not use transliteration in the code. Bad habit. It is better to get rid of it as soon as possible.

Well, now in essence. I suggest you give up manual memory management here and use std::vector and other STL charms (first, a code with comments, and then my suggestion):

 int udalit_kontakt() { system("cls"); char otvet = 'y'; do{ FILE *f; f = fopen("telbook.dat", "r+b"); fseek(f, 0, SEEK_END); int size = ftell(f); int kol_el = size / sizeof(contact); // старайтесь обходиться без ручного управления памятью. Дальше я покажу, что у вас это приводит к ошибке с утечкой памяти на каждой итерации цикла. //contact *buf = new contact[kol_el]; std::vector<contact> contacts(kol_el); fseek(f, 0, SEEK_SET); //fread(buf, sizeof(contact), kol_el, f); fread(contacts.data(), sizeof(contact), contacts.size(), f); fclose(f); // Неплохо не растаскивать по коду аллокацию ресурса и его освобождение, поэтому // открытие перенесено ниже //f = fopen("telbook.dat", "w+b"); // Используйте STL, вы же на C++ пишете, а если номер бангладеша или китая какой? там больше 12 цифр. А если пользователь просто больше введёт? //char udalit_name[12]; std::string numberToErase; cout << "Введите номер контакта,который хотите удалить"; fflush(stdin); cin >> numberToErase; //int k = 0; size_t i; // если мы ничего не засвопили, i после цикла будет равен contacts.size(), этот признак можно использовать. for (int i = 0; i < contacts.size(); ++i) { if (numberToErase == contacts[i].nomer) { // неплохое решение, вместо удаления сделать своп с последним элементом //buf[i] = buf[kol_el-1]; contacts[i] = contacts.back(); // А вот тут косяк, в вашем первоначальном варианте buf и так указатель на область памяти, в которой хранятся последовательно // все элементы "базы", вы же передаёте адрес на адрес. Как рузультат всё перезаписывается мусором. В исходном коде // можно попытаться просто заменить [1] на [2] //fwrite(&buf, sizeof(contact), (kol_el-1), f); [1] //fwrite(buf, sizeof(contact), (kol_el-1), f); [2] // новая же запись вынесена за цикл //цикл тут достаточно завершить по break break; } } if (i == contacts.size()) { cout << "\nКонтакт не найден.\n"; // А вот этого можно не делать: вы же не изменили коллекцию, так зачем записывать то, что // и так не менялось? Плюс снова ошибка с записью адреса и мусора //fwrite(&buf, sizeof(contact), kol_el, f); } else { // а вот тут можно и удалить данные, для этого откроем файл, усечём его и запишем новыми данными f = fopen("telbook.dat", "wb"); if (f) { fwrite(contacts.data(), sizeof(contact), contacts.size() - 1, f); fclose(f); cout << "\nКонтакт успешно удалён!\n"; } else { // ошибка: не смог открыть файл для записи } } cout << "Хотите удалить другой контакт?[y/n]"; fflush(stdin); otvet = getchar(); system("pause"); // А вот в этом месте у вас ещё и утечка памяти была: вы не делали // delete[] buf; // в случае с std::vector он сам разрушится и память у вас не потечёт. } while (otvet != 'n'); return 0; } 

Plus, you don’t have to read the file size each time: you know how many structures were deleted. And you can also run and delete all the numbers first, and only then, in one session, write all this into a file.

Those. I would do something like (iostream do not use):

 int udalit_kontakt() { system("cls"); char otvet = 'y'; FILE *db = fopen("telbook.dat", "r+b"); if (!db) { cerr << "Can't phone data base for reading\n"; return -1; } fseek(db, 0, SEEK_END); size_t size = ftell(db); size_t count = size / sizeof(contact); fseek(db, 0, SEEK_SET); // В этом месте у нас код не exception-safe: // если не хватит памяти, вылетит исключение и мы потеряем db, в результате утечёт дескриптор. std::vector<contact> contacts(count); fread(contacts.data(), sizeof(contact), contacts.size(), db); fclose(db); char ask = 'y'; do { std::string numberToErase; cout << "Введите номер контакта,который хотите удалить"; cin >> numberToErase; // не будем тут использовать std::remove_if и прелести C++11 :-) bool found = false; for (size_t idx = 0; idx < count; ++idx) { if (numberToErase == contacts[i].nomer) { contacts[idx] = contacts[count - 1]; count--; found = true; break; } } if (found) cout << "Контакт найден и подготовлен к удалению\n"; else cout << "Контакт не найден"; cout << "Хотите удалить другой контакт?[y/n]"; cin >> ask; } while (ask == 'y' || ask == 'Y'); // Явно что-то удалили if (count != contacts.size()) { db = fopen("telbook.dat", "wb"); if (!db) { cerr << "Can't phone data base for writing\n"; return -1; } // удалённые контакты постепенно всплывали в конец массива fwrite(contacts.data(), sizeof(contact), count, f); fclose(f); cout << "\nВыбранные контакты успешно удалёны!\n"; } return 0; } 

The code was not checked for compilability and is shown only for the sake of the concept.