简体   繁体   中英

Writing tList<string> to tFileStream

I use Berlin in Windows 10. I try to save tList<string> to a file.

I know how to handle tStringlist, tStreamWriter and tStreamReader but I need to use tFileStream because the other type of data should be added.

In the following code the loop of Button2Click which reads the data raises an eOutOfMemory exception. When I allocate simple string value to _String it works well but if I put tList value to the same _String it seems that wrong data were written on the file. I can't understand the difference between _String := _List.List[i] and _String := 'qwert' .

How can I write tList<string> to 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;

If you lookup in the docs what TFileStream.Write does, it tells you (inherited from THandleStream.Write ):

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

Writes Count bytes from the Buffer to the current position in the resource.

Now, Buffer is untyped and as such is expected to be the memory address of the data to be written. You are passing a string variable which is a reference to the actual string data, the address of the variable holds a pointer to string data. You are therefore writing a pointer to the file.

To correct it pass the strings first character for the buffer, ....write(_string[1], ... If you have compiler directive {$ZEROBASEDSTRINGS ON} you would use index 0. Alternatively, typecast the string to PChar and dereference it: ....write(PChar(_String)^, ...

Then look at the second parameter, Count . As the docs say, it indicates the number of bytes to be written, specifically not characters. In Delphi 2009 and later strings are UnicodeString , so each character is 2 bytes. You need to pass the strings size in bytes.

This will write 4 characters (8 bytes) to the file stream:

_FileStream.Write(_String[1], 4 * SizeOf(Char));

or better

_FileStream.Write(PChar(_String)^, 4 * SizeOf(Char));

For reading you need to make corresponding changes, but most notable, you need to set the strings length before reading (length is counted in characters).

  SetLength(_String, 4);
  for i := 0 to 1 do
  begin
    _FileStream.Read(_String[1], 4 * SizeOf(Char));

    Memo1.Lines.Add(_String);
  end;

To continue with this low-level approach you could generalize string writing and reading as follows: Add a variable to hold the length of a string

var
  _String: string;
  _Length: integer;

then writing

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;

and reading

begin
  ...
  for ....
  begin
    _FileStream.Read(_Length, SizeOf(Integer));
    SetLength(_String, _Length);
    _FileStream.Read(_String[1], _Length * SizeOf(Char));
    Memo1.Lines.Add(_String);
  end;

IOW, you write the length first and then the string. On reading you read the length and then the string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM