•  

ГлавнаяIndyЧастые вопросы по Indy → Пытаюсь сделать свои функции URLDecode и URLEncode

Создано: 17.05.2014 1:14:05 · Исправлено: 17.05.2014 1:14:05 · Прочтений: 1127

Добрый вечер! Пытаюсь сделать свои функции URLDecode и URLEncode - использование idURI из Indy 10 прибавляет 50Кб к конечному файлу. Взял эти функции из idURI.pas и осталось:
function URLDecode(ASrc: string): string;
var
  i: integer;
  ESC: string[2];
  CharCode: integer;
begin
  Result := ;    {Do not Localize}
  i := 1;
  while i <= Length(ASrc) do begin
    if ASrc[i] <> % then begin  {do not localize}
      Result := Result + ASrc[i]
    end else begin
      Inc(i); // skip the % char
      ESC := Copy(ASrc, i, 2); // Copy the escape code
      Inc(i, 1); // Then skip it.
      try
        CharCode := StrToInt($ + ESC);  {do not localize}
        Result := Result + Char(CharCode);
      except end;
    end;
    Inc(i);
  end;
end;

function CharIsInSet(const AString: string; const ACharPos: Integer; const ASet:  String): Boolean;
begin
  if ACharPos > Length(AString) then begin
    Result := False;
  end else begin
    Result := Pos(AString[ACharPos], ASet)>0;
  end;
end;

function CharRange(const AMin, AMax : Char): String;
var i : Char;
begin
  Result := ;
  for i := Amin to AMax do
  begin
    Result := Result + i;
  end;
end;

function ParamsEncode(const ASrc: string): string;
var
  i: Integer;
const
  UnsafeChars = *#%<> [];  {do not localize}
begin
  Result := ;    {Do not Localize}
  for i := 1 to Length(ASrc) do
  begin
    if ((CharIsInSet(ASrc, i, UnsafeChars)) or (not (CharIsInSet(ASrc, i, CharRange(#33,#128))))) then
    begin {do not localize}
      Result := Result + % + IntToHex(Ord(ASrc[i]), 2);  {do not localize} // (1)
    end
    else
    begin
      Result := Result + ASrc[i];
    end;
  end;
end;

class function PathEncode(const ASrc: string): string;
const
  UnsafeChars = *#%<>+ [];  {do not localize}
var
  i: Integer;
begin
  Result := ;    {Do not Localize}
  for i := 1 to Length(ASrc) do begin
    if (CharIsInSet(ASrc, i, UnsafeChars)) or (ASrc[i] >= #$80) or (ASrc[i] < #32) then begin
      Result := Result + % + IntToHex(Ord(ASrc[i]), 2);  {do not localize}
    end else begin
      Result := Result + ASrc[i];
    end;
  end;
end;

function URLEncode(const ASrc: string): string;
var i: Integer;
begin
  Result := ;
  try
    i := LastDelimiter(/, ASrc);
    Result := PathEncode(Copy(ASrc, 0, i) + ParamsEncode(Copy(ASrc, i+1, Length(ASrc)-i)));
  finally
  end;
end;

Функции Sys.IntToHex, Sys.IntToStr, IndyPos заменил на IntToHex, IntToStr, Pos. Вроде определения этих функций не отличаются от стандартных. Но, строка (1) выдает все русские символы не как %NN, а с каким-то непонятным префиксом %D1. Русская р конвертируется вообще непонятно во что. В чем моя ошибка?
Господа, подскажите, пожалуйста, где же моя ошибка. Аналогичный код в indy работает верно. Почему строка Result := Result + % + IntToHex(Ord(ASrc[i]), 2); возвращает значение вида %D1nn, а не %nn? Откуда берется D1?
>>> idURI из Indy 10 прибавляет 50Кб к конечному файлу Файлу какого размера? Если отправлять файл на 10 байт - те же 50 килобайт пришиваются, или все-таки меньше? А вы в курсе, что каждое Result:=Result+... приводит к перераспределению памяти, что очень медленно? И почему бы не почитать исходники - Indy ведь поставляется в исходниках, разве не так?
>>> Файлу какого размера? ~500Кб. >>> А вы в курсе, что каждое Result:=Result+... приводит к перераспределению памяти, что очень медленно? а как правильно? >>> И почему бы не почитать исходники - Indy ведь поставляется в исходниках, разве не так? так этот код (слегка измененный) и есть из исходников Indy. вопрос в том, что я неправильно изменил. кстати Result:=Result+... в исходниках Indy и есть
>>> а как правильно? Ну на правильность не претендую, но примерно так:
function MyURLEncode(const S:string):string;
Const UnsafeChars=[#0..#32,*,#,%,<,>,[,],#128..#255];
      HexChar=0123456789ABCDEF;
var I,J:integer;
begin
  SetLength(Result,3*Length(S)); // maximal possible length
  J:=1;For I:=1 to Length(S) do
    if S[I] in UnsafeChars then begin
      Result[J]:=%;
      Result[J+1]:=HexChar[succ(ord(S[I])shr 4)];
      Result[J+2]:=HexChar[succ(ord(S[I])and $F)];
      Inc(J,3);
    end else begin Result[J]:=S[I];Inc(J);end;
  SetLength(Result,pred(J));
end;

function MyURLDecode(const S:string):string;
const HexChar=0123456789ABCDEF;
var I,J:integer;
begin
  SetLength(Result,Length(S));
  I:=1;J:=1;while(I<=Length(S)) do begin
    if(S[I]=%)and(I+2then begin // here you must check, that 2 characters left
      Result[J]:=chr(((pred(Pos(S[I+1],HexChar)))shl 4)or(pred(Pos(S[I+2],HexChar))));
      Inc(I,2);
    end else Result[J]:=S[I];
    Inc(I);
    Inc(J);
  end;
  SetLength(Result,pred(J));
end;
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
var S:string;
begin
  S:=Превед, медвед!!! Hello, bear!!!;
  ShowMessage(S);
  S:=MyURLEncode(S);
  ShowMessage(S);
  S:=MyURLDecode(S);
  ShowMessage(S);
end;

Ну и наконец, непонятно, что же там у Вас за try...finally - код внутри исключений не вызывает.
Кстати, для Indy есть и штатный метод работы с URLEncode. Его формат: AURLString := TIdURI.URLEncode(AString); Но я им никогда не пользовался. Когда писал »вопрос КС №38145« сделал свою реализацию, а потом, когда узнал про штатный метод, на него переходить так и не стал. Но считаю нужным оповестить Вас о существовании такого метода.
спасибо огромное! про метод в indy я, естественно, знаю (код, что я привел в примере, - и есть код реализации этого метода из исходников indy). try - из оригинального кода.
Товарищи, написал следующий код для URLEncode (точнее - взял чужой и несколько изменил/дополнил). Надеюсь, Вы сможете его проверить -
function ParamsEncode(const AStr: String): String;
const
  NoConversion = [0..9,A..Z,a..z];
var
  Sp, Rp: PChar;
  ParmName: Boolean;
begin
  SetLength(Result, Length(AStr) * 3);
  Sp := PChar(AStr);
  Rp := PChar(Result);
  ParmName := False;
  while Sp^ <> #0 do
  begin
    if (Sp^ = ?) or (Sp^ = &) and (ParmName = false) then
      ParmName := true;
    if (Sp^ = =) and (ParmName = true) then
    begin
      Rp^ := Sp^;
      Inc(Rp);
      Inc(Sp);
      ParmName := false;
    end;

    if ParmName or (Sp^ in NoConversion) then
      Rp^ := Sp^
    else
    begin
      FormatBuf(Rp^, 3, %%%.2x, 6, [Ord(Sp^)]);
      Inc(Rp,2);
    end;

    Inc(Rp);
    Inc(Sp);
  end;
  SetLength(Result, Rp - PChar(Result));
end;


function URLEncode(const ASrc: string): string;
var i: Integer;
begin
  Result := ;
  try
    i := Pos(?, ASrc)-1;
    if i>0 then
      Result := Copy(ASrc, 0, i) + ParamsEncode(Copy(ASrc, i+1, Length(ASrc)-i))
    else
      Result := ASrc;
  finally
  end;
end;