2009-03-30 3 views
0

У меня проблема с сохранением большого размера базы данных в Delphi. Он содержит массив [1..3500] TItem, который, в свою очередь, имеет два массива [1.50] и [1..20]. Я получаю переполнение стека, если не установить переменную как указатель и использовать команды GetMem, FreeMem ниже, но тогда я не могу ее сохранить. Код ниже.Сохранение и переполнение стека

procedure TDatabase.SaveDB; 
var 
TempDB: ^TSaveDB; 
K, X: integer; 
sComment, sTitle, sComposer, sISDN, sCategory: string; 
begin 
GetMem(TempDB, SizeOf(TSaveDB)); 

TempDB.CatCount := fCategoryCount; 
TempDB.ItemCount := fItemCount; 

for K := 1 to fCategoryCount do 
TempDB.Categories[K] := fCategories[K]; 

for K := 1 to fItemCount do 
begin 
    fItems[K].ReturnSet(sTitle, sComposer, sCategory, sISDN, sComment); 
    with TempDB.Items[K] do 
    begin 
    Title := sTitle; 
    Composer := sComposer; 
    Category := sCategory; 
    ISDN := sISDN; 
    end; 

    TempDB.Items[K].Comments[1] := Copy(sComment, 1, 255); 
    Delete(sComment, 1, 255); 
    TempDB.Items[K].Comments[2] := Copy(sComment, 1, 255); 
    Delete(sComment, 1, 255); 
    TempDB.Items[K].Comments[3] := Copy(sComment, 1, 255); 
    Delete(sComment, 1, 255); 
    TempDB.Items[K].Comments[4] := Copy(sComment, 1, 255); 
    Delete(sComment, 1, 255); 

    TempDB.Items[K].KeyWCount := fItems[K].GetKeyCount; 

    for X := 1 to fItems[K].GetKeyCount do 
    TempDB.Items[K].Keywords[X] := fItems[K].GetKeywords(X); 
end; 

AssignFile(DBSave, fSaveName); 
Rewrite(DBSave); 
    Write(DBSave, TempDB); 
Closefile(dBSave); 

FreeMem(TempDB, sizeof(TSaveDB)); 
end; 

ответ

1

Ваша проблема в инструкции «написать». Ведение вещей с помощью произвольных указателей приводит к всевозможным странным поведением. У вас было бы намного легче, если бы вы переписали это с помощью TFileStream вместо текущего подхода.

3

Использование GetMem или SetLength или TList/TObjectList и запись в файл одного TSaveDB за раз. Или измените тип файла и используйте BlockWrite, чтобы написать все сразу. Или еще лучше: используйте TFileStream.

1

Чтобы расширить ответ Мейсона:

НИКОГДА не чтения или записи указатель, период. Чтобы добиться чего-либо разумного, и в реальном мире, когда вы не просто запускаете свою программу, шансы успеха идут от бесконечно малых до нуля.

Скорее, вам нужно прочитать и написать то, на что указывает указатель.

Обратите внимание, что любая строка, длина которой не указана в объявлении, является указателем, если вы не работаете в режиме совместимости, который делает строку «string» в «string [255]» - этот режим существует только для совместимости с очень старым кодом, который был написан, когда это были единственные строки, которые у нас были.

Поскольку вы, кажется, просто пишете все это, нет причин играть в игры с фиксированными записями. Просто напишите каждое поле в поток, напишите длину строки перед написанием самой строки, чтобы вы могли ее правильно загрузить. Файл будет меньше, и ничто не будет усечено.

Также, как он говорит, используйте tFileStream. Старый формат использует его для файла записей, который остается на диске, и нет причин использовать его в таком случае.

+0

Очень хороший ответ. Написание только такого количества полей, как есть, не только уменьшит размер файла, но и не сработает код, если будет нарушен один из жестко заданных пределов в типе данных. – mghie

+0

Он, кажется, копирует его в String [255], поэтому результат - усечение, а не сбой. –

+0

@Loren: Это не то, что я имею в виду - что происходит в OP-коде, если fItemCount> High (fItems) или if fCategoryCount> High (Категории)? Он потерпит крах. Не обращайте внимания на то, что пределы, кроме 0, 1 и бесконечности, произвольны. Почему только 3500 предметов? Почему максимальная длина комментария 1220? – mghie