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

ZzZ*_*mbo -1 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;
Run Code Online (Sandbox Code Playgroud)

问题是循环从不退出.调试显示该行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;
Run Code Online (Sandbox Code Playgroud)

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

Rem*_*eau 5

你的循环永远运行,因为P永远不会nil开始,而不是因为你的指针数学问题(虽然我将在下面进一步讨论). PChar()始终返回非nil指针.如果S不为空,PChar()返回一个指向第一Char,但如果S为空,那么PChar()在返回一个指向空终止const记忆.您的代码不能解释后一种可能性.

如果你想处理S一个空值终止的C字符串(为什么不采取充分Length()S考虑呢?),那么你需要使用while P^ <> #0 do的替代while P <> nil do.

除此之外:

  • P应该声明为PChar而不是MarshaledString.没有理由MarshaledString在这种情况下或以这种方式使用.

  • 这将是更有效地使用TStringBuilder.Append(Char)在你传递一个单一的情况下Char,以DoIt().事实上,我建议DoIt()完全摆脱它,因为它并没有真正获得任何有用的东西.

  • 你为什么把它'\'#0作为换行符?要考虑\输入字符串末尾的字符?如果您遇到这种情况,那么您正在递增P超过null终止符,然后您处于未定义的区域,因为您正在读取周围的内存.或者您的输入字符串是否真的具有嵌入#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;
Run Code Online (Sandbox Code Playgroud)

或者这个(如果没有嵌入的#0字符):

class function UTIL.ProcessString(const S: string): string;
var
  SB: TStringBuilder;
  P: PChar;
  Ch: Char;
begin
  Result := '';
  P := PChar(S);
  if P^ = #0 then Exit;
  SB := TStringBuilder.Create;
  try
    repeat
      Ch := P^;
      Inc(P);
      if Ch <> '\' then
        SB.Append(Ch)
      else
      begin
        Ch := P^;
        if Ch = #0 then
        begin
          // up to you if you really need this or not:
          // SB.Append(sLineBreak);
          Break;
        end;
        Inc(P);
        case Ch of
          '\','"': SB.Append(Ch);
          'n':     SB.Append(sLineBreak);
          't':     SB.Append(#9);
          else     SB.Append('\'+Ch);
        end;
      end;
    until P^ = #0;
    Result := SB.ToString;
  finally
    SB.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)