我有一个问题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\((?\d+)\)'; Replace := 'new_value(${name})'; // Result: 'new_value(4)=new_value(){name})'
替换总是那么简单(可能是零次或多次my_replace_string
),所以我可以轻松创建自定义搜索和替换功能,但我想知道这里发生了什么.
这是我的错,还是一个错误?
我可以重现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));
当Delphi 2009发布时,Embarcadero发表了很多关于不应该使用字符串类型来表示字节序列的讨论.现在看来他们正在犯下使用TBytes来表示字符串的相反错误.
我之前向Embarcadero推荐的整个混乱的解决方案是切换到使用UTF16LE的新pcre16函数,就像Delphi字符串一样.当Delphi XE发布时,这些功能不存在,但它们现在已经存在,应该使用它们.