【问题标题】:Delphi TRegEx backreference broken?Delphi TRegEx 反向引用坏了?
【发布时间】:2014-01-07 11:22:51
【问题描述】:

我在使用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),所以我可以轻松创建自定义搜索和替换功能,但我想知道这里发生了什么。

是我的错还是BUG?

【问题讨论】:

    标签: regex delphi delphi-xe4 backreference


    【解决方案1】:

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

    错误在TPerlRegEx.ComputeReplacement。我贡献给 Embarcadero 以包含在 Delphi XE3 中的代码使用了UTF8String。 Delphi XE4 Embarcadero 从RegularExpressionsCore 单元中删除了UTF8String,并用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 发布时不存在,但现在有了,应该使用。

    【讨论】:

    • 非常感谢 - 我已经解决了。
    • 而那些 utf-16 函数被 Jedi Code Lib 使用。至少这不会在 utf-16 到 utf-8 到 utf-16 往返上浪费时间
    • 很高兴知道 JCL 已经更新为使用 pcre16。
    【解决方案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)
    

    所以看起来这个错误是在 XE4 中引入的。我建议你提交一份质量控制报告。使用我上面的 SSCCE,因为它是独立的。

    【讨论】:

    • 感谢您的确认
    • 已在 XE5 中修复,因此无需 QC 报告。
    猜你喜欢
    • 2012-09-22
    • 2015-12-02
    • 1970-01-01
    • 2017-05-09
    • 2014-01-12
    • 2012-10-30
    • 1970-01-01
    • 2014-10-07
    • 1970-01-01
    相关资源
    最近更新 更多