Delphi和Indy&utf8

时间:2018-10-14 06:59:10

标签: delphi indy delphi-xe6

我无法访问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中,我一直发现错误的字符,例如“名称”的正确值为

“名称”:“ Parcan national du Mercantour国家公园”,

但是在获得Get指令后

“名称”:“ Parcan national du Mercantour国家公园的艾尔城堡”,

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

1 个答案:

答案 0 :(得分:6)

在包含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,则可以从以下几种解决方法中进行选择:

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

    GIdDefaultTextEncoding := encUTF8;
    
  • 如果空白或不正确,可以使用TIdHTTP.OnHeadersAvailable事件将TIdHTTP.Response.Charset属性更改为'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;