我正在使用 Embarcadero 的 Rad Studio Delphi (10.2.3),并且在读取非常大的文本文件(700 万行以上,每行都不同,行长度可以是 1 到 ~200 个字符等)时遇到内存问题。我对 Delphi 编程相当陌生,所以在发帖之前我已经搜索过 SO 和 Google 寻求帮助。
我最初实现了一个 TStringList 并使用 LoadFromFile 方法读取文件,但是当处理的文本文件变得足够大时,这会失败。然后,我实现了一个 TStreamReader 并使用 ReadLn 使用此处找到的基本代码填充 TStringList:
TStringList.LoadFromFile - 大文本文件的异常
代码示例:
//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;
Run Code Online (Sandbox Code Playgroud)
在我需要处理的文件变得巨大(约 700 万行以上)之前,这种方法一直很有效。看起来 TStringList 变得太大以至于内存不足。我说“出现”是因为我实际上无权访问正在运行的文件,并且所有错误信息都是由我的客户通过电子邮件提供的,这使得这个问题变得更加困难,因为我无法简单地在 IDE 中调试它。
该代码是32位编译的,我无法使用64位编译器。我也不能包括数据库系统之类的。不幸的是,我有一些严格的限制。我需要加载每一行以查找模式并将这些行与其他行进行比较以查找“模式中的模式”。我很抱歉在这里说得很含糊。
底线是——有没有一种方法可以在不使用 TStringList 的情况下访问文本文件中的每一行,或者也许有更好的方法来处理 TStringList 内存?
也许有一种方法可以将 StreamReader 中的特定行块加载到 TStringList 中(例如,读取前 100,000 行并处理,接下来的 100,000 行等),而不是一次加载所有内容?我想我可以写一些东西来处理可能的“块间”模式。
预先感谢您的任何和所有帮助和建议!
***** 编辑更新 *****
好的,这是我需要实现的基本解决方案:
var …Run Code Online (Sandbox Code Playgroud) 我倾向于使用Delphi的TStringList进行文本操作,所以我写了很多程序/函数,如:
var
TempList: TStringList;
begin
TempList:= TStringList.Create;
try
// blah blah blah do stuff with TempList
finally
TempList.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
切断创建和释放这样一个常见的实用程序类会很好.
因为我们现在有方法的记录,是否可以在记录中包装类似TStringList的类,所以我可以:
var
TempList: TRecordStringList;
begin
// blah blah blah do stuff with TempList
end;
Run Code Online (Sandbox Code Playgroud) 我正在将用Delphi 2007 .Net编写的应用程序迁移到Delphi Prism,这是替换TStringList和TStrings类的最佳选择吗?
提前致谢.
再见.
我目前正在开发一个程序来生成Delphi 2010中的文件哈希.作为其中的一部分,我可以选择创建用户预设,例如用户可以创建/保存/删除的哈希算法的预定义选择.我有创建和加载代码正常工作.它使用ComboBox并从文件"fhpre.ini"加载,在此文件中是用户预设,格式为: -
PresetName
PresetCode(12位数字符串,使用0表示不散列,1表示do)
在应用程序加载时,它会将此文件中的数据加载到ComboBox和一个数组中,其中ComboBox的ItemIndex与数组中0和1的相应正确字符串相匹配.
现在我需要实现一项功能,让用户从列表中删除预设.到目前为止,我的代码如下,
procedure TForm1.Panel23Click(Sender : TObject);
var
fil : textfile;
contents : TStringList;
x,i : integer;
filline : ansistring;
filestream : TFileStream;
begin //Start Procedure
//Load data into StringList
contents := TStringList.Create;
fileStream := TFileStream.Create((GetAppData+'\RFA\fhpre.ini'), fmShareDenyNone);
Contents.LoadFromStream(fileStream);
fileStream.Destroy();
//Search for relevant Preset
i := 0;
if ComboBox4.Text <> Contents[i] then
begin
Repeat
i := i + 1;
Until ComboBox4.Text = Contents[i];
end;
contents.Delete(i); //Delete Relevant Preset Name
contents.Delete(i); //Delete Preset Digit String
//Write StringList back …Run Code Online (Sandbox Code Playgroud) 使用的版本: Delphi 7.
我正在开发一个在Virtual ListView上执行简单for循环的程序.数据存储在以下记录中:
type TList=record
Item:Integer;
SubItem1:String;
SubItem2:String;
end;
Run Code Online (Sandbox Code Playgroud)
项目是索引.SubItem1操作的状态(成功与否).SubItem2文件的路径.在对循环加载每个文件,做一些操作,然后保存.操作发生在TStringList中.文件大约每个2mb.
现在,如果我在主窗体上执行操作,它可以完美地工作.
多线程,存在巨大的内存问题.不知何故,TStringList似乎没有被完全释放.在3-4k文件之后,我得到一个EOutofMemory异常.有时候,软件会停留在500-600mb,有时候不会.在任何情况下,TStringList始终返回EOutofMemory异常,并且不再可以加载任何文件.在具有更多内存的计算机上,获取异常需要更长时间.
其他组件也会发生同样的事情.例如,如果我使用Synapse的THTTPSend,过了一段时间,软件无法创建任何新线程,因为内存消耗太高.它大概是500-600mb,而它应该是,最大,100mb.在主窗体上,一切正常.
我想这个错误就在我身边.也许我不太了解线程.我试图释放Destroy事件中的所有内容.我尝试过FreeAndNil程序.我一次只尝试一个线程.我尝试手动释放线程(没有FreeOnTerminate ......)
没运气.
所以这是线程代码.这只是基本的想法; 不是所有操作的完整代码.如果我删除LoadFile prodecure,一切都很好.根据线程池为每个文件创建一个线程.
unit OperationsFiles;
interface
uses Classes, SysUtils, Windows;
type
TOperationFile = class(TThread)
private
Position : Integer;
TPath, StatusMessage: String;
FileStringList: TStringList;
procedure UpdateStatus;
procedure LoadFile;
protected
procedure Execute; override;
public
constructor Create(Path: String; LNumber: Integer);
end;
implementation
uses Form1;
procedure TOperationFile.LoadFile;
begin
try …Run Code Online (Sandbox Code Playgroud) 在我的应用程序中,当我想导入文件时,我使用TStringList.
但是,当有人从Excel导出数据时,文件编码是UCS-2 Little Endian,而TStringList无法读取数据.
有什么方法可以验证这种情况,识别文本编码并向用户发送提示文本不兼容的警告?
为了清楚起见,用户将只提供纯文本..通讯和数字,否则,我必须发送警告.
没有BOM的Unicode文件很好.(TStringList可以读取它!)
ANSI文件也是.(TStringList可以读取它!)
如果有一种方法可以删除它,即使带有BOM的Unicode也会很好.(TStringList可以读取它!,但是带有"i"">>"和"reverse?"字符,属于BOM字节)
我有一个字符串列表和它们要替换的值.我正在尝试将它们组合在一个列表中,'O'='0',' .'='.', ...因此我很容易编辑它并添加更多对替换.
现在我能想到的最好方法是:
var
ListaLimpeza : TStringList;
begin
ListaLimpeza := TStringList.Create;
ListaLimpeza.Delimiter := '|';
ListaLimpeza.QuoteChar := '"';
ListaLimpeza.DelimitedText := 'O=0 | " .=."';
ShowMessage('1o Valor = '+ListaLimpeza.Names[1]+' e 2o Valor = '+ListaLimpeza.ValueFromIndex[1]);
Run Code Online (Sandbox Code Playgroud)
这是有效的,但它对于视觉效果并不好,因为我不能像之前的那样对字符串(对于ex ' .')进行编码(对于SPACE字符来说是非常直观的),只有像(" .)这样=才能分配名称和值在TStringList中.
我正在做一些代码生成工作,我需要做的一件事是创建一个函数调用,其中一个参数是函数调用,如下所示:
result := Func1(x, y, Func2(a, b, c));
Run Code Online (Sandbox Code Playgroud)
TStringList.CommaText 对于生成参数列表非常有用,但是当我遍历树以构建外部函数调用时,我最终得到的结果如下:
result := Func1(x, y, "Func2(a, b, c)");
Run Code Online (Sandbox Code Playgroud)
它引用了第三个参数,因为它包含逗号,并且产生了无效代码。但是我不能做一些像 StringReplace 所有双引号为空字符串这样简单的事情,因为函数参数很可能是一个带有双引号的字符串。有没有办法让它不转义包含逗号的行?
是否有可能在记录中有一个字符串列表?例如
TImportStats = record
ATotal:Integer;
BTotal:String;
AList:TStringist;
end;
Run Code Online (Sandbox Code Playgroud)
如果我认为我需要在使用记录之前创建它?
我在私有部分声明了TStringList的变量.在按钮单击事件中,我想访问该TStringList对象.
sVariable:= TStringList.Create;
sVariable.add('Test1');
Run Code Online (Sandbox Code Playgroud)
现在每当我点击该按钮时,每次将其新创建的内存分配给该变量.是否有任何属性/函数可用于确定是否为该变量创建了对象,并且它也不会给出访问冲突错误?