2017-10-28 90 views
4

我在TMemo中有100000行。我想做类似的事情:TMemo在处理大量行时非常缓慢

for i:= 0 to Memo.Lines.Count-1 do 
    Memo.Lines[i]:= SomeTrim(Memo.Lines[i]); 

但速度是每秒0.5行!!

添加BeginUpdate/EndUpdate后,我没有看到任何速度改进。

Memo.Lines.BeginUpdate; 
for i:= 0 to Memo.Lines.Count-1 do 
    Memo.Lines[i]:= SomeTrim(Memo.Lines[i]); 
Memo.Lines.EndUpdate; 

我的问题是为什么BeginUpdate/EndUpdate不会帮助?

+3

不好的用户会滚动这样的备忘录。 – Victoria

+0

PS:目前的解决方案是将行分配给TStringList,处理它们并将它们放回备忘录。但我仍然好奇为什么BeginUpdate不起作用。 – Ampere

+0

@维多利亚 - 用户将把那些线放在那里。通常情况下,我预计在100行以下。我想测试看看100000会发生什么情况。这就是发生了什么事情。 – Ampere

回答

10

TStrings.BeginUpdate/EndUpdate只会禁止OnChangingOnChanged事件。它对内容处理本身的变化没有影响。

TMemo.LinesTMemoStrings实现,它将文本内容存储在Window控件本身中。因此BeginUpdate/EndUpdate在这里很没用。

您可以通过使用本地TStringList实例,并使用Text属性将数据复制从TMemoTStringList和背部得到更好的结果。 Text属性是一次访问TMemo的全部内容的最有效方式。

lst := TStringList.Create; 
    try 
    lst.Text := Memo1.Lines.Text; 
    for I := 0 to lst.Count - 1 do begin 
     lst[I] := SomeTrim(lst[I]); 
    end; 
    Memo1.Lines.Text := lst.Text; 
    finally 
    lst.Free; 
    end; 

注:一些评论提到复制含量和备忘录时使用Assign代替Text属性:Assign在这种情况下显著慢,由于Text财产为TMemoLines的内部优化。该属性的Getter和Setter直接使用单个WM_GETTEXT/WM_SETTEXT消息访问Windows控件,而Assign每行使用一个EM_GETLINE消息进行读取,每行使用EM_LINEINDEX,EM_SETSEL,EM_LINELENGTH和EM_REPLACESEL序列进行写入。简单的时间测试表明上述代码需要大约600毫秒,而用Assign代替Text分配需要超过11秒!

+5

使用'Assign()'方法代替'Text'属性:'lst.Assign(Memo1.Lines); ... Memo1.Lines.Assign(lst);'由于这些行已经分开,只是按原样复制它们,不要连接它们只是为了重新解析它们,这是浪费内存和处理 –

+0

@RemyLebeau ,单独获取和设置每行代码实际上就是原始代码所做的。 –

+9

我知道这一点。我指的是将备忘录内容复制到StringList然后回来。你的回答说'Text'属性是“最有效的”方法,但这甚至不是真的,特别是对于100000行。 'Text' getter执行2遍扫描来分配和复制内存,然后'Text' setter解析输入以执行大量的分配。另一方面,'Assign()'执行少得多的分配并使用1遍扫描。自己分析它,'Assign()'比'Text'更有效 –