将tList <string>写入tFileStream

时间:2016-08-27 09:48:21

标签: delphi tlist tfilestream

我在Windows 10中使用Berlin。我尝试将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 个答案:

答案 0 :(得分:2)

如果您在文档中查找TFileStream.Write的内容,它会告诉您(继承自THandleStream.Write):

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

将Buffer中的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,先写下长度,再写下字符串。在阅读时,你读取长度,然后读取字符串。