为什么TPageProducer不会从字符串中删除引号?

8

我正在尝试调试一个问题,这个问题只在使用XE4编译后运行我的大型应用程序时出现,而在XE3中运行良好。该问题似乎会导致一些带引号的字符串(例如"MyString")即使已经被Web.HTTPProd中的TPageProducer“去引号”,仍然保留它们的引号。例如,考虑下面的代码,它是来自Delphi源代码单元Web.HTTPApp的一个小片段:

procedure ExtractHeaderFields(Separators, _WhiteSpace: TSysCharSet; Content: PChar;
  Strings: TStrings; Decode: Boolean; StripQuotes: Boolean = False);
{$ENDIF NEXTGEN}
var
  Head, Tail: PChar;
  EOS, InQuote, LeadQuote: Boolean;
  QuoteChar: Char;
  ExtractedField: string;
{$IFNDEF NEXTGEN}
  WhiteSpaceWithCRLF: TSysCharSet;
  SeparatorsWithCRLF: TSysCharSet;
{$ENDIF !NEXTGEN}

  function DoStripQuotes(const S: string): string;
  var
    I: Integer;
    InStripQuote: Boolean;
    StripQuoteChar: Char;
  begin
    Result := S;
    InStripQuote := False;
    StripQuoteChar := #0;
    if StripQuotes then
    begin
      for I := Result.Length - 1 downto 0 do
        if Result.Chars[I].IsInArray(['''', '"']) then
          if InStripQuote and (StripQuoteChar = Result.Chars[I]) then
          begin
            Result.Remove(I, 1);
            InStripQuote := False;
          end
          else if not InStripQuote then
          begin
            StripQuoteChar := Result.Chars[I];
            InStripQuote := True;
            Result.Remove(I, 1);
          end
    end;
  end;

我看到在使用TPageProducer时会调用这个函数,我可以看到我的好源字符串进入上面的ExtractHeaderFields例程,然后进入“DoStripQuotes”函数。进入DoStripQuotes并观察“Result”时,即使调用Result.Remove(以去除引号),它也不会改变。当我将此“DoStripQuotes”例程移植到简单的测试应用程序中时,它无法编译,告诉我“Result.anything”不允许。那么我假设,在Web.HTTPProd的上下文中,虽然已定义为“string”,但Result必须是另一种类型的字符串。

因此我想这可能与我听说过的“不可变字符串”有关。我阅读了这篇SO问题,虽然我大致理解了其中的内容,但我需要更多实用的建议。

具体地,我想对以下问题得到答案:

  1. 如果允许符号Result.Length,则'Result'是什么类型的'string'?
  2. 是否有一种方法可以告诉编译器在一个单位中使用“XE3”兼容性? (这可能会让我看到问题的起源)。 我尝试过{$ZEROBASEDSTRINGS ON} / OFF,但这似乎会造成更大的混乱,我不知道该怎么做!

感谢任何帮助。

后来编辑:如下面所接受的答案中所述,这是VCL单元Web.HTTPApp.pas中的一个错误,应该在2645行附近两个位置中读取“Result:=Result.Remove(I,1)”,而不是“Result.Remove(I,1)”

1个回答

9
如果允许使用“Result.Length”表示法,那么“Result”是什么类型的“string”?
它只是您自从Delphi 2009以来一直在使用的旧“string”,别名为“UnicodeString”。不同之处在于,此代码使用新的记录助手(特别是“SysUtils.TStringHelper”)。这就是让您在字符串变量上使用“.”符号的原因。
我是否可以告诉编译器使用“XE3”兼容性来编译单元?
不。所涉及的代码是库单元,并且设计为在特定模式下进行编译。而且,除非您自己承担编译RTL / VCL,否则您无法轻松重新编译它。即使有这样的模式,也没有帮助,因为代码简单地错误(请参见下文)。任何模式切换都不能修复此特定代码片段。
我开始想也许这与我听说过的“不可变字符串”有关。
不是这样。迄今为止,Delphi编译器都没有不可变的字符串。不可变字符串的概念只是作为未来的更改提出的。如果进行更改,则应首先在移动编译器中进行更改。
问题实际上只是您发布的代码中的一个非常简单的错误。使用“Remove”是错误的。该方法不会直接修改字符串。相反,它返回一个已删除字符的新字符串。代码应该是这样的:
Result := Result.Remove(I, 1);

开发人员在编写ExtractHeaderFields时犯了错误的原因是命名为Remove的字符串助手代码的设计者将其命名错误。既然Remove是一个动词,您就希望它可以原地操作。该方法不修改主题并返回新实例,因此应给该方法命名为名词,例如Remnants。我认为RTL设计人员复制了.net命名,其中也存在同样的缺陷。
如果不存在QC报告,则应提交QC报告。我知道XE4更新1刚刚发布。它很可能包含修复。
我看到的您的其他选择有:
1.继续使用XE3,直到XE4足够调试。
2.在项目中包括Web.HTTPApp单元的副本,并自行修复漏洞。

你说得完全正确,David。问题就出在 Delphi 的 Web.HTTPApp 中,它忘记了对 Result 进行赋值。感谢你的教程。 - Brian Frost
QC报告已提交。我稍后会检查这个问题是否在更新#1中得到解决。 - Brian Frost
似乎没有编译器选项来警告忽略函数结果。在这种情况下,即使有一个将TStringHelper标记为这种指令的选项,编译器错误也应该被引发。 - LU RD
@LURD 如果我没记错,你在原始的 Pascal 中无法忽略函数结果,但是扩展语法会改变这种行为。编译器警告将非常有价值。很遗憾,许多 Emba 库代码的质量都很低。 - David Heffernan
@所有人,我可以确认在更新1中这个问题仍未被修复。 - Brian Frost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接