使用指针数学解析字符串的无限循环

时间:2015-06-10 04:05:44

标签: delphi pointer-arithmetic delphi-xe8

我有一个处理类似C的字符串的例程,导致通常的Delphi字符串:

class function UTIL.ProcessString(const S: string): string;
var
  SB:TStringBuilder;
  P:MarshaledString;
  procedure DoIt(const S:string;const I:Integer=2);
  begin
  SB.Append(S);
  Inc(P,I);
  end;
begin
SB:=TStringBuilder.Create;
P:=PChar(S);
while P<>nil do
  begin
  if P^<>'\' then DoIt(P^,1) else
    case (P+1)^ of
    '\','"':DoIt((P+1)^);
    #0,'n':DoIt(sLineBreak);
    't':DoIt(#9);
    else DoIt('\'+(P+1)^,2);
    end;
  end;
Result:=SB.ToString;
SB.Free;
end;

问题是循环永不退出。调试显示行while P<>nil do未评估为False,因为在处理结束时P为'',因此代码尝试对其执行超范围操作。由于我没有在Delphi中找到关于指针数学的任何简明文档,所以我很可能在这里有错。

编辑:我已经重写了这个功能,所有内容都是这样读到的:

class function UTIL.ProcessString(const S: string): string;
var
  SB:TStringBuilder;
  P:PChar;
  C:Char;
begin
SB:=TStringBuilder.Create;
P:=PChar(S);
  repeat
  C:=P^;
  Inc(P);
    case C of
    #0:;
    '\':
      begin
      C:=P^;
      Inc(P);
        case C of
        #0,'n':SB.Append(sLineBreak);
        '\','"':SB.Append(C);
        't':SB.Append(#9);
        else SB.Append('\').Append(C);
        end;
      end;
    else SB.Append(C);
    end;
  until P^=#0;
Result:=SB.ToString;
SB.Free;
end;

我在内部案例陈述中检查#0是否"such \ strings"被送入例程,i。即从源中读取的一系列字符串,然后逐个格式化。到目前为止,这很好用,但是它无法正确解析'\\t''\t'和类似的结构,它只返回#9。我无法想到任何原因。哦,老版本也有这个错误BTW。

1 个答案:

答案 0 :(得分:5)

你的循环永远运行,因为P永远不会nil开始,而不是因为你的指针数学问题(尽管我将在下面进一步讨论)。 PChar()始终返回非nil指针。如果S不为空,PChar()会返回指向第一个Char的指针,但如果S为空,则PChar()返回指向const内存中的空终止符。你的代码不能解释后一种可能性。

如果您要将S作为以空字符结尾的C字符串进行处理(为什么不考虑Length()的完整S?),那么您需要使用{{ 1}}而不是while P^ <> #0 do

除此之外:

  • while P <> nil do应声明为P而不是PChar。在这种情况下,或以这种方式,没有理由使用MarshaledString

  • 在将MarshaledString传递给TStringBuilder.Append(Char)的情况下,使用Char效率更高。事实上,我建议完全摆脱DoIt(),因为它并没有真正为你带来任何有用的东西。

  • 为什么将DoIt()视为换行符?要在输入字符串的末尾考虑'\'#0个字符?如果遇到这种情况,您将通过空终止符递增\,然后由于您正在读取周围的内存,因此您处于未定义的区域。或者您的输入字符串是否真的具有嵌入的P字符,然后是最终的空终止符?这对于文本数据来说是不寻常的格式。

尝试更类似的内容(如果确实存在嵌入的#0字符):

#0

或者这个(如果没有嵌入的class function UTIL.ProcessString(const S: string): string; var SB: TStringBuilder; P: PChar; begin Result := ''; P := PChar(S); if P^ = #0 then Exit; SB := TStringBuilder.Create; try repeat if P^ <> '\' then begin SB.Append(P^); Inc(P); end else begin Inc(P); case P^ of '\','"': SB.Append(P^); #0, 'n': SB.Append(sLineBreak); 't': SB.Append(#9); else SB.Append('\'+P^); end; Inc(P); end; until P^ = #0; Result := SB.ToString; finally SB.Free; end; end; 个字符):

#0