我正在运行Delphi RAD Studio XE2。
我有一些非常大的文件,每个文件都包含大量的行。线条本身很小 - 只有3个标签分隔的双打。我想使用TStringList
将文件加载到TStringList.LoadFromFile
,但这会引发大文件异常。
对于200万行(约1GB)的文件,我得到EIntOverflow
例外。对于较大的文件(例如,2000万行和大约10GB),我得到ERangeCheck
异常。
我有32GB的RAM可供使用,我只是想加载这个文件并快速使用它。这里发生了什么,我有什么其他选择?我可以使用带有大缓冲区的文件流将此文件加载到TStringList中吗?如果是这样,请提供一个例子。
答案 0 :(得分:14)
当Delphi在Delphi 2009中切换到Unicode时,TStrings.LoadFromStream()
方法(内部TStrings.LoadFromFile()
调用)对于大型流/文件来说效率非常。
在内部,LoadFromStream()
将整个文件作为TBytes
读入内存,然后使用UnicodeString
将其转换为TEncoding.GetString()
(进行解码)将字节转换为TCharArray
,将其复制到最终UnicodeString
中,然后释放数组),然后解析UnicodeString
(TBytes
仍然在内存中)添加根据需要将子字符串放入列表中。
因此,就在LoadFromStream()
退出之前,内存中有四个文件数据副本 - 三个副本占用更差的filesize * 3
个字节的内存(其中每个copy使用自己的连续内存块+一些MemoryMgr开销,并为解析的子串提供一个副本!当然,当LoadFromStream()
实际退出时,前三个副本被释放。但这解释了为什么在到达那个点之前你会遇到内存错误 - LoadFromStream()
试图使用3-4 GB内存来加载1GB文件,而RTL的内存管理器无法处理。
如果您想将大文件的内容加载到TStringList
,最好使用TStreamReader
代替LoadFromFile()
。 TStreamReader
使用缓冲文件I / O方法以小块读取文件。只需在循环中调用其ReadLine()
方法,Add()
将每一行调用到TStringList
。例如:
//MyStringList.LoadFromFile(filename);
Reader := TStreamReader.Create(filename, true);
try
MyStringList.BeginUpdate;
try
MyStringList.Clear;
while not Reader.EndOfStream do
MyStringList.Add(Reader.ReadLine);
finally
MyStringList.EndUpdate;
end;
finally
Reader.Free;
end;
也许有一天,可能会重写LoadFromStream()
以在内部使用TStreamReader
。