德尔福TRegEx反向引用破坏了吗?

Pha*_*aoh 10 regex delphi backreference delphi-xe4

我有一个问题TRegEx.replace:

var
  Value, Pattern, Replace: string;
begin
  Value   := 'my_replace_string(4)=my_replace_string(5)';
  Pattern := 'my_replace_string\((\d+)\)';
  Replace := 'new_value(\1)';
  Value   := TRegEx.Replace(Value, Pattern, Replace);
  ShowMessage(Value);
end;
Run Code Online (Sandbox Code Playgroud)

new_value(4)=new_value(5)我的代码(用Delphi XE4编译)给出了预期的结果new_value(4)=new_value()1)

使用Notepad ++,我得到了预期的结果.

使用命名组可以清楚地看出1是字面意义上的后向引用:

Pattern := 'my_replace_string\((?<name>\d+)\)';
Replace := 'new_value(${name})';
// Result: 'new_value(4)=new_value(){name})'
Run Code Online (Sandbox Code Playgroud)

替换总是那么简单(可能是零次或多次my_replace_string),所以我可以轻松创建自定义搜索和替换功能,但我想知道这里发生了什么.

这是我的错,还是一个错误?

Jan*_*rts 13

我可以重现Delphi XE4中的错误.我在Delphi XE5中得到了正确的行为.

这个bug就在TPerlRegEx.ComputeReplacement.我使用Delphi XE3为Embarcadero贡献的代码UTF8String.随着德尔福XE4 Embarcadero UTF8String从该RegularExpressionsCore单元中淘汰并取而代之TBytes.进行此更改的开发人员似乎错过了Delphi中字符串和动态数组之间的重要区别.字符串使用写时复制机制,而动态数组则不使用.

因此,在我的原始代码中,TPerlRegEx.ComputeReplacement可以执行S := FReplacement然后修改临时变量S以替换反向引用而不影响FReplacement字段,因为两者都是字符串.在修改后的代码,S := FReplacement使得S指向相同的阵列FReplacement,并且当反向引用在S被取代时,FReplacement也被修改.因此,第一次更换是正确的,而后续更换是错误的,因为FReplacement已经瘫痪.

在Delphi XE5中,通过替换S := FReplacement它来修复此问题以制作适当的临时副本:

SetLength(S, Length(FReplacement));
Move(FReplacement[0], S[0], Length(FReplacement));
Run Code Online (Sandbox Code Playgroud)

当Delphi 2009发布时,Embarcadero发表了很多关于不应该使用字符串类型来表示字节序列的讨论.现在看来他们正在犯下使用TBytes来表示字符串的相反错误.

我之前向Embarcadero推荐的整个混乱的解决方案是切换到使用UTF16LE的新pcre16函数,就像Delphi字符串一样.当Delphi XE发布时,这些功能不存在,但它们现在已经存在,应该使用它们.