2013-09-16 4 views
7

Тека следующих ситуаций:TIdHTTP кодировка символов ответа POST

procedure Test; 

var 
Response : String; 

begin 
Response := IdHttp.Post(MyUrL, AStream); 
DoSomethingWith(Response); 
end; 

Теперь веб-сервер возвращает мне данные в UTF-8. Предположим, что он возвращает мне некоторый XML-код UTF-8, содержащий символ é. Если я использую переменную Response, она не содержит этого символа, но это вариант UTF-8 (# C3 # A9), поэтому Indy не расшифровал декодирование?

Теперь я знаю, как решить эту проблему:

procedure Test; 

var 
Response : String; 

begin 
Response := UTF8ToString(IdHttp.Post(MyUrL, AStream)); 
DoSomethingWith(Response); 
end; 

Один нюанс с этим решением: Delphi вызывает предупреждение W1058 (неявный строковый бросок с потенциальной потери данных из «строка» до «RawByteString»)

Мой вопрос: это правильный способ справиться с этой проблемой или я могу дать указание TIdHTTP сделать преобразование в UnicodeString для меня?

ответ

7

Если вы используете версию уточненный Инди 10, то перегружен версия TIdHTTP.Post(), которая возвращает Stringделает декодировать данные в Unicode, однако фактическая кодировка используется для декодирования зависит от того, какого типа носителя заголовок ответа HTTP Content-Type указует:

  1. если тип носителя либо application/xml, application/xml-external-parsed-entity, application/xml-dtd, или не является text/..., но заканчивается +xml, тогда используется кодировка, указанная в атрибуте пролога XML. Если кодировка не указана, используется UTF-8.

  2. в противном случае, если заголовок ответа Content-Type указывает кодировку, то он используется.

  3. в противном случае, если тип носителя является text/... типа, то:

    а. если тип носителя text/xml, text/xml-external-parsed-entity или заканчивается +xml, то используется us-ascii.

    b. в противном случае используется ISO-8859-1.

  4. В противном случае используется кодировка по умолчанию Indy (ASCII по умолчанию).

Не видя фактического HTTP Content-Type заголовок, это трудно понять, какие условия вашей ситуации попадает. Похоже, что он попадает либо в # 2, либо в # 3b, который учитывал бы значения байтов UTF-8, возвращаемые как есть, если используется ISO-8859-1 или аналогичная кодировка.

UTF8ToString() ожидает, что UTF-8 закодирован RawByteString в качестве входных данных, но вы передаете ему кодировку UTF-16 UnicodeString. RTL выполнит преобразование UTF16-> Ansi в этой ситуации, используя кодировку Ansi по умолчанию для преобразования. Вот почему вы получаете предупреждение компилятора, потому что такое преобразование может потерять данные.

XML - это действительно двоичный формат данных с учетом кодировки кодировки. Парсер XML должен знать, что такое кодировка XML, и иметь возможность самостоятельно анализировать необработанные закодированные байты. Вот почему XML имеет явный атрибут encoding в прологе XML. Однако, когда TIdHTTP загружает XML как String, хотя он автоматически декодирует его в Unicode, он не, но обновляет пролог XML соответственно.

Реальное решение заключается в том, чтобы не загружать XML как String в первую очередь. Загрузите его как TStream вместо этого (TMemoryStream - лучший выбор, чем TStringStream), поэтому ваш XML-парсер имеет доступ к исходным байтам, оригинальной декларации кодировки и т. Д. Например, вы можете передать TStream методу TXMLDocument.LoadFromStream().

+0

Привет, Реми, спасибо за ваш четкий ответ. После проверки заголовка ответа HTTP я увидел, что Charset не указан, поэтому в моем случае это было # 3b. – whosrdaddy

2

Вы можете сделать это:

var 
    sstream: TStringStream; 
begin 
    sstream := TStringStream.Create('', TEncoding.UTF8); 
    try 
    IdHttp.Post(MyUrL, AStream, sstream); 
    DoSomethingWith(sstream.DataString); 
    finally 
    sstream.Free; 
    end; 
+1

Это работает только если вы заранее знаете, что ответ всегда UTF-8. –

+0

Привет, Марко, спасибо за ваш ответ, на самом деле это решение, которое я искал, потому что разбор XML не нужен в моем конкретном случае (и я ЗНАЮ, что у меня будет UTF-8 в качестве ответа). Я принял ответ Реми, потому что это самый правильный ответ :). – whosrdaddy

Смежные вопросы