似乎这里的一切都已经被考虑和尝试过了。但我又遇到了一把旧耙子。 Delphi 11/12,处理大型文本文件。
简单代码:
var slist:TStringList;
slist:=TStringList.Create;
slist.LoadFromFile(ParamStr(1));
导致错误 Exception EIntOverflow in module PrimitiveMotif.exe at 0000000000010DEA,版本 11 中的整数溢出,以及错误 Exception EAbstractError in module PrimitiveMotif.exe at 0000000000044191,抽象错误在版本 12 中。
该文件是文本。是的,巨大(就像我们所有人一样)。但“仅”1.5 GB,FAR 仅显示 1800 万行,这非常符合 TStringList 的限制。计算机上的内存 - 已满 (192 GB)。去哪里挖?
PS 字符串 - ANSI
如果你看看它是如何工作的
TStrings.LoadFromFile
,它会将LoadFromStream
文件的内容加载 () 到一个字节数组中,然后将该数组转换为一个字符串GetString
,然后解析 Unicode 字符串(我可能还没有考虑到所有步骤)。因此,内存中同时存在文件大小的三到四倍,分配了三到四个连续的千兆字节内存块 - 这不是很聪明或聪明,而且内存管理器似乎无法应对。但是,如果您需要在内存中保留这样的 TStringList,您可以从文件中逐行读取到列表中:
像 Readln 这样的旧方法 - 现在不推荐这样做
逐行独立地解析文件(我曾经从内存映射的巨型日志文件中读取,它比 ANSI 行时代的 Readln 快三倍,即使在 32 位时代也根本不可能完全读取这些文件)
使用
TStreamReader.ReadLine
- 这可能是就劳动力成本而言最优化的方法,我还没有在速度方面对其进行测试。再次,使用David Heffernan精心开发的 BufferedFileStream 逐行独立解析文件
PS 但同时需要所有线路?数据库在这里不起作用?
一个小测试。创建一个 4 GB 大小的文件,包含 1600 万行长度为 256 的 Ansi 行(包括 CRLF)。
使用具有 SATA 的 SSD 驱动器(其速度约为 600 MB/s)。
Readln 在没有改进缓冲的情况下运行一分钟,在增加缓冲的情况下仅运行 8 秒。每第四行被检索一次。此时程序占用了2G内存,并没有崩溃。
使用缓冲读取模块和使用 CRLF 的独立解析(更正的 LF 到达缓冲区的开头),它也可以在 6-8 秒内完成。
使用 TStreamReader(尚未在 SSD 上测试)
在 HDD 和较旧的计算机上,时间约为 28、21 和 24 秒。这接近磁盘带宽限制,因此 StreamReader 的行为相当正常。
PS Newfangled
TFile.ReadAllLines
也无法完整读取该文件(读取方式与列表大致相同)。PPS 您还可以使用OmniThreads 作者的GPTextFile,但我认为您不会获得太多好处。原来的问题确实存在(EnSO 上的示例通常与我写的内容一致),但就您而言,目前您可以接受这样的事实:无需下载整个列表。