Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
替换HTML字符串中的字符-标记除外_Html_String_Delphi_Replace_Char - Fatal编程技术网

替换HTML字符串中的字符-标记除外

替换HTML字符串中的字符-标记除外,html,string,delphi,replace,char,Html,String,Delphi,Replace,Char,我需要遍历一个HTML字符串并用0(零)替换字符,除了标记、空格和换行符。我在下面创建了这个代码,但是它太慢了。请,有人能帮我使它更快(优化) 程序TForm1.btn1单击(发送方:TObject); 变量 Txt:字符串; Idx:整数; 标签:布尔; 开始 标签:=假; Txt:=mem1.Text; 对于Idx:=0到长度(Txt)-1 Do 开始 如果(Txt[Idx]=''),则 开始 标签:=假; 继续; 结束; 如果是,则继续; 如果(不是[10、[13、[32]中的Txt[Id

我需要遍历一个HTML字符串并用0(零)替换字符,除了标记、空格和换行符。我在下面创建了这个代码,但是它太慢了。请,有人能帮我使它更快(优化)

程序TForm1.btn1单击(发送方:TObject);
变量
Txt:字符串;
Idx:整数;
标签:布尔;
开始
标签:=假;
Txt:=mem1.Text;
对于Idx:=0到长度(Txt)-1 Do
开始
如果(Txt[Idx]=''),则
开始
标签:=假;
继续;
结束;
如果是,则继续;
如果(不是[10、[13、[32]中的Txt[Idx]),那么
Txt[Idx]:=“0”;
结束;
mem2.Text:=Txt;
结束;
HTML文本永远不会有“”外部标记(在文本中间),因此我不需要担心这一点


谢谢大家!

编辑:看起来我错了-
唯一字符串
不是问题所在。实际的瓶颈似乎是按字符访问字符串。考虑到我的整个回答都是无关紧要的,我已经完全替换了它

如果使用
PChar
来避免重新计算字符串偏移量,同时仍然通过
Txt[Idx]
更新字符串,则该方法的速度要快得多(在我的1000次运行测试中,从5秒下降到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;
程序TForm1.btn1单击(发送方:TObject);
变量
Idx:整数;
标签:布尔;
p:PChar;
Txt:字符串;
开始
标签:=假;
Txt:=Mem1.Text;
p:=PChar(txt);
Dec(p);
对于Idx:=0到长度(Txt)-1 Do
开始
公司(p),;
如果(非标记和(p^=''),则
开始
标签:=假;
继续;
结束;
如果是,则继续;
如果不是(p^in[#10,#13,#32]),那么开始
Txt[Idx]:=“0”;
结束;
结束;
mem2.Text:=Txt;
结束;

这看起来很简单。如果不根据您使用的数据分析代码,很难确定(这始终是一个好主意;如果您需要优化Delphi代码,请先尝试运行它,以了解您实际花费的所有时间),但是如果我不得不做出有根据的猜测,我想您的瓶颈在这一行:

Txt[Idx] := '0';
作为编译器对
字符串
类型的安全写时复制语义保证的一部分,对字符串的单个元素(字符)的每次写入都涉及对例程的隐藏调用。这可以确保您没有更改某个字符串,而该字符串是其他地方保存引用的字符串

在这个特殊的例子中,这是没有必要的,因为在这个例程的开始,您得到了新的字符串,并且您知道它是唯一的。如果你小心的话,这是有办法的

明确无误的警告:在不确保您拥有唯一字符串的情况下,不要执行我将要解释的操作最简单的方法是手动调用
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;
程序TForm1.btn1单击(发送方:TObject);
变量
Txt:字符串;
Idx:整数;
标签:布尔;
电流:PChar//指向字符的指针
开始
标签:=假;
Txt:=mem1.Text;
唯一字符串(txt)//非常重要
如果长度(txt)=0,则
退出//如果不选中此项,下一行将在空白字符串上引发AV
当前:=@txt[1];
12月(当前)//您需要在元素1之前启动,但编译器不允许
//分配给元素0
对于Idx:=0到长度(Txt)-1 Do
开始
公司(现任)//将其放在循环的顶部,以正确处理连续案例
如果(当前^=“”),则
开始
标签:=假;
继续;
结束;
如果是,则继续;
如果(不是(当前的[#10,#13,#32]),那么
当前^:=“0”;
结束;
mem2.Text:=Txt;
结束;
这改变了隐喻。我们不是将字符串作为数组进行索引,而是将其视为磁带,以指针为头,一次向前移动一个字符,从头到尾扫描,并在适当时更改其下的字符。没有对
UniqueString
的冗余调用,也没有重复计算偏移量,这意味着这可以快得多


使用这样的指针时要非常小心。编译器的安全检查有一个很好的理由,并且在它们之外的步骤中使用指针。但有时,它们确实可以帮助加快代码中的速度。再次强调,在尝试类似的操作之前,请先进行简要介绍。确保你知道是什么让事情变得缓慢,而不是仅仅认为你知道。如果结果是其他东西运行缓慢,不要这样做;找到真正问题的解决方案。

我做了一些分析,提出了这个解决方案

  • 测试
    \32
    而不是
    [10,13,32]
    会提高一些速度(谢谢@DavidHeffernan)
  • 一个更好的循环逻辑也会提供一点额外的速度
  • PChar
    的帮助下以独占方式访问字符串更有效

procedure-TransformHTML(var-Txt:String);
变量
IterCnt:整数;
PTxt:PChar;
标签:布尔;
开始
PTxt:=PChar(Txt);
Dec(PTxt);
标签:=假;
对于IterCnt:=0到长度(Txt)-1 do
开始
公司(PTxt),;
如果(PTxt^=''),则
标记:=假
其他的
如果(非标记)和(PTxt^>#32),则
PTxt^:=“0”;
结束;
结束;
此解决方案的效率比Mason的解决方案高30%左右,比Blorgbeard的解决方案高2.5倍。

“写时复制语义”并不意味着每次更改某个内容时,整个内容都会被复制;这意味着外部行为将
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;
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;