Delphi & Indy & utf8 - PullRequest
       35

Delphi & Indy & utf8

0 голосов
/ 14 октября 2018

У меня проблема с доступом к веб-сайтам с кодировкой utf8, например, когда я пытаюсь получить доступ на этом www

Нажмите, например,

все символы 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" равно

"name":"Национальный парк Меркантур",

, но после инструкции Get у меня есть

"имя": "Национальный парк Меркантур",

У вас есть идея, где моя ошибка?Спасибо всем!

1 Ответ

0 голосов
/ 14 октября 2018

В Delphi 2009+, который включает в себя XE6, string - это кодировка UTF-16 UnicodeString.

Вы используете перегруженную версию TIdHTTP.Get(), которая возвращает string.Он декодирует отправленный текст в UTF-16, используя любой набор символов, указанный в ответе.Если текст не декодируется должным образом, это, вероятно, означает, что ответ не сообщает о правильной кодировке.Если используется неправильная кодировка, текст не будет декодироваться должным образом.

Рассматриваемый URL фактически отправляет заголовок ответа Content-Type с application/json без указания charset ввсе.Кодировка по умолчанию для application/json - UTF-8, но Indy этого не знает, поэтому вместо этого она использует свое собственное внутреннее значение по умолчанию, которое не является UTF-8.Вот почему текст не декодируется должным образом, когда присутствуют символы, не входящие в ASCII.

В этом случае, если вы ЗНАЕТЕ, что кодировка всегда будет UTF-8, у вас есть несколько обходных путей на выбор:

  • Вы можете установить кодировку Indy по умолчанию в UTF-8, установив глобальную переменную GIdDefaultTextEncoding в единице IdGlobal:

    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.Используя TMemoryStream или TStringStream, вы можете самостоятельно декодировать необработанные байты, используя 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;
    
...