【问题标题】:Delphi & Indy & utf8德尔福 & 印地 & utf8
【发布时间】:2019-03-18 21:49:45
【问题描述】:

我在访问带有 utf8 字符集的网站时遇到问题,例如当我尝试访问此 www 时

Click for example

所有 utf8 字符均未正确编码。 这是我的访问例程:

var
  Web     : TIdHTTP;
  Sito    : String;
  hIOHand : TIdSSLIOHandlerSocketOpenSSL;

begin
  Url := TIdURI.URLEncode(Url);


  try
    Web := TIdHTTP.Create(nil);
    hIOHand := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    hIOHand.DefStringEncoding := IndyTextEncoding_UTF8;
    hIOHand.SSLOptions.SSLVersions := [sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2,sslvSSLv2,sslvSSLv3,sslvSSLv23];
    Web.IOHandler := hIOHand;
    Web.Request.CharSet := 'utf-8';


    Web.Request.UserAgent := INET_USERAGENT;       //Custom user agent string
    Web.RedirectMaximum := INET_REDIRECT_MAX;      //Maximum redirects
    Web.HandleRedirects := INET_REDIRECT_MAX <> 0; //Handle redirects
    Web.ReadTimeOut := INET_TIMEOUT_SECS * 1000;   //Read timeout msec
    try
      Sito := Web.Get(Url);
      Web.Disconnect;
    except
      on e : exception do
        Sito := 'ERR: ' +Url+#32+e.Message;
    end;
  finally
    Web.Free;
    hIOHand.Free;
  end;

我尝试了所有解决方案,但在 Sito var 中我发现所有时间错误的字符,例如“名称”的正确值是

"name": "Aire d'adhésion du Parc national du Mercantour",

但是在获取指令之后我有

"name": "Aire d'adhésion du Parc national du Mercantour",

你知道我的错误在哪里吗? 谢谢大家!

【问题讨论】:

  • delphi XE6 的 var 是:Web : TIdHTTP;西托:字符串;
  • FWIW,您问题中的链接在这里不起作用。
  • 我更改了文本中的链接,并添加了 var 部分。交易
  • 您不应该为 HTTP 设置 DefStringEncoding。您不应为 SSL 启用 sslvSSLv2sslvSSLv3sslvSSLv23。您不应该设置Request.CharSet。而且您无需致电Disconnect

标签: delphi indy delphi-xe6


【解决方案1】:

在包括 XE6 的 Delphi 2009+ 中,string 是 UTF-16 编码的UnicodeString

您正在使用TIdHTTP.Get() 的重载版本,它返回string。它使用响应报告的任何字符集将发送的文本解码为 UTF-16。如果文本未正确解码,则可能意味着响应未报告正确的字符集。如果使用了错误的字符集,文本将无法正确解码。

实际上,有问题的 URL 发送了一个响应 Content-Type 标头,该标头设置为 application/json,根本没有指定 charsetapplication/json 的默认字符集是 UTF-8,但 Indy 不知道这一点,所以它最终使用了自己的内部默认值,而不是 UTF-8。这就是为什么当存在非 ASCII 字符时文本无法正确解码的原因。

在这种情况下,如果您知道字符集将始终为 UTF-8,那么您有几个解决方法可供选择:

  • 您可以通过在IdGlobal 单元中设置全局GIdDefaultTextEncoding 变量来将Indy 的默认字符集设置为UTF-8:

    GIdDefaultTextEncoding := encUTF8;
    
  • 如果TIdHTTP.Response.Charset 属性为空或不正确,您可以使用TIdHTTP.OnHeadersAvailable 事件将'utf-8' 属性更改为'utf-8'

    Web.OnHeadersAvailable := CheckResponseCharset;
    
    ...
    
    procedure TMyClass.CheckResponseCharset(Sender: TObject; AHeaders: TIdHeaderList; var VContinue: Boolean);
    var
      Response: TIdHTTPResponse;
    begin
      Response := TIdHTTP(Sender).Response;
      if IsHeaderMediaType(Response.ContentType, 'application/json') and (Response.Charset = '') then
        Response.Charset := 'utf-8';
      VContinue := True;
    end;
    
  • 您可以使用TIdHTTP.Get() 的另一个重载版本来填充输出TStream,而不是返回string。使用TMemoryStreamTStringStream,您可以使用UTF-8 自己解码原始字节:

    MStrm := TMemoryStream.Create;
    try
      Web.Get(Url, MStrm);
      MStrm.Position := 0;
      Sito := ReadStringFromStream(MStrm, IndyTextEncoding_UTF8);
    finally
      SStrm.Free;
    end;
    

    SStrm := TStringStream.Create('', TEncoding.UTF8);
    try
      Web.Get(Url, SStrm);
      Sito := SStrm.DataString;
    finally
      SStrm.Free;
    end;
    

【讨论】:

  • 完美的人头马,你很厉害!我使用最后一个解决方案:使用 TStringStream 我将 GET 指令更改为: Web.Get(Url,TStr); Sito := UTF8Decode(TStr.DataString) - TStr 是我的 TStringStream - 一切都很完美!所有字符都完美解码!非常感谢!!
  • @ondertol 在这种情况下使用TStringStream正确方法是在其构造函数中使用TEncoding.UTF8,而不是使用UTF8Decode()。我用例子更新了我的答案。
  • Remy...谢谢您的重要帮助,我将程序作为您的代码进行了审核,我对指令 TStr := TStringStream.Create(TEncoding.UTF8); 仅有问题。 (编译器给出一条消息“没有可以使用这些参数调用的'Create'的重载版本”来解决它我为第一个参数插入一个字符串作为 TStr := TStringStream.Create(Stg,TEncoding.UTF8);。 ..那么一切都很完美。再一次谢谢你!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-17
  • 2013-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多