2016-08-27 4 views
1

Я использую Berlin в Windows 10. Я пытаюсь сохранить файл tList<string> в файл.Запись tList <string> в tFileStream

Я знаю, как обрабатывать 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; 
+0

Вы пробовали сериализовать его? –

+0

@ IgnacioVazquez-Abrams Нет, я этого не делал. Мне жаль, что я никогда не слышал об этом. –

+0

Почему вы не используете [TStringList] (http://docwiki.embarcadero.com/Libraries/en/System.Classes.TStringList), у него есть методы SaveToFile и LoadFromFile? – whosrdaddy

ответ

2

Если вы LookUp в документации, что TFileStream.Write делает, он говорит вам (унаследованный от THandleStream.Write):

function Write(const Buffer; Count: Longint): Longint; override; 
function Write(const Buffer: TBytes; Offset, Count: Longint): Longint; override; 

Пишет Count байт из буфера в текущую позицию в ресурса.

В настоящее время 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; 

IOW, вы записываете длину сначала, а затем строку. При чтении вы читаете длину, а затем строку.

+0

Запись _List.Add ('abcde'); _String: = _List.List [0]; _FileStream.Write (_string [1], 5); и чтение SetLength (_String, 5); _FileStream.Read (_String [1], 5); работает хорошо. Ваш комментарий был настолько полезен, что я могу решить проблему. Запись и чтение должны иметь размер каждого элемента tList, и это раздражает. Лучше использовать ShortString статической строки. Большое спасибо. –

+0

@JOSeongGng См. Мое обновление. –

+0

В случае строки [1] длина должна быть длиной * 2. Ваш комментарий о unicode был очень полезным. Большое спасибо. –