使用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>
这是我的错还是错误?
答案 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,因为它是独立的。