将 tList<string> 写入 tFileStream
Writing tList<string> to 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;
如果您在文档中查找 TFileStream.Write
的作用,它会告诉您(继承自 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.
现在,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,你先写长度,再写字符串。在阅读时,您先阅读长度,然后阅读字符串。
我在 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;
如果您在文档中查找 TFileStream.Write
的作用,它会告诉您(继承自 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.
现在,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,你先写长度,再写字符串。在阅读时,您先阅读长度,然后阅读字符串。