Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.10.28;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.51 MB
Время: 0.043 c
15-1191019788
Beks
2007-09-29 02:49
2007.10.28
Как вычислить значение..?


2-1191923774
новый_
2007-10-09 13:56
2007.10.28
ValueListEditor и ComboBox


15-1191253648
vrem
2007-10-01 19:47
2007.10.28
Задачка :)


1-1186743437
Dash Rendar
2007-08-10 14:57
2007.10.28
Можно ли добавить контекстное меню в ShellListView?


2-1191602274
Антон Шестаков
2007-10-05 20:37
2007.10.28
соответствующий список DBComboBox





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский