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

时间:2014-01-07 11:22:51

标签: regex delphi delphi-xe4 backreference

使用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;

预期结果为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})'

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

这是我的错还是错误?

2 个答案:

答案 0 :(得分:13)

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

该错误发生在TPerlRegEx.ComputeReplacement。我为Embarcadero提供的包含在Delphi XE3中的代码使用了UTF8String。使用Delphi 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));

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

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

答案 1 :(得分:2)

这似乎是一个错误。这是我的测试程序:

{$APPTYPE CONSOLE}

uses
  RegularExpressions;

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);
  Writeln(Value);
  Readln;
end.

在我的XE3上输出为:

new_value(4)=new_value(5)

所以看起来这个bug是在XE4中引入的。我建议您提交质量控制报告。使用我的SSCCE,因为它是独立的。