A
To begin with, why not use file databases for such purposes? Like sqlite.Then look at this answer: https://ru.stackoverflow.com/questions/464207/464215#464215 Maybe he' some kind of thought.Then, purely for the sake of not using the transliteration in the code. Bad habit. Better get rid of her as soon as possible.Well, now, in fact. I'm suggesting you give up manual memory management and use it. std::vector And other admirations of STL.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 count the size of the file every time you know how many structures have been removed. And you can run first and remove all the numbers, and then, in one parish, put all this in the file.I'd do something like that.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 wasn't checked for compulsion, and it's only a match for the sake.