Помощь - Поиск - Пользователи - Календарь
Полная версия: Удаление строки из текстового файла в БД
Форум «Всё о Паскале» > Pascal, Object Pascal > Задачи
hemm
База данных. Не могу наладить удаление конкретной записи.
Удаляются данные из массива, но остаются символы, разделяющие колонки.
Получается такая картина:
Изображение
*Создал две записи. Затем удалил запись №1, но пустые колонки все равно остались.

Буду благодарен за любые советы по улучшению и упрощению кода.
*процедуру проверки на существования текстового файла пока не делал, поэтому для запуска программы в том же каталоге надо создать файл DB.TXT
Проверял на FREEPASCAL.



Program Students;
Uses crt, dos;







Type DataBase=record
SecondName: string;
FirstName: string;
FatherName: string;
Group: string;
Course: string;
End;

Var Massive:array[1..50] of DataBase;
n:integer;







Procedure menu;
Forward;







{Считывание базы данных}

Procedure ReadAll;
Var f: text;
Begin
n:=0;
Assign(f,'db.txt');

{$I-}
Reset(f);
{$I+}
If IOresult<>0 then
Writeln('Ошибка: невозможно открыть исходный файл. Повторите ввод.');
While not EOF(f) do
Begin
inc(n);
Readln(f,Massive[n].SecondName);
Readln(f,Massive[n].FirstName);
Readln(f,Massive[n].FatherName);
Readln(f,Massive[n].Group);
Readln(f,Massive[n].Course);
Readln(f);
End;
close(f);
End;







{Сохранение отредактированной базы данных в файл}

Procedure SaveAll;
Var i:integer; f:text;
Begin
Assign(f,'db.txt');
ReWrite(f);
For i:=1 To n do
Begin
Writeln(f,Massive[i].SecondName);
Writeln(f,Massive[i].FirstName);
Writeln(f,Massive[i].FatherName);
Writeln(f,Massive[i].Group);
Writeln(f,Massive[i].Course);
Writeln(f);
End;
close(f);
End;







{Линия}

Procedure Line;
Var k:integer;
Begin
For k:=1 To 79 do
Begin
Write('-');
End;
Writeln;
End;







{Оглавление}

Procedure MainTitle;
Var k,m,n:integer;
Begin
Line;
Write('Фамилия |'); Write(' Имя |'); Write(' Отчество |'); Write(' Группа |');Write(' Курс');
Writeln;
Line;
End;







{Оглавление редактирования}
Procedure MainTitleEdit;
Var k,m,n:integer;
Begin
Line;
Write('№ | Фамилия |'); Write(' Имя |'); Write(' Отчество |'); Write('Группа |');
Writeln;
Line;
End;







{Оглавление процедуры поиска}
Procedure SearchTitle;
Begin
Line;
Writeln('Поиск...');
Line;
Writeln;
End;







{Добавление записи}
Procedure Add;
Var i:integer;
Begin
Clrscr;
inc(n);
Writeln('Добавление новой записи о студенте:');
Writeln;
Write('Фамилия........: '); Readln(Massive[n].SecondName);
Write('Имя............: '); Readln(Massive[n].FirstName);
Write('Отчество.......: '); Readln(Massive[n].FatherName);
Write('Номер группы...: '); Readln(Massive[n].Group);
Write('Курс...........: '); Readln(Massive[n].Course);

SaveAll;

Writeln;

Write('Запись успешно добавлена в базу данных');

Delay(1000);

Menu;

End;






{Вывод записей базы данных на экран}

Procedure WriteAll;
Var i,j:integer; s1,s2,s3,s4:string;

Begin
clrscr;

MainTitle;

For i:=1 To n do

{-------- ВОТ ЗДЕСЬ ПОДСКАЖИТЕ КАК УПРОСТИТЬ}
Begin

s1:='';
s2:='';
s3:='';
s4:='';

For j:=1 To 18-length(Massive[i].SecondName) do s1:=s1+' ';
For j:=1 To 8-length(Massive[i].FirstName) do s2:=s2+' ';
For j:=1 To 18-length(Massive[i].FatherName) do s3:=s3+' ';
For j:=1 To 18-length(Massive[i].Group) do s4:=s4+' ';

Writeln(Massive[i].SecondName,s1,' | ',Massive[i].FirstName,s2,' | ',Massive[i].FatherName,s3,' | ',Massive[i].Group, s4, ' | ', Massive[i].Course);

End;

Line;

Writeln;
Write(' |Enter|.. Переход в меню');
Readln;

Menu;

End;







{Вывод записей базы данных с сортировкой на экран}

Procedure Sort;

Procedure Exchange(Var a,b:string);
Var c:string;
Begin
c:=a;
a:=b;
b:=c;
End;

Var i,Letter,nn:integer;
Swop:boolean;
SecondName1,SecondName2:string;

Begin

Clrscr;

For Letter:=5 DownTo 1 do
Begin
nn:=n;

Repeat

swop:=false;

For i:=1 To nn-1 do Begin

SecondName1:=Massive[i].SecondName;
SecondName2:=Massive[i+1].SecondName;

If Ord(SecondName1[Letter])>Ord(SecondName2[Letter]) then

Begin

Exchange(Massive[i].SecondName, Massive[i+1].SecondName);
Exchange(Massive[i].FirstName, Massive[i+1].FirstName);
Exchange(Massive[i].FatherName, Massive[i+1].FatherName);
Exchange(Massive[i].Group, Massive[i+1].Group);
Exchange(Massive[i].Course, Massive[i+1].Course);

swop:=true;

End;

End;

nn:=nn-1;

Until not swop;
End;

MainTitle;

WriteAll;

Line;

Readln;

Menu;

End;







{Поиск по фамилии}

Procedure SearchSecondName;
Var SN: string;
i,j: integer;
s1,s2,s3: string;
Swop: boolean;
x: char;

Begin

Clrscr;

SearchTitle;

Write('Введите фамилию студента: ');

Readln(SN);

Writeln;

Swop:=false;

MainTitle;

For i:=1 To n do

If Massive[i].SecondName=SN then

Begin
{-------- ВОТ ЗДЕСЬ ПОДСКАЖИТЕ КАК УПРОСТИТЬ}

s1:='';
s2:='';
s3:='';

For j:=1 To 18-length(Massive[i].SecondName) do s1:=s1+' ';
For j:=1 To 8-length(Massive[i].FirstName) do s2:=s2+' ';
For j:=1 To 18-length(Massive[i].FatherName) do s3:=s3+' ';

Writeln(Massive[i].SecondName,s1,' | ',Massive[i].FirstName,s2,' | ',Massive[i].FatherName,s3,' | ',Massive[i].Group,' | ',Massive[i].Course);

Swop:=true;
End;

Line;


If not Swop then Writeln('Записей с такой фамилией не обнаружено либо имя указано неверно');

Writeln;

Line;

Writeln(' |1|...... Повторный поиск');
Writeln(' |0|...... Выход из программы');
Writeln(' |Enter|.. Переход в меню');

Line;

Write('Ваш выбор| ');

Readln(x);

Case x of
'1':SearchSecondName;
'0':halt;

Else Menu;
End;
End;









{Поиск по группе}

Procedure SearchGroup;
Var
SG:string;
i,j:integer;
s1,s2,s3:string;
Swop:boolean;
x:char;

Begin

clrscr;

SearchTitle;

Write('Введите номер группы: ');
Readln(SG);
Writeln;

Swop:=false;

MainTitle;

For i:=1 To n do
If Massive[i].Group=SG then
Begin

s1:='';
s2:='';
s3:='';

For j:=1 To 18-length(Massive[i].SecondName) do s1:=s1+' ';
For j:=1 To 8-length(Massive[i].FirstName) do s2:=s2+' ';
For j:=1 To 18-length(Massive[i].FatherName) do s3:=s3+' ';

Writeln(Massive[i].SecondName,s1,' | ',Massive[i].FirstName,s2,' | ',Massive[i].FatherName,s3,' | ',Massive[i].Group,' | ',Massive[i].Course);

Swop:=true;
End;

Line;

If not Swop then Writeln('Записей с таким номером группы не обнаружено.');

Writeln;

Line;

Writeln(' |1|...... Повторный поиск');
Writeln(' |0|...... Выход из программы');
Writeln(' |Enter|.. Переход в меню');

Line;

Write('Ваш выбор| ');

Readln(x);

Case x of
'1':SearchGroup;
'0':halt;
Else
Menu;
End;
End;









{Редактирование}
Procedure Edit;
Var
i,j:integer;
s1,s2,s3:string;
Swop:boolean;

Begin

Clrscr;

Writeln('Редактирование записей базы данных:');
Writeln;

MainTitleEdit;

For i:=1 To n do

Begin

s1:='';
s2:='';
s3:='';

For j:=1 To 18-length(Massive[i].SecondName) do s1:=s1+' ';
For j:=1 To 8-length(Massive[i].FirstName) do s2:=s2+' ';
For j:=1 To 18-length(Massive[i].FatherName) do s3:=s3+' ';

Writeln(i,'. ',Massive[i].SecondName,s1,' | ',Massive[i].FirstName,s2,' | ',Massive[i].FatherName,s3,' | ',Massive[i].Group,' | ',Massive[i].Course);

End;

Writeln;

Write('Введите номер редактируемой записи (укажите [0] для отмены): ');

Readln(i);

If i=0 then

Begin
Writeln('Отмена редактирования');
Delay(500);
Menu;
End;

s1:='';
s2:='';
s3:='';

clrscr;

Writeln('Изменяем:');
For j:=1 To 18-length(Massive[i].SecondName) do s1:=s1+' ';
For j:=1 To 8-length(Massive[i].FirstName) do s2:=s2+' ';
For j:=1 To 18-length(Massive[i].FatherName) do s3:=s3+' ';

MainTitleEdit;

Writeln(i,'. ',Massive[i].SecondName,s1,' | ',Massive[i].FirstName,s2,' | ',Massive[i].FatherName,s3,' | ',Massive[i].Group);
Writeln;
Writeln('Введите новые данные:');

Write('Фамилия........: '); Readln(Massive[i].SecondName);
Write('Имя............: '); Readln(Massive[i].FirstName);
Write('Отчество.......: '); Readln(Massive[i].FatherName);
Write('Номер группы...: '); Readln(Massive[i].Group);
Write('Курс...........: '); Readln(Massive[i].Course);

SaveAll;
ReadAll;

Writeln;
Write('Запись успешно отредактирована.');
Delay(1000);

Menu;

End;









{Удаление всех записей}

Procedure DeleteAll;
Var f:text; k:char;
Begin
Writeln;
Line;
Writeln;
Writeln('Вы действительно хотите удалить все данные? [указать "Y" для удаления]');
Write('Ваш выбор| ');
Readln(k);

If (k='Y') or (k='y') then
Begin
Assign(f,'db.txt');
ReWrite(f);
Write('');
close(f);
Writeln;
Write(' Все данные успешно удалены');
Delay(1000);
Menu;
End;
Writeln(' Указан неверный символ');
Delay(500);
Writeln(' Переход в меню');
Delay(500);
Menu;

End;

{---------------------------------------}







{ ---------------------------- НЕКОРРЕКТНО УДАЛЯЕТ ЗАПИСЬ. УДАЛЯЕТ ДАННЫЕ ИЗ МАССИВА, НО В СТРОКЕ ОСТАЮТСЯ СИМВОЛЫ "|" КОТОРЫЕ РАЗДЕЛЯЮТ КОЛОНКИ }

{Удаление записи}

Procedure Deleting;
Var i,j:integer;
begin
Writeln('Удалить запись с номером: ');
readln(i);

Delete(Massive[i].SecondName,1,50);
Delete(Massive[i].FirstName,1,50);
Delete(Massive[i].FatherName,1,50);
Delete(Massive[i].Group,1,50);
Delete(Massive[i].Course,1,50);


SaveAll;
ReadAll;

Writeln;
Write('Запись успешно удалена.');
Delay(1000);

Menu;
End;









Procedure Password;
Var i,s:integer;
Begin
n:=5;
For i:= 1 To n do Begin
clrscr;
Writeln('Количество попыток ввода правильного пароля: ', n);
Write( 'Введите пароль: ' );
Readln(S);
n:=n-1;
If S = 1234 then Break
Else If i <> 5 then continue;
clrscr;
goToxy(1,1);
Writeln('Количество попыток ввода правильного пароля: 0');
Delay(1000);
Writeln( 'Доступ запрещен!');
Delay(2000);
Writeln( 'Завершение работы программы...');
Delay(1000);
Halt;
End;
End;









{Меню}

Procedure Menu;

Var option:char;

Begin

clrscr;

ReadAll;
Writeln('База данных "Студенты"');
Line;
Writeln(' Меню');
Line;
Writeln(' |1| Добавление новой записи');
Writeln(' |2| Просмотр записей');
Writeln(' |3| Просмотр записей с сортировкой');
Writeln(' |4| Редактирование записи');
Writeln(' |5| Удаление записи');
Writeln(' |6| Очистить базу данных');
Line;
Writeln(' Поиск');
Line;
Writeln(' |7| Поиск по фамилии');
Writeln(' |8| Поиск по группе');
Line;
Writeln(' |0| Выход из программы');
Line;
Write('Ваш выбор| ');
Readln(option);
Case option of
'1':Add;
'2':WriteAll;
'3':Sort;
'4':Edit;
'5':Deleting;
'6':DeleteAll;
'7':SearchSecondName;
'8':SearchGroup;
'0':halt;

Else Menu;
End;

End;



Begin

{Password;}

Menu;

End.

IUnknown
Удалять надо не строки из содержимого ячейки массива, а собственно саму ячейку (сдвигать все содержимое массива на одну позицию влево, начиная от i-той) :

Procedure Deleting;
Var i, j:integer;
begin
Writeln('Удалить запись с номером: ');
readln(i);
if i <= n then
begin
Move(Massive[i+1], Massive[i], Sizeof(Database) * (n - i));
dec(n);
SaveAll;
// ReadAll;
Writeln;
Write('Запись успешно удалена.');
end
else write('Ошибка: неверный номер записи...');
Delay(1000);
// Menu; { <--- }
End;
(можно было, конечно, передвинуть все элементы массива вручную, обычным циклом, но зачем, если есть процедура, которая делает это сама...)

Как видишь, я внес еще пару изменений в код: для начала - не надо пересчитывать содержимое файла в массив, зачем? Ты ж только что его записал туда... Добавлена так же проверка на ошибку в номере записи (можешь добавить еще проверку на i > 0, чтоб нельзя было ввести нулевых или отрицательных номеров).

Ну, и, наконец - самая большая ошибка: вызов Menu в конце работы процедуры, из этого же Menu вызванной. Не надо этого делать!!!

Ошибки при организации меню (здесь я собрал часто встречающиеся ошибки. Так вот, ошибка №2 - это твой случай)
hemm
Большое спасибо, Владимир.
Но вот насчет:

Цитата
Ну, и, наконец - самая большая ошибка: вызов Menu в конце работы процедуры, из этого же Menu вызванной. Не надо этого делать!!!


Если убираю допустим после Добавления новой записи (Procedure Add) процедуру Menu, по завершению ввода данных программа завершает свою работу, а не переходит обратно в меню.

И вот если я буду из меню открывать процедуру, а затем опять же возвращаться в меню, в конечном итоге это забьет стек?
IUnknown
Ах, так у тебя и сама процедура Menu неверно написана:

Procedure Menu;
Var option: char;
Begin
ReadAll;

repeat
clrscr;
Writeln('База данных "Студенты"');
Line;
Writeln(' Меню');
Line;
Writeln(' |1| Добавление новой записи');
Writeln(' |2| Просмотр записей');
Writeln(' |3| Просмотр записей с сортировкой');
Writeln(' |4| Редактирование записи');
Writeln(' |5| Удаление записи');
Writeln(' |6| Очистить базу данных');
Line;
Writeln(' Поиск');
Line;
Writeln(' |7| Поиск по фамилии');
Writeln(' |8| Поиск по группе');
Line;
Writeln(' |0| Выход из программы');
Line;
Write('Ваш выбор| ');

Readln(option);
Case option of
'1':Add;
'2':WriteAll;
'3':Sort;
'4':Edit;
'5':Deleting;
'6':DeleteAll;
'7':SearchSecondName;
'8':SearchGroup;
end;
until option = '0';
End;
Перед тем, как запускать - убедись, что больше нигде Menu рекурсивно не вызывается. Иначе по первому нажатию "0" не выйдешь изменю, придется нажимать столько раз, сколько было рекурсивных вызовов.

Цитата
И вот если я буду из меню открывать процедуру, а затем опять же возвращаться в меню, в конечном итоге это забьет стек?
Если будешь из Menu вызывать процедуру, а затем, в ее конце - опять вызывать Menu - то да, стек будет заполняться. Если даже не забьешь полностью - то займешь какое-то пространство, которое может пригодиться: стека много не бывает...

Я надеюсь, программа отлаживается в контролем стека? (Options->Compiler->Generated code->Stack checking включено?)
hemm
Фантастика! Благодарю!

Нашел еще одну проблему. Если ввести очень длинную фамилию, то она смещает все данные и таблица нарушается.



{Вывод записей базы данных на экран}

Procedure WriteAll;
Var i,j:integer; s1,s2,s3,s4:string;

Begin
clrscr;

MainTitle;

For i:=1 To n do

{-------- ВОТ ЗДЕСЬ ПОДСКАЖИТЕ КАК УПРОСТИТЬ}
Begin

s1:='';
s2:='';
s3:='';
s4:='';

For j:=1 To 18-length(Massive[i].SecondName) do s1:=s1+' ';
For j:=1 To 8-length(Massive[i].FirstName) do s2:=s2+' ';
For j:=1 To 18-length(Massive[i].FatherName) do s3:=s3+' ';
For j:=1 To 18-length(Massive[i].Group) do s4:=s4+' ';

Writeln(Massive[i].SecondName,s1,' | ',Massive[i].FirstName,s2,' | ',Massive[i].FatherName,s3,' | ',Massive[i].Group, s4, ' | ', Massive[i].Course);

{ ----------- МОЖНО ЛИ ЗДЕСЬ ОГРАНИЧИТЬ КОЛИЧЕСТВО ВЫВОДИМЫХ СИМВОЛОВ? ПОПЫТАЛСЯ УКАЗАТЬ Massive[i].SecondName: 5, НО ВИДИМО ЭТО ЛИШЬ ДЛЯ ЧИСЕЛ ПОДХОДИТ }

End;

Line;

Writeln;
Write(' |Enter|.. Переход в меню');
Readln;

IUnknown
Procedure WriteAll;
var
i : integer;
Begin
clrscr;
MainTitle;
For i:=1 To n do
with Massive[i] do
begin
Writeln(
copy(SecondName, 1, 16):18, ' | ',
copy(FirstName, 1, 6):8,' | ',
copy(FatherName, 1, 16):18, ' | ',
copy(Group, 1, 16):18, ' | ',
Course
);
end;
Line;
Writeln;
Write(' |Enter|.. Переход в меню');
Readln;
End;
Теперь какая бы не была фамилия - отобразятся только первые её 16 символов.

Цитата
МОЖНО ЛИ ЗДЕСЬ ОГРАНИЧИТЬ КОЛИЧЕСТВО ВЫВОДИМЫХ СИМВОЛОВ? ПОПЫТАЛСЯ УКАЗАТЬ Massive[i].SecondName: 5, НО ВИДИМО ЭТО ЛИШЬ ДЛЯ ЧИСЕЛ ПОДХОДИТ
Это и для чисел не подходит. Нельзя задать ширину поля меньше, чем нужно для вывода числа:
var i : integer;
begin
i := 10241;
writeln(i:4);
end.
не напечатает 1024, будет выведено число полностью.
IUnknown
Итак, от изначальных 715 строк исходного файла в результате нехитрых действий осталось чуть больше 420:

Много кода, поэтому под спойлером (Показать/Скрыть)
Можно еще покумекать над объединением двух процедур поиска в одну (я где-то на форуме выкладывал пример, если не ошибаюсь, надо будет поискать). Также стоит подумать над вынесением часто повторяющихся
Write('bla-bla-bla');
Delay(...);

в отдельную процедуру. В общем, заготовка у тебя есть, допиливай smile.gif
-TarasBer-
Спойлер (Показать/Скрыть)
IUnknown
Спойлер (Показать/Скрыть)
hemm
Допилил. Все работает как надо.

Единственное, что при редактировании (если в базе больше 10 записей) нельзя прокрутить и посмотреть первые.

Получается такая штука:

Изображение

Спойлер (Показать/Скрыть)
IUnknown
Прокрутку я тоже как-то показывал на форуме, поищи.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.