如何使用Indy解码utf-8 unicode字符

时间:2017-02-01 21:37:25

标签: http delphi indy

我有一个TIdHttpServer应用程序。我有一个带有特殊字符的简单html文档:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">


    <head>
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
        <title>This is the title</title>
    </head>

    <body>
        <form method="post">
            <p>
                <input name="name" value="Все данные по веб-сайту" />
                <input type="submit" value="submit" />
            </p>
        </form>
    </body>
</html>

我提供此页面并处理帖子。我的“获取”代码如下。问题是我无法正确解码%hh 数据。

procedure TForm3.Get(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  mFileName: String;
  txtFile: TextFile;
begin
  if ARequestInfo.Params.values['name']<>'' then begin
    AssignFile( txtFile , ChangeFileExt(ParamStr(0),'.log') );
    Append( TxtFile );
    WriteLn(TxtFile,'Unparsed:'+ARequestInfo.UnparsedParams);
    WriteLn(TxtFile,'Parsed:'+ARequestInfo.Params.values['name']);
    MyDecodeAndSetParams(ARequestInfo);
    WriteLn(TxtFile,'Decoded:'+ARequestInfo.Params.values['name']);
    System.Close( TxtFile );
  end ;
  mFileName := ExtractFileDir(ParamStr(0))+'\inputform.txt';
  AResponseInfo.ContentStream := TFileStream.Create(mFileName, fmOpenRead);

end;

MyDecodeAndSetParams 功能:

procedure MyDecodeAndSetParams(ARequestInfo: TIdHTTPRequestInfo);
var
  i, j : Integer;
  value,s: string;
  LEncoding: IIdTextEncoding;
begin
  if IsHeaderMediaType(ARequestInfo.ContentType, 'application/x-www-form-urlencoded') then
  begin
    value := ARequestInfo.FormParams;
//    LEncoding := CharsetToEncoding(ARequestInfo.CharSet);
    if ARequestInfo.CharSet <> '' then
      LEncoding := CharsetToEncoding(ARequestInfo.CharSet)
    else
      LEncoding := IndyTextEncoding_UTF8;
  end else
  begin
    value := ARequestInfo.QueryParams;
    LEncoding := IndyTextEncoding_UTF8;
  end;

  ARequestInfo.Params.BeginUpdate;
  try
    ARequestInfo.Params.Clear;
    i := 1;
    while i <= Length(value) do
    begin
      j := i;
      while (j <= Length(value)) and (value[j] <> '&') do
      begin
        Inc(j);
      end;
      s := StringReplace(Copy(value, i, j-i), '+', ' ', [rfReplaceAll]);
      ARequestInfo.Params.Add(TIdURI.URLDecode(s, LEncoding));
      i := j + 1;
    end;
  finally
    ARequestInfo.Params.EndUpdate;
  end;
end;

我的文件中的输出如下:

Unparsed:name=%D0%92%D1%81%D0%B5+%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5+%D0%BF%D0%BE+%D0%B2%D0%B5%D0%B1-%D1%81%D0%B0%D0%B9%D1%82%D1%83
Parsed:οсе даннϿе по веб-сайϿϿ
Decoded:οсе даннϿе по веб-сайϿϿ

我可以使用this decoder获取Unparsed数据并对其进行解码,并正确返回字符串:

  

Вседанныеповеб-сайту

我需要做什么才能将params正确解码为表单上的内容?

1 个答案:

答案 0 :(得分:5)

如果AResponseInfo.CharSet为空(因为客户端没有在HTTP Content-Type标头中发送字符集),CharsetToEncoding('')将返回Indy的本地8位字符集而不是UTF-8。这就是为什么你的数据没有被正确解码的原因。

对于application/x-www-form-urlencoded,字符集并不总是在HTTP标头中发送,因为客户端可能会认为服务器根据它发送HTML的字符集知道所期望的字符集。客户端也可能可能会在已发布的表单数据中发送字符集,例如在_charset_字段中。

尝试更改此内容:

LEncoding := CharsetToEncoding(ARequestInfo.CharSet);

对此:

if ARequestInfo.CharSet <> '' then
  LEncoding := CharsetToEncoding(ARequestInfo.CharSet)
else
  LEncoding := IndyTextEncoding_UTF8;

这样,除非客户端发送显式字符集,否则默认为UTF-8。

更新:如果您使用的是Unicode前版本的Delphi(2007或更早版本),Indy会使用AnsiString代替UnicodeString,因此TIdURI.URLDecode()将首先使用指定的AByteEncoding参数将输入解码为Unicode(如果未指定,则默认为IndyTextEncoding_UTF8),然后使用指定的ADestEncoding参数将Unicode数据转换为ANSI(默认值)如果没有指定,则IndyTextEncoding_OSDefault

您在解码为UTF-8时已经显示正确解码为Unicode的俄语输入,但如果您的代码在运行的机器上运行,则在转换为ANSI期间很容易丢失字符(将它们转换为'?')不要在OS层使用俄语字符集,例如ISO-8859-5或KOI8-R。

为确保正确的转换,您必须在这些计算机上指定所需的AnsiString编码,例如:

var
  LEncoding, LAnsiEncoding: IIdTextEncoding;
...

LEncoding := IndyTextEncoding_UTF8;
LAnsiEncoding := CharsetToEncoding('ISO-8859-5'); // or 'KOI8-R', etc
...
ARequestInfo.Params.Add(TIdURI.URLDecode(s, LEncoding, LAnsiEncoding));

在Unicode版本的Delphi(2009及更高版本)中,Indy使用UnicodeString代替AnsiString,因此不存在ADestEncoding参数。