2009-09-24 2 views
0

У меня есть запись, которая выглядит примерно так:сохранения записей, содержащих элемент типа строки в файл (Delphi, Windows)

type 
    TNote = record 
    Title : string; 
    Note : string; 
    Index : integer; 
    end; 

Simple. Причина, по которой я решил установить переменные как строку (в отличие от массива символов), заключается в том, что я понятия не имею, как долго эти строки будут. Они могут быть 1 char long, 200 или 2000. Конечно, когда я пытаюсь сохранить запись в файл типа (файл ...), компилятор жалуется, что мне нужно указать размер строки. Есть ли способ преодолеть это? или способ сохранить эти записи в нетипизированном файле и сохранить способ поиска?

Пожалуйста, не указывайте мне Возможные решения, если вы знаете решение, пожалуйста, отправьте код. Спасибо

+0

К сожалению моего XML ответ был немного густым; Я нахожусь на пути к двум конференциям (BASTA! И DelphiLive!). В основном то, что вам нужно сделать, очень просто: создайте образец XML-файла, затем запустите мастер привязки данных XML Delphi (доступный в Delphi с версии 6), который будет генерировать единицу для вас, у которой есть интерфейсы и классы, сопоставляющие XML к объектам Delphi и нескольким вспомогательным функциям для чтения их из файла, создания нового объекта и т. д. Моя сессия (см. ссылку в моем ответе) фактически содержит большую часть деталей этого процесса. –

+0

Просьба также показать изменение в моем ответе: вы также можете хранить свои данные в файлах DFM. –

+0

Назад онлайн :-) Пожалуйста, убедитесь, что вы загружаете образцы и слайды: эти ARE читаемы: третья ссылка в разделе «Практический XML с Delphi» на этой странице: http://wiert.wordpress.com/2009/09/ 13/coderage-4-session-are-replays-online-too/ –

ответ

3

Вы не можете сделать это с помощью типизированного файла. Попробуйте что-то вроде этого с помощью TFileStream:

type 
    TStreamEx = class helper for TStream 
    public 
     procedure writeString(const data: string); 
     function readString: string; 
     procedure writeInt(data: integer); 
     function readInt: integer; 
    end; 

function TStreamEx.readString: string; 
var 
    len: integer; 
    iString: UTF8String; 
begin 
    self.readBuffer(len, 4); 
    if len > 0 then 
    begin 
     setLength(iString, len); 
     self.ReadBuffer(iString[1], len); 
     result := string(iString); 
    end; 
end; 

procedure TStreamEx.writeString(const data: string); 
var 
    len: cardinal; 
    oString: UTF8String; 
begin 
    oString := UTF8String(data); 
    len := length(oString); 
    self.WriteBuffer(len, 4); 
    if len > 0 then 
     self.WriteBuffer(oString[1], len); 
end; 

function TStreamEx.readInt: integer; 
begin 
    self.readBuffer(result, 4); 
end; 

procedure TStreamEx.writeInt(data: integer); 
begin 
    self.WriteBuffer(data, 4); 
end; 

type 
    TNote = record 
    Title : string; 
    Note : string; 
    Index : integer; 
    procedure Save(stream: TStream); 
    end; 

procedure TNote.Save(stream: TStream); 
var 
    temp: TMemoryStream; 
begin 
    temp := TMemoryStream.Create; 
    try 
     temp.writeString(Title); 
     temp.writeString(Note); 
     temp.writeInt(Index); 
     temp.seek(0, soFromBeginning); 
     stream.writeInt(temp.size); 
     stream.copyFrom(temp, temp.size); 
    finally 
     temp.Free; 
    end; 
end; 

Я оставлю процедуру загрузки вам. Та же основная идея, но ей не нужен поток temp. С размером записи перед каждой записью вы можете прочитать ее и узнать, как далеко пропустить, если вы ищете определенную запись #, вместо того, чтобы читать все это.

EDIT: Это было написано специально для версий Delphi, которые используют строки Unicode. В более старых версиях вы можете немного упростить его.

+0

спасибо. Посмотрим, смогу ли я работать над этим. Мне нужно изменить мой код. – wonderer

+0

благодарю вас за то, что вы так прямолинейны. Код указал мне на правильное решение. – wonderer

2

Почему бы не написать это как XML? См. Мою сессию «Practical XML with Delphi» о том, как начать работу с этим.

Другой возможностью было бы сделать ваши записи в классы по убыванию формы TComponent и сохранить/восстановить ваши данные в файлах DFM.

This Stackoverflow entry показывает, как это сделать.

--jeroen

PS: Извините мой XML ответ был немного густая; Я нахожусь на пути к двум конференциям (BASTA! и DelphiLive! Germany).

В основном то, что вам нужно сделать, очень просто: создайте образец XML-файла, затем запустите Delphi XML Data Binding Wizard (доступно в Delphi с версии 6).

Этот мастер создаст для вас единицу, которая имеет интерфейсы и классы, сопоставляющие XML с объектами Delphi, и несколько вспомогательных функций для их чтения из файла, создания нового объекта и т. Д. Моя сессия (см. Первую ссылку выше) на самом деле содержит большую часть деталей этого процесса.

Вышеупомянутая ссылка - это видео, демонстрирующее использование мастера привязки данных XML Delphi.

+1

спасибо. поэтому теперь мне нужно написать полную функцию синтаксического анализа XML. Для такой простой задачи (сохранения строки в файл) это слишком много работает. если у вас нет (как я попросил) образца кода, который может это сделать. – wonderer

+0

+1 потому что Delphi предоставляет XML-интерфейс (msxmldom, XMLDoc) и IMHO, это проще, чем реализация пользовательского формата. –

+0

, если это так просто, отправьте код. Я не понимаю, почему люди не читают вопрос правильно. – wonderer

0

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

(Извините, нет кода.)

0
TNote = record 
    Title : string; 
    Note : string; 
    Index : integer; 
    end; 

можно перевести как

TNote = record 
    Title : string[255]; 
    Note : string[255]; 
    Index : integer; 
    end; 

и использовать Stream.writebuffer (ANodeVariable, SizeOf (Tnode), но вы сказали, что строки получить идти через 255 символов в этом случае, если строка переходит 65535 символы затем изменить WORD целочисленным

type 
TNodeHeader=Record 
    TitleLen, 
    NoteLen: Word; 
end; 

(* this is for writing a TNode *) 
procedure saveNodetoStream(theNode: TNode; AStream: TStream); 
var 
    header: TNodeHeader; 
    pStr: PChar; 
begin 
    ... 
    (* writing to AStream which should be initialized before this *) 
    Header.TitleLen := Length(theNode.Title); 
    header.NodeLen := Length(theNode.Note); 
    AStream.WriteBuffer(Header, sizeof(TNodeHeader); 
    (* save strings *) 
    PStr := PChar(theNode.Title); 
    AStream.writeBuffer(PStr^, Header.TitleLen); 
    PStr := PChar(theNode.Note); 
    AStream.writebuffer(PStr^, Header.NoteLen); 
    (* save index *) 
    AStream.writebuffer(theNode.Index, sizeof(Integer)); 
end; 
(* this is for reading a TNode *) 
function readNode(AStream: TStream): TNode; 
var 
    header: THeader 
    PStr: PChar; 
begin 
    AStream.ReadBuffer(Header, sizeof(TNodeHeader); 
    SetLength(Result.Title, Header.TitleLen); 
    PStr := PChar(Result.Title); 
    AStream.ReadBuffer(PStr^, Header.TitleLen); 
    SetLength(Result.Note, Header.NoteLen); 
    PStr := PChar(Result.Note); 
    AStream.ReadBuffer(PStr^, Header.NoteLen); 
    AStream.ReadBuffer(REsult.Index, sizeof(Integer)(* 4 bytes *); 
end; 
0

Вы можете использовать функции available in this Open Source unit.

Это позволяет сериализовать любое содержимое записи в двоичный, включая даже динамические массивы в пределах:

type 
    TNote = record 
    Title : string; 
    Note : string; 
    Index : integer; 
    end; 

var 
    aSave: TRawByteString; 
    aNote, aNew: TNote; 
begin 
    // create some content 
    aNote.Title := 'Title'; 
    aNote.Note := 'Note'; 
    aNote.Index := 10; 
    // serialize the content 
    aSave := RecordSave(aNote,TypeInfo(TNote)); 
    // unserialize the content 
    RecordLoad(aNew,pointer(aSave),TypeInfo(TNote)); 
    // check the content 
    assert(aNew.Title = 'Title'); 
    assert(aNew.Note = 'Note'); 
    assert(aNew.Index = 10); 
end;