Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.10.28;
Скачать: CL | DM;

Вниз

Неправильно удаляются записи из нетипизированного файла   Найти похожие ветки 

 
tmp   (2007-10-02 11:55) [0]

Не могу понять где тут ошибка. Есть нетипизированный файл, состоящий из записей динамической длины.
Запись выглядет так:

RecNameLen: Cardinal; // длина имени записи
RecName:    PChar     // имя записи + #0
DataLen:    Cardinal; // длина данных
BinData:    Pointer;  // сами данные (в данном примере это строка PChar)

Чтобы ускорить процесс удаления записи, мэппирую .dat файл, выделяю блок памяти, в который
последовательно записываются все записи кроме удаляемой. Во время чтения данных файла происходит
сравнение функцией lstrcmpi имен найденной записи с именем удаляемой. Если имена не совпадают,
то найденная запись копируется, если совпадают - пропускается. При копировании записи в
переменную запоминается размер записи + размер поля, содержащего этот размер. После прохода
по всему файлу переменная, содержащая колличество скопированных байт, сравнивается с размером
исходного файла данных и если они не равны, то создается временный файл, в который записываются
скопированные данные. Далее оригинальный .dat файл удаляется, а временный файл переименовывается
в оригинальный .dat. Функция записывает неверные данные после удаления записи. Причем все смещения
расчитываются правильно, т.к если бы это было не так, то имена найденных записей отображались бы
неправильно (см. ShowMessage(PChar(Cardinal(MfCurrentAddr) + INFO_FIELD_SIZE)))
Короче: я запутался! :)

На всякий уточню: записей с одинаковыми именами в файле быть не может, т.к в реальной программе есть
проверка перед записью в .dat. Поэтому к "вырубке" всех "совпадающих" нижеприведенная функция не приведет.

Вопрос: можно как-то упростить эту функцию и не ли тут ошибок?

function DeleteRecord(const DataFile, RecName: string): Boolean;
const
 INFO_FIELD_SIZE = SizeOf(Cardinal);
var
 MfBaseAddr, AlBaseAddr, MfCurrentAddr, AlCurrentAddr: Pointer;
 hDataFile, hTempFile, hMap: THandle;
 DataFileSize, MfMaxAddr, Copyed, Written: Cardinal;
 TempFile: string;
 RecLen, NameLen, DataLen, NextOffset: Cardinal;
begin
 Result := False;
 hDataFile := CreateFile(PChar(DataFile), GENERIC_READ, 0, nil, OPEN_EXISTING,
   FILE_ATTRIBUTE_NORMAL, 0);
 if hDataFile <> INVALID_HANDLE_VALUE then
 begin
   DataFileSize := GetFileSize(hDataFile, nil);
   hMap := CreateFileMapping(hDataFile, nil, PAGE_READONLY, 0, DataFileSize, nil);
   if hMap > 0 then
   begin
     MfBaseAddr := MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, DataFileSize);
     if Assigned(MfBaseAddr) then
     begin
       MfCurrentAddr := MfBaseAddr;
       // сюда будут копироваться записи, не подлежащие удалению
       AlBaseAddr := VirtualAlloc(nil, DataFileSize, MEM_COMMIT, PAGE_READWRITE);
       if Assigned(AlBaseAddr) then
       begin
         AlCurrentAddr := AlBaseAddr;
         // вычисляем конечный адрес мэппированных данных
         MfMaxAddr := Cardinal(MfBaseAddr) + DataFileSize;
         Copyed := 0;
         while Cardinal(MfCurrentAddr) < MfMaxAddr do
         begin
           NameLen := Cardinal(MfCurrentAddr^);
           DataLen := Cardinal(Pointer(Cardinal(MfCurrentAddr) + INFO_FIELD_SIZE + NameLen)^);
           RecLen := INFO_FIELD_SIZE + INFO_FIELD_SIZE + NameLen + DataLen;
           NextOffset := 0;
           ShowMessage(PChar(Cardinal(MfCurrentAddr) + INFO_FIELD_SIZE)); // Проверка    
           if lstrcmpi(PChar(Cardinal(MfCurrentAddr) + INFO_FIELD_SIZE), PChar(RecName)) <> 0 then
           begin
             // длина имени записи
             CopyMemory(AlCurrentAddr, MfCurrentAddr, INFO_FIELD_SIZE);
             Inc(NextOffset, INFO_FIELD_SIZE);
             AlCurrentAddr := Pointer(Cardinal(AlCurrentAddr) + NextOffset);
             // имя записи
             CopyMemory(AlCurrentAddr, Pointer(Cardinal(MfCurrentAddr) + NextOffset), NameLen);
             Inc(NextOffset, NameLen);
             AlCurrentAddr := Pointer(Cardinal(AlCurrentAddr) + NextOffset);
             // длина данных
             CopyMemory(AlCurrentAddr, Pointer(Cardinal(MfCurrentAddr) + NextOffset),
               INFO_FIELD_SIZE);
             Inc(NextOffset, INFO_FIELD_SIZE);
             AlCurrentAddr := Pointer(Cardinal(AlCurrentAddr) + NextOffset);
             // данные
             CopyMemory(AlCurrentAddr, Pointer(Cardinal(MfCurrentAddr) + NextOffset), DataLen);
             Inc(NextOffset, DataLen);
             AlCurrentAddr := Pointer(Cardinal(AlCurrentAddr) + NextOffset);
             Inc(Copyed, RecLen);
           end;
           // переходим на новый адрес в мэппированном файле
           MfCurrentAddr := Pointer(Cardinal(MfCurrentAddr) + RecLen);
         end;  
         // если скопированно меньше исходного размера, значит запись найдена  
         if Copyed < DataFileSize then
         begin
           TempFile := ChangeFileExt(DataFile,".tmp");  
           hTempFile := CreateFile(PChar(TempFile), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
             FILE_ATTRIBUTE_NORMAL, 0);
           if hTempFile <> INVALID_HANDLE_VALUE then
           begin
             Result := WriteFile(hTempFile, AlBaseAddr^, Copyed, Written, nil);    
             CloseHandle(hTempFile);
           end;
         end;  
         VirtualFree(AlBaseAddr, DataFileSize, MEM_DECOMMIT);
       end; // if Assigned(AlBaseAddr)
       UnmapViewOfFile(MfBaseAddr);
     end; // if Assigned(MfBaseAddr)  
     CloseHandle(hMap);
   end; // if hMap > 0
   CloseHandle(hDataFile);          
 end; // if hDataFile <> INVALID_HANDLE_VALUE
 if Result then
 begin
   DeleteFile(DataFile);  
   MoveFile(PChar(TempFile), PChar(DataFile));
 end
 else if FileExists(TempFile) then
   DeleteFile(TempFile);  
end;


 
tmp   (2007-10-02 11:56) [1]

Следующие процедуры удаляют/записывают в файл данные

procedure TForm1.Button2Click(Sender: TObject);
var
 hDataFile: THandle;
 NameLen, DataLen, fSize,  Written: Cardinal;
 RecName, SData: string;
 ErrSuccess: Boolean;
begin
 if Trim(edRecName.Text) = "" then
   edRecName.Text := TimeToStr(Time);
 if Trim(Memo1.Text) = "" then
   Memo1.Lines.Add(DateToStr(Date));
 RecName := edRecName.Text;
 SData := Memo1.Text;
 NameLen := Length(RecName) + 1;
 DataLen := Length(SData) + 1;
 if Trim(edFileName.Text) = "" then
 with TSaveDialog.Create(nil) do
 begin
   Filter := "Файлы .DAT|*.dat";
   DefaultExt := "dat";
   FileName := "new";
   if Execute then
     edFileName.Text := FileName;
   Free;
 end;    
 if Trim(edFileName.Text) = "" then
   Exit;
 ErrSuccess := False;
 hDataFile := CreateFile(PChar(edFileName.Text), GENERIC_WRITE, 0, nil, OPEN_ALWAYS,
   FILE_ATTRIBUTE_NORMAL, 0);
 if hDataFile <> INVALID_HANDLE_VALUE then
 begin
   fSize := GetFileSize(hDataFile, nil);
   if SetFilePointer(hDataFile, fSize, nil, FILE_BEGIN) <> DWORD(-1) then
   begin
     if WriteFile(hDataFile, NameLen, SizeOf(NameLen), Written, nil) then
       if WriteFile(hDataFile, PChar(RecName)^, NameLen, Written, nil) then
         if WriteFile(hDataFile, DataLen, SizeOf(DataLen), Written, nil) then
           ErrSuccess := WriteFile(hDataFile, PChar(SData)^, DataLen, Written, nil);
   end else
     Application.MessageBox("SetFilePointer", nil, MB_ICONERROR);
   CloseHandle(hDataFile);  
 end else
   Application.MessageBox("CreateFile", nil, MB_ICONERROR);
 if not ErrSuccess then
   Application.MessageBox(PChar("Ошибка при записи в файл " " + edFileName.Text + " ""), nil,
     MB_ICONERROR);            
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 if DeleteRecord(edFileName.Text, Trim(edRecName.Text)) then
   Application.MessageBox(PChar("Запись " " + edRecName.Text + " " успешно удалена из файла."),
     "Запись удалена", MB_ICONINFORMATION)
 else
   Application.MessageBox("При удалении записи произошла ошибка.", nil, MB_ICONERROR);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 edFileName.Text := ExtractFilePath(Application.ExeName) + "new.dat";
end;


 
Сергей М. ©   (2007-10-02 11:59) [2]


> RecName:    PChar


> BinData:    Pointer


Бессмысленно хранить в файле указатели.


 
Сергей М. ©   (2007-10-02 12:07) [3]


> tmp


Изобретение очередного велосипеда - задача учебная ?)


 
tmp   (2007-10-02 12:19) [4]


> Сергей М. ©   (02.10.07 12:07) [3]
>
>
> > tmp
>
>
> Изобретение очередного велосипеда - задача учебная ?)
>

И да и нет.
С одной строны программа пишется на чистом API, с другой - для себя стало интересно :)



> Сергей М. ©   (02.10.07 11:59) [2]
>
>
> > RecName:    PChar
>
>
> > BinData:    Pointer
>
>
> Бессмысленно хранить в файле указатели.

В файле храняться обычные строки (см. procedure TForm1.Button2Click)... или я чего-то не понял?


 
Сергей М. ©   (2007-10-02 12:26) [5]


> программа пишется на чистом API


Мало ли в Windows существующих по сабжу готовых механизмов, доступ к которым при желании можно осуществлять "на чичтом API")


> В файле храняться обычные строки


Тип PChar, упомянутый тобой с структуре в [0], - это не строка, это указатель на месторасположение строки.

Структура эта, кстати, вообще не фигурирует в [1].


 
clickmaker ©   (2007-10-02 12:31) [6]

а почему бы не читать данные в массив структур или List, удалять оттуда, а потом писать обратно?


 
tmp   (2007-10-02 13:37) [7]


> Сергей М. ©   (02.10.07 12:26) [5]
>
>
> > программа пишется на чистом API
>
>
> Мало ли в Windows существующих по сабжу готовых механизмов,
>  доступ к которым при желании можно осуществлять "на чичтом
> API")
>
>
> > В файле храняться обычные строки
>
>
> Тип PChar, упомянутый тобой с структуре в [0], - это не
> строка, это указатель на месторасположение строки.
>
> Структура эта, кстати, вообще не фигурирует в [1].

Это чтобы понятнее была структра файла. Сама структура в файле естественно не хранится (взгляните на код).


 
Сергей М. ©   (2007-10-02 13:46) [8]


> tmp   (02.10.07 13:37) [7]


Не проще ли было воспользоваться структурированными хранилищами (structured storage files) ?


 
tmp   (2007-10-02 13:57) [9]

Сергей М. ©   (02.10.07 13:46) [8]

> tmp   (02.10.07 13:37) [7]

Не проще ли было воспользоваться структурированными хранилищами (structured storage files) ?

Я гляну... Но в чем все же ошибка? Неужели в PChar? (не проверял)


 
Сергей М. ©   (2007-10-02 13:59) [10]


> Но в чем все же ошибка?


Ты думаешь мне интересно в этой абракадабре разбираться ?)


 
Slym ©   (2007-10-03 07:23) [11]

program Project1;

{$APPTYPE CONSOLE}

uses
 windows,SysUtils;
type
 PData=^TData;
 TData=packed record
   Size:integer;
   Value:array[0..0] of char;
 end;
function DeleteRecord(const Source,Dest:Pointer;const SourceSize:DWORD;var DestSize:DWORD;const RecName: string): Boolean;
var
SourcePos, DestPos, MaxPos: Pointer;
CurrentRecName:string;
RecLengh:integer;
begin
 SourcePos:=Source;
 DestPos:=Dest;
 MaxPos:=pointer(DWORD(Source)+SourceSize);
 while integer(SourcePos)<integer(MaxPos) do
 begin
   SetString(CurrentRecName,PData(SourcePos).Value,PData(SourcePos).Size-1);
   if CurrentRecName<>RecName then
   begin
     //Copy NameRecord
     RecLengh:=SizeOf(PData(SourcePos).Size)+PData(SourcePos).Size;
     CopyMemory(DestPos,SourcePos,RecLengh);
     Inc(PChar(DestPos),RecLengh);
     Inc(PChar(SourcePos),RecLengh);
     //Copy DataRecord
     RecLengh:=SizeOf(PData(SourcePos).Size)+PData(SourcePos).Size;
     CopyMemory(DestPos,SourcePos,RecLengh);
     Inc(PChar(DestPos),RecLengh);
     Inc(PChar(SourcePos),RecLengh);
   end else
   begin
     //Skip NameRecord
     RecLengh:=SizeOf(PData(SourcePos).Size)+PData(SourcePos).Size;
     Inc(PChar(SourcePos),RecLengh);
     //Skip DataRecord
     RecLengh:=SizeOf(PData(SourcePos).Size)+PData(SourcePos).Size;
     Inc(PChar(SourcePos),RecLengh);
   end;
 end;
 DestSize:=integer(DestPos)-integer(Dest);
 result:=SourceSize<>DestSize;
end;

function DeleteRecordFromFile(const DataFile,RecName: string): Boolean;
var
hFile, hMap: THandle;
FileSize,NewSize: DWORD;
Mem: Pointer;
begin
 hFile:=CreateFile(PChar(DataFile), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING,0, 0);
 if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
 try
   FileSize := GetFileSize(hFile, nil);
   hMap := CreateFileMapping(hFile, nil, PAGE_READWRITE, 0, FileSize, nil);
   if hMap = 0 then RaiseLastOSError;
   try
     Mem:=MapViewOfFile(hMap, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, FileSize);
     if not Assigned(Mem) then RaiseLastOSError;
     try
       result:=DeleteRecord(Mem,Mem,FileSize,NewSize,RecName);
     finally
       if not UnmapViewOfFile(Mem) then RaiseLastOSError;
     end;
   finally
     if not CloseHandle(hMap) then RaiseLastOSError;
   end;
   if SetFilePointer(hFile,NewSize,nil,FILE_BEGIN)<>NewSize then RaiseLastOSError;
   if not SetEndOfFile(hFile) then RaiseLastOSError;
 finally
   if not CloseHandle(hFile) then RaiseLastOSError;
 end;
end;



Страницы: 1 вся ветка

Текущий архив: 2007.10.28;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.02 c
1-1187177937
partizan
2007-08-15 15:38
2007.10.28
TWebBrowser, текст страници


15-1190734016
ProgRAMmer Dimonych
2007-09-25 19:26
2007.10.28
Вопрос по ресурсам


15-1190881952
Vlad Oshin
2007-09-27 12:32
2007.10.28
Как забросить Cookie на определенное время? Не получается


15-1191578295
Германн
2007-10-05 13:58
2007.10.28
С Днем Учителя!


4-1177575482
capkoh
2007-04-26 12:18
2007.10.28
Повторное открытие диалога