Форум: "Начинающим";
Текущий архив: 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