我有一个处理类似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。
答案 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