Delphi2010下UTF8编码问题

时间:2023-01-05 09:47:04
同样的代码,Delphi7下用UTF8Encode给中文编码没问题,但是在Delphi2010下中文超过两个字时(小于等于两个汉字时没问题)最后一个汉字的编码就是错误的,如:

Delphi7下
UTF8Encode('明信片')
idHttp.Get时url编码后为:%E6%98%8E%E4%BF%A1%E7%89%87 ,是正确的。


Delphi2010下
UTF8Encode('明信片')
idHttp.Get时url编码后为:%E6%98%8E%E4%BF%A1%E7%89%00 ,最后变成%00了。

问题出在哪里呢?

27 个解决方案

#1


我测试没问题:
uses HTTPApp;
...
procedure TForm1.Button2Click(Sender: TObject);
begin
  Edit1.text:=HTTPEncode(UTF8Encode('明信片'));
end;

结果为:
%E6%98%8E%E4%BF%A1%E7%89%87

2010+2in7

#2


检查存放变量的长度

#3


URL编码没用HTTPEncode,因为不想把url里的&和=也编码,我用的是下面这个函数,麻烦看看这个函数有什么问题呢?在Delphi7下就没问题。

function URLEncode(const S: AnsiString; const InQueryString: Boolean): AnsiString;
var
  Idx: Integer;
begin
  Result := '';
  for Idx := 1 to Length(S) do
  begin
      case S[Idx] of
        'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.', '=', '&', '%':
          Result := Result + S[Idx];
        ' ':
          if InQueryString then
            Result := Result + '+'
          else
            Result := Result + '%20';
        else
          Result := Result + '%' + SysUtils.IntToHex(Ord(S[Idx]), 2);
      end;
  end;
end;

#4


我用你的程序,在D2010+win7下也没问题。

#5


查你的程序的其它部分

#6


找到原因了,好像是TStringList的问题哦,

var
  str : AnsiString;
  List : TStringList;
begin
  List := TStringList.Create;
  try
  List.Add('q=' + UTF8Encode('明信片')); 
  ShowMessage(List.Text);


结果是:q=鏄庝俊鐗

这咋解决呀....呜....

#7


q=鏄庝俊鐗 
这个没问题,utf8在系统上是要乱码的

#8


不对呀,这个不是“明信片”的UTF8编码呀,放解码器里看是不对的,把“鏄庝俊鐗”最后一个“鐗”去掉就是“明信”两个字。

#9


呜~是什么问题呀!!

#10


可能的问题是List.Text在2010下是unicode的,你将它转成AnsiString再试试(我单位没有2010)

#11


“明信片”编码后正确的应该是:%E6%98%8E%E4%BF%A1%E7%89%87

可是用下面的代码得到的结果是:
q=%E6%98%8E%E4%BF%A1%E7%89%00&cid=&nicks=&price=&page=&size=&orderby=&status=&post=&v=1.0&

怎么解决这个问题呢?


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function URLEncode(const S: AnsiString; const InQueryString: Boolean): AnsiString;
var
  Idx: Integer;
begin
  Result := '';
  for Idx := 1 to Length(S) do
  begin
      case S[Idx] of
        'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.', '=', '&', '%':
          Result := Result + S[Idx];
        ' ':
          if InQueryString then
            Result := Result + '+'
          else
            Result := Result + '%20';
        else
          Result := Result + '%' + SysUtils.IntToHex(Ord(S[Idx]), 2);
      end;
  end;
end;

function ParamList():AnsiString;
var
  str : AnsiString;
  i : Integer;
  List : TStringList;
begin
  Result := '';
  List := TStringList.Create;
  try
    List.Add('q=' + UTF8Encode('明信片'));
    List.Add('cid=');
    List.Add('nicks=');
    List.Add('price=');
    List.Add('page=');
    List.Add('size=');
    List.Add('orderby=');
    List.Add('status=');
    List.Add('post=');
    List.Add('v=1.0');
    for i := 0 to List.Count-1 do
    begin
      str := str + List.Strings[i] + '&';
    end;
    Result := str;
  finally
    List.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Text := URLEncode(ParamList(),true);
end;

end.

#12


等答案。。。

#13


经测试,是由于LIST的TSrings引起的,它是unicode的,有非法字符生成,我的结果为:
%E6%98%8E%E4%BF%A1%E7%89%3F
%3F为?
正在研究中

#14


我也查了很久了,希望能有解决办法!!

#15


LIST的Strings为WideString类型(Unicode)不能存某些utf8字符,会引起乱码。目前我能想到的解决方案(就你的程序来讲)
1.改:
 try 
    List.Add('q=' + UTF8Encode('明信片')); 
为:
  str:='q=' + UTF8Encode('明信片'); //这个单独处理,不放入list
2.改:
 str := str + List.Strings[i] + '&'; 
为:
 str := str + AnsiString(List.Strings[i]) + '&'; //不能用WideString参与运算
-------------------
这样,在Memo中可以得到你要的:
q=%E6%98%8E%E4%BF%A1%E7%89%87cid=&nicks=&price=&page=&size=&orderby=&status=&post=&v=1.0&

#16


1中的try还是要的

#17


这样也不行,参数是要排序的。
用了:List.Sorted := True; 参数要以字母升序顺序。
如果是用数组呢,定义数组觉得有点麻烦好像。

#18


如果你一定要用list,可以这样:
function ParamList():AnsiString;
var
  str : AnsiString;
  i : Integer;
  List : TStringList;
begin
  Result := '';
  List := TStringList.Create;
  try
    List.Add('q=明信片');  //这是先不转成utf8,因为list不能存某些utf8字符
    List.Add('cid=');
    List.Add('nicks=');
    List.Add('price=');
    List.Add('page=');
    List.Add('size=');
    List.Add('orderby=');
    List.Add('status=');
    List.Add('post=');
    List.Add('v=1.0');
    for i := 0 to List.Count-1 do
    begin
      str := str + List.Strings[i] + '&';
    end;
    Result := str;
  finally
    List.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Memo1.Text := URLEncode(UTF8Encode(ParamList()),true); //最后转,对于ascii字符utf8与unicode或ansi编码都是相同的,只有汉字不同
end; 

结果就是你要的

#19


要求是中文先UTF8编码,ParamList()返回值要去算一个MD5的,如果先URL编码算出来的MD5就跟要求不一样了。最后idhttp提交时才进行URLEncode。
如果不用TStringList还有什么办法实现么?

#20


如果不用TStringList,可以用数组,不过要繁一点,考虑到你要sort,我将数组放入一LIST中,list可用sort,当然你也可以自己写sort,有关list的sort,如果你不会用,只能明天写给你,以下是我新写的ParamList()函数:
function ParamList():AnsiString;
var
  str : AnsiString;
  i : Integer;
  List : TList;
  s:array [0..20] of AnsiString; //这里你可用动态数组

begin
  Result := '';
  List := TList.Create;
  try
  s[0]:='q=' + UTF8Encode('明信片');
  List.Add(@s[0][1]);  //LIST加入的是指针,这是注意的
  s[1]:='cid=';
  List.Add(@s[1][1]);
  s[2]:='nicks=';
  List.Add(@s[2][1]);
  s[3]:='price=';
  List.Add(@s[3][1]);
  s[4]:='page=';
  List.Add(@s[4][1]);
  s[5]:='size=';
  List.Add(@s[5][1]);
  s[6]:='orderby=';
  List.Add(@s[6][1]);
  s[7]:='status=';
  List.Add(@s[7][1]);
  s[8]:='post=';
  List.Add(@s[8][1]);
  s[9]:='v=1.0';
  List.Add(@s[9][1]);

  str:='';
    for i := 0 to List.Count-1 do
    begin
      str := str + AnsiString(List.Items[i]) + '&';
    end;
    Result := str;
  finally
    List.Free;
  end;
end;
在D2010下测试通过。

#21


非常感谢keiy耐心的解答,我试一下。
没用过TList,排序规则要怎么写呢?

#22


LIST排序的话先写一排序函数:
function CompareNames(Item1, Item2: Pointer): Integer; //函数名可自己定
begin
  Result :=CompareText( AnsiString(Item1), AnsiString(Item2)); //升序,如果要降序,换一下Item1, Item2
end;

然后在程序中:
  List.Sort(@CompareNames); //用CompareNames指定的函数排序

-----------------------------------------------------
单位里没有2010,你试一下,应该没问题的

#23


这个问题解决了,万分感谢keiy
还有个小问题想请教下,ParamList()得到的串我要算一个MD5,但是 UTF8Encode('明信片')中文是一个字和三个字的时候算出来的值都不符合要求,只有两个中文字的时候算出来的是符合要求的,这样的话应该是MD5算法函数上的问题吧。

#24


哦,好像不是MD5函数的问题,我用PHP和DELPHI的MD5函数分别给同一汉字编码,结果是一样的。
有点晕了,我要再看看了。。。。为什么两个汉字的就可以三个和一个的就不行呢,唉。。。

#25


居然是中文字数为单数时就不对,字数是双数时就正确。。。。

#26


D本身的MD5有算出的的确与其它的会不同。如果是你自己写的,主要还可能是widestring的问题,用AnisiString或PAnsiChar。
不行把程序贴出来看看

#27


找到问题了,是个替换字符串的函数改成AnsiString就可以了。
谢谢keiy一直以来的解答,像你这样细致耐心回答的人越来越少了,真是我们初学者的福音。

#1


我测试没问题:
uses HTTPApp;
...
procedure TForm1.Button2Click(Sender: TObject);
begin
  Edit1.text:=HTTPEncode(UTF8Encode('明信片'));
end;

结果为:
%E6%98%8E%E4%BF%A1%E7%89%87

2010+2in7

#2


检查存放变量的长度

#3


URL编码没用HTTPEncode,因为不想把url里的&和=也编码,我用的是下面这个函数,麻烦看看这个函数有什么问题呢?在Delphi7下就没问题。

function URLEncode(const S: AnsiString; const InQueryString: Boolean): AnsiString;
var
  Idx: Integer;
begin
  Result := '';
  for Idx := 1 to Length(S) do
  begin
      case S[Idx] of
        'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.', '=', '&', '%':
          Result := Result + S[Idx];
        ' ':
          if InQueryString then
            Result := Result + '+'
          else
            Result := Result + '%20';
        else
          Result := Result + '%' + SysUtils.IntToHex(Ord(S[Idx]), 2);
      end;
  end;
end;

#4


我用你的程序,在D2010+win7下也没问题。

#5


查你的程序的其它部分

#6


找到原因了,好像是TStringList的问题哦,

var
  str : AnsiString;
  List : TStringList;
begin
  List := TStringList.Create;
  try
  List.Add('q=' + UTF8Encode('明信片')); 
  ShowMessage(List.Text);


结果是:q=鏄庝俊鐗

这咋解决呀....呜....

#7


q=鏄庝俊鐗 
这个没问题,utf8在系统上是要乱码的

#8


不对呀,这个不是“明信片”的UTF8编码呀,放解码器里看是不对的,把“鏄庝俊鐗”最后一个“鐗”去掉就是“明信”两个字。

#9


呜~是什么问题呀!!

#10


可能的问题是List.Text在2010下是unicode的,你将它转成AnsiString再试试(我单位没有2010)

#11


“明信片”编码后正确的应该是:%E6%98%8E%E4%BF%A1%E7%89%87

可是用下面的代码得到的结果是:
q=%E6%98%8E%E4%BF%A1%E7%89%00&cid=&nicks=&price=&page=&size=&orderby=&status=&post=&v=1.0&

怎么解决这个问题呢?


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function URLEncode(const S: AnsiString; const InQueryString: Boolean): AnsiString;
var
  Idx: Integer;
begin
  Result := '';
  for Idx := 1 to Length(S) do
  begin
      case S[Idx] of
        'A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.', '=', '&', '%':
          Result := Result + S[Idx];
        ' ':
          if InQueryString then
            Result := Result + '+'
          else
            Result := Result + '%20';
        else
          Result := Result + '%' + SysUtils.IntToHex(Ord(S[Idx]), 2);
      end;
  end;
end;

function ParamList():AnsiString;
var
  str : AnsiString;
  i : Integer;
  List : TStringList;
begin
  Result := '';
  List := TStringList.Create;
  try
    List.Add('q=' + UTF8Encode('明信片'));
    List.Add('cid=');
    List.Add('nicks=');
    List.Add('price=');
    List.Add('page=');
    List.Add('size=');
    List.Add('orderby=');
    List.Add('status=');
    List.Add('post=');
    List.Add('v=1.0');
    for i := 0 to List.Count-1 do
    begin
      str := str + List.Strings[i] + '&';
    end;
    Result := str;
  finally
    List.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Text := URLEncode(ParamList(),true);
end;

end.

#12


等答案。。。

#13


经测试,是由于LIST的TSrings引起的,它是unicode的,有非法字符生成,我的结果为:
%E6%98%8E%E4%BF%A1%E7%89%3F
%3F为?
正在研究中

#14


我也查了很久了,希望能有解决办法!!

#15


LIST的Strings为WideString类型(Unicode)不能存某些utf8字符,会引起乱码。目前我能想到的解决方案(就你的程序来讲)
1.改:
 try 
    List.Add('q=' + UTF8Encode('明信片')); 
为:
  str:='q=' + UTF8Encode('明信片'); //这个单独处理,不放入list
2.改:
 str := str + List.Strings[i] + '&'; 
为:
 str := str + AnsiString(List.Strings[i]) + '&'; //不能用WideString参与运算
-------------------
这样,在Memo中可以得到你要的:
q=%E6%98%8E%E4%BF%A1%E7%89%87cid=&nicks=&price=&page=&size=&orderby=&status=&post=&v=1.0&

#16


1中的try还是要的

#17


这样也不行,参数是要排序的。
用了:List.Sorted := True; 参数要以字母升序顺序。
如果是用数组呢,定义数组觉得有点麻烦好像。

#18


如果你一定要用list,可以这样:
function ParamList():AnsiString;
var
  str : AnsiString;
  i : Integer;
  List : TStringList;
begin
  Result := '';
  List := TStringList.Create;
  try
    List.Add('q=明信片');  //这是先不转成utf8,因为list不能存某些utf8字符
    List.Add('cid=');
    List.Add('nicks=');
    List.Add('price=');
    List.Add('page=');
    List.Add('size=');
    List.Add('orderby=');
    List.Add('status=');
    List.Add('post=');
    List.Add('v=1.0');
    for i := 0 to List.Count-1 do
    begin
      str := str + List.Strings[i] + '&';
    end;
    Result := str;
  finally
    List.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject); 
begin 
  Memo1.Text := URLEncode(UTF8Encode(ParamList()),true); //最后转,对于ascii字符utf8与unicode或ansi编码都是相同的,只有汉字不同
end; 

结果就是你要的

#19


要求是中文先UTF8编码,ParamList()返回值要去算一个MD5的,如果先URL编码算出来的MD5就跟要求不一样了。最后idhttp提交时才进行URLEncode。
如果不用TStringList还有什么办法实现么?

#20


如果不用TStringList,可以用数组,不过要繁一点,考虑到你要sort,我将数组放入一LIST中,list可用sort,当然你也可以自己写sort,有关list的sort,如果你不会用,只能明天写给你,以下是我新写的ParamList()函数:
function ParamList():AnsiString;
var
  str : AnsiString;
  i : Integer;
  List : TList;
  s:array [0..20] of AnsiString; //这里你可用动态数组

begin
  Result := '';
  List := TList.Create;
  try
  s[0]:='q=' + UTF8Encode('明信片');
  List.Add(@s[0][1]);  //LIST加入的是指针,这是注意的
  s[1]:='cid=';
  List.Add(@s[1][1]);
  s[2]:='nicks=';
  List.Add(@s[2][1]);
  s[3]:='price=';
  List.Add(@s[3][1]);
  s[4]:='page=';
  List.Add(@s[4][1]);
  s[5]:='size=';
  List.Add(@s[5][1]);
  s[6]:='orderby=';
  List.Add(@s[6][1]);
  s[7]:='status=';
  List.Add(@s[7][1]);
  s[8]:='post=';
  List.Add(@s[8][1]);
  s[9]:='v=1.0';
  List.Add(@s[9][1]);

  str:='';
    for i := 0 to List.Count-1 do
    begin
      str := str + AnsiString(List.Items[i]) + '&';
    end;
    Result := str;
  finally
    List.Free;
  end;
end;
在D2010下测试通过。

#21


非常感谢keiy耐心的解答,我试一下。
没用过TList,排序规则要怎么写呢?

#22


LIST排序的话先写一排序函数:
function CompareNames(Item1, Item2: Pointer): Integer; //函数名可自己定
begin
  Result :=CompareText( AnsiString(Item1), AnsiString(Item2)); //升序,如果要降序,换一下Item1, Item2
end;

然后在程序中:
  List.Sort(@CompareNames); //用CompareNames指定的函数排序

-----------------------------------------------------
单位里没有2010,你试一下,应该没问题的

#23


这个问题解决了,万分感谢keiy
还有个小问题想请教下,ParamList()得到的串我要算一个MD5,但是 UTF8Encode('明信片')中文是一个字和三个字的时候算出来的值都不符合要求,只有两个中文字的时候算出来的是符合要求的,这样的话应该是MD5算法函数上的问题吧。

#24


哦,好像不是MD5函数的问题,我用PHP和DELPHI的MD5函数分别给同一汉字编码,结果是一样的。
有点晕了,我要再看看了。。。。为什么两个汉字的就可以三个和一个的就不行呢,唉。。。

#25


居然是中文字数为单数时就不对,字数是双数时就正确。。。。

#26


D本身的MD5有算出的的确与其它的会不同。如果是你自己写的,主要还可能是widestring的问题,用AnisiString或PAnsiChar。
不行把程序贴出来看看

#27


找到问题了,是个替换字符串的函数改成AnsiString就可以了。
谢谢keiy一直以来的解答,像你这样细致耐心回答的人越来越少了,真是我们初学者的福音。