我需要浏览HTML字符串并将字符替换为0(零),标记,空格和换行符除外。我在下面创建了这个代码,但它太慢了。请有人帮助我加快速度(优化)吗?
procedure TForm1.btn1Click(Sender: TObject);
var
Txt: String;
Idx: Integer;
Tag: Boolean;
begin
Tag := False;
Txt := mem1.Text;
For Idx := 0 to Length(Txt) - 1 Do
Begin
If (Txt[Idx] = '<') Then
Tag := True Else
If (Txt[Idx] = '>') Then
Begin
Tag := False;
Continue;
end;
If Tag Then Continue;
If (not (Txt[Idx] in [#10, #13, #32])) Then
Txt[Idx] := '0';
end;
mem2.Text := Txt;
end;
HTML文本永远不会有“&lt;”或“&gt;”外部标签(在文本中间),所以我不需要担心这个。
谢谢!
答案 0 :(得分:7)
这看起来非常简单。如果没有根据您正在使用的数据分析代码,很难确定(这总是一个好主意;如果您需要优化Delphi代码,请先尝试通过Sampling Profiler运行它以了解您的位置'实际上花了你所有的时间,但如果我必须做出有根据的猜测,我猜你的瓶颈在这一行:
Txt[Idx] := '0';
作为编译器保证string
类型的安全写时复制语义的一部分,对字符串的单个元素(字符)的每次写入都涉及对UniqueString
例程的隐藏调用。这可以确保您不会更改其他地方的字符串,该字符串包含对。
在这种特殊情况下,这不是必需的,因为你在这个例程的开头就得到了新的字符串,你知道它是独一无二的。如果你小心的话,有办法解决它。
明确无误警告:如果不确定首先是否有唯一字符串,请不要执行我要解释的内容!最简单的方法是手动调用UniqueString
。此外,在循环期间不要做任何可以将此字符串分配给任何其他变量的操作。虽然我们这样做,但它并没有被视为正常的字符串。 未注意此警告可能会导致数据损坏。
好了,既然已经解释过,你可以使用指针直接访问字符串的字符,并绕过编译器的安全措施,如下所示:
procedure TForm1.btn1Click(Sender: TObject);
var
Txt: String;
Idx: Integer;
Tag: Boolean;
current: PChar; //pointer to a character
begin
Tag := False;
Txt := mem1.Text;
UniqueString(txt); //very important
if length(txt) = 0 then
Exit; //If you don't check this, the next line will raise an AV on a blank string
current := @txt[1];
dec(current); //you need to start before element 1, but the compiler won't let you
//assign to element 0
For Idx := 0 to Length(Txt) - 1 Do
Begin
inc(current); //put this at the top of the loop, to handle Continue cases correctly
If (current^ = '<') Then
Tag := True Else
If (current^ = '>') Then
Begin
Tag := False;
Continue;
end;
If Tag Then Continue;
If (not (current^ in [#10, #13, #32])) Then
current^ := '0';
end;
mem2.Text := Txt;
end;
这改变了这个比喻。我们不是将字符串作为数组索引,而是将其视为磁带,指针为头部,一次向前移动一个字符,从头到尾扫描,并在适当时更改其下的字符。没有冗余调用UniqueString
,也没有重复计算偏移量,这意味着这可以快得多。
使用这样的指针时要非常小心。编译器的安全检查有充分的理由,并使用指针步骤。但有时,它们确实可以帮助您加快代码速度。再试一次,在尝试这样的事情之前。确保你知道什么在减慢事情,而不是只是想你知道。如果结果是其他运行缓慢的东西,不要这样做;找到真正问题的解决方案。
答案 1 :(得分:2)
编辑:看起来我错了 - UniqueString
不是问题。实际的瓶颈似乎是按字符访问字符串。鉴于我的答案是无关紧要的,我已经完全取代了它。
如果你使用PChar
来避免重新计算字符串偏移量,同时仍然通过Txt[Idx]
更新字符串,那么该方法要快得多(在我的1000次运行测试中,下降到0.5秒)
这是我的版本:
procedure TForm1.btn1Click(Sender: TObject);
var
Idx: Integer;
Tag: Boolean;
p : PChar;
Txt : string;
begin
Tag := False;
Txt := Mem1.Text;
p := PChar(txt);
Dec(p);
For Idx := 0 to Length(Txt) - 1 Do
Begin
Inc(p);
If (not Tag and (p^ = '<')) Then begin
Tag := True;
Continue;
end
Else If (Tag and (p^ = '>')) Then
Begin
Tag := False;
Continue;
end;
If Tag Then Continue;
If (not (p^ in [#10, #13, #32])) Then begin
Txt[Idx] := '0';
end;
end;
mem2.Text := Txt;
end;
答案 2 :(得分:1)
我做了一些分析并想出了这个解决方案。
> #32
而不是[#10,#13,#32]
的测试获得了一定的速度(感谢@DavidHeffernan)。PChar
的帮助下访问字符串更有效。procedure TransformHTML( var Txt : String);
var
IterCnt : Integer;
PTxt : PChar;
tag : Boolean;
begin
PTxt := PChar(Txt);
Dec(PTxt);
tag := false;
for IterCnt := 0 to Length(Txt)-1 do
begin
Inc(PTxt);
if (PTxt^ = '<') then
tag := true
else
if (PTxt^ = '>') then
tag := false
else
if (not tag) and (PTxt^ > #32) then
PTxt^ := '0';
end;
end;
这种解决方案比Mason的解决方案效率高30%,比Blorgbeard的效率高2.5倍。