Запись tList<string> в tFileStream
Я использую Берлин в Windows 10. Я пытаюсь сохранить tList<string>
в файл.
Я знаю, как обрабатывать tStringlist, tStreamWriter и tStreamReader, но мне нужно использовать tFileStream, потому что должны быть добавлены данные другого типа.
В следующем коде цикл Button2Click, который читает данные, вызывает исключение eOutOfMemory. Когда я назначаю простое строковое значение _String, оно работает хорошо, но если я помещаю значение tList в ту же _String, кажется, что в файл записаны неправильные данные. Я не могу понять разницу между _String := _List.List[i]
а также _String := 'qwert'
,
Как я могу написать tList<string>
в tFileSteam?
procedure TForm1.Button1Click(Sender: TObject);
var
_List: TList<string>;
_FileStream: TFileStream;
_String: string;
i: Integer;
begin
_List := TList<string>.Create;
_List.Add('abcde');
_List.Add('abcde12345');
_FileStream := TFileStream.Create('test', fmCreate);
for i := 0 to 1 do
begin
_String := _List.List[i]; // _String := 'qwert' works well
_FileStream.Write(_string, 4);
end;
_FileStream.Free;
_List.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
_FileStream: TFileStream;
_String: string;
i: Integer;
begin
_FileStream := TFileStream.Create('test', fmOpenRead);
for i := 0 to 1 do
begin
_FileStream.Read(_String, 4);
Memo1.Lines.Add(_String);
end;
_FileStream.Free;
end;
1 ответ
Если вы посмотрите в документах, что TFileStream.Write
делает, он говорит вам (наследуется от THandleStream.Write
):
function Write(const Buffer; Count: Longint): Longint; override; function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override;
Записывает количество байтов из буфера в текущую позицию в ресурсе.
Сейчас, Buffer
нетипизирован и, как таковой, должен быть адресом памяти данных для записи. Вы передаете строковую переменную, которая является ссылкой на фактические строковые данные, адрес переменной содержит указатель на строковые данные. Поэтому вы пишете указатель на файл.
Чтобы исправить это, передайте строки первый символ для буфера, ....write(_string[1], ...
Если у вас есть директива компилятора {$ZEROBASEDSTRINGS ON}, вы должны использовать индекс 0. В качестве альтернативы, введите строку в PChar
и разыменовывать это: ....write(PChar(_String)^, ...
Затем посмотрите на второй параметр, Count
, Как говорится в документации, он указывает количество байтов, которые должны быть записаны, а не символы. В Delphi 2009 и более поздних версиях строки UnicodeString
Таким образом, каждый символ составляет 2 байта. Вам нужно передать размер строки в байтах.
Это позволит записать 4 символа (8 байт) в файловый поток:
_FileStream.Write(_String[1], 4 * SizeOf(Char));
или лучше
_FileStream.Write(PChar(_String)^, 4 * SizeOf(Char));
Для чтения вам необходимо внести соответствующие изменения, но, что наиболее важно, вам нужно установить длину строк перед чтением (длина считается в символах).
SetLength(_String, 4);
for i := 0 to 1 do
begin
_FileStream.Read(_String[1], 4 * SizeOf(Char));
Memo1.Lines.Add(_String);
end;
Чтобы продолжить этот низкоуровневый подход, вы можете обобщить запись и чтение строки следующим образом: Добавьте переменную для хранения длины строки
var
_String: string;
_Length: integer;
потом писать
begin
...
for ....
begin
_String := _List.List[i];
_Length := Length(_String);
_FileStream.Write(_Length, SizeOf(Integer));
_FileStream.Write(PChar(_List.List[i])^, _Length * SizeOf(Char));
end;
и чтение
begin
...
for ....
begin
_FileStream.Read(_Length, SizeOf(Integer));
SetLength(_String, _Length);
_FileStream.Read(_String[1], _Length * SizeOf(Char));
Memo1.Lines.Add(_String);
end;
Итак, сначала вы пишете длину, а затем строку. При чтении вы читаете длину, а затем строку.