使用单位 LazUTF8 时 TIdURI.URLDecode 的错误结果

Wrong result of TIdURI.URLDecode when unit LazUTF8 is used

使用 Free Pascal 3.0.4,此测试程序正确写入 ÄÖÜ

program FPCTest;

uses IdURI;

begin
  WriteLn(TIdURI.URLDecode('%C3%84%C3%96%C3%9C'));
  ReadLn;
end.

但是,如果使用单位 LazUTF8(如 here 所述),它会写入 ???

program FPCTest;

uses IdURI, LazUTF8;

begin
  WriteLn(TIdURI.URLDecode('%C3%84%C3%96%C3%9C'));
  ReadLn;
end.

如何解决使用 LazUTF8 的程序的解码错误?

在 TIdURI.URLDecode 行中进行此更改后,可以使用第 386ff 行 LazUTF8:

  {$IFDEF FPC}
  Result := string(AByteEncoding.GetString(LBytes));
  {$ELSE}
  {$IFDEF STRING_IS_ANSI}
  EnsureEncoding(ADestEncoding, encOSDefault);
  CheckByteEncoding(LBytes, AByteEncoding, ADestEncoding);
  SetString(Result, PAnsiChar(LBytes), Length(LBytes));
  {$ELSE}
  Result := AByteEncoding.GetString(LBytes);
  {$ENDIF}
  {$ENDIF}

备注

此更改假定始终使用 LazUTF8 单元,并且每次使用新版本时都需要应用 Indy 源代码更改。

此外,我发现无法以适用于和不适用 LazUTF8 的方式修复 TIdURI.URLDecode。

String 类型是 AnsiString 1 的别名时,Indy 的许多功能公开了额外的 parameters/properties 让用户控制当在执行 AnsiString<->byte 转换的操作中传递 AnsiString 值时,使用 ANSI 编码。

1: Delphi pre-2009, FreePascal/Lazarus when {$ModeSwitch UnicodeStrings} and {$Mode DelphiUnicode} are not used (FYI, Indy 11 will use them !).

在大多数情况下,Indy 的默认字节编码是 ASCII(因为 Indy 实现的许多 Internet 协议最初只支持 ASCII - 各个 Indy 组件会根据协议将自己升级为 UTF),尽管有些东西使用 OS 默认 codepage/charset 而不是。

Indy 的默认字节编码可以在运行时通过在 IdGlobal 单元中设置全局 GIdDefaultTextEncoding 变量来更改,例如:

GIdDefaultTextEncoding := encUTF8;

但是,在这种特殊情况下,TIdURI.URLEncode() 不使用 GIdDefaultTextEncoding,但它确实有一个可选的 ADestEncoding 参数,您可以使用该参数为returned AnsiString(除了一个可选的 AByteEncoding 参数来指定已解析的 url 八位字节的字节编码 - 默认为 UTF-8),例如:

TIdURI.URLDecode('%C3%84%C3%96%C3%9C'
  {$IFNDEF FPC_UNICODESTRINGS}, IndyTextEncoding_UTF8, IndyTextEncoding_UTF8{$ENDIF}
)

上面的代码会将 url 编码的八位字节解析为 UTF-8,然后 return 该数据按原样解析为 UTF-8 编码 AnsiString.

如果您没有为 ADestEncoding 指定输出编码,URLDecode() 默认为 OS 默认值。如果您希望它使用 GIdDefaultTextEncoding,请在 ADestEncoding 参数中指定 IndyTextEncoding_Default

TIdURI.URLDecode('%C3%84%C3%96%C3%9C'
  {$IFNDEF FPC_UNICODESTRINGS}, IndyTextEncoding_UTF8, IndyTextEncoding_Default{$ENDIF}
)

另一种选择是对 ADestEncoding 使用 IndyTextEncoding(CodePage) 函数,将其传递给 FreePascal 的 DefaultSystemCodePage 变量,LazUtils 包将其设置为 CP_UTF8 2:

TIdURI.URLDecode('%C3%84%C3%96%C3%9C'
  {$IFNDEF FPC_UNICODESTRINGS}, IndyTextEncoding_UTF8, IndyTextEncoding(DefaultSystemCodePage){$ENDIF}
)

2:我在 Indy 的问题跟踪器中有 opened a ticket,以便在为 FreePascal/Lazarus 编译时添加对 DefaultSystemCodePage 的支持。