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.
你的循环永远运行,因为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)