StringReplace альтернативы для улучшения производительности - PullRequest
10 голосов
/ 26 сентября 2008

Я использую StringReplace для замены & gt и & lt самим символом в сгенерированном XML, как это:

StringReplace(xml.Text,'>','>',[rfReplaceAll]) ;
StringReplace(xml.Text,'&lt;','<',[rfReplaceAll]) ;

Дело в том, что слишком долго, чтобы заменить каждый случай & gt.

Вы хотите улучшить идею?

Ответы [ 8 ]

8 голосов
/ 27 сентября 2008

Если вы используете Delphi 2009, эта операция примерно в 3 раза быстрее с TStringBuilder, чем с ReplaceString. Это также безопасно для Unicode.

Я использовал текст http://www.CodeGear.com со всеми вхождениями "<" и ">", которые были изменены на "&lt;" и "&gt;" в качестве отправной точки.

Включая строковые назначения и создание / освобождение объектов, в моей системе это заняло около 25 мс и 75 мс соответственно:

function TForm1.TestStringBuilder(const aString: string): string;
var
  sb: TStringBuilder;
begin
  StartTimer;
  sb := TStringBuilder.Create;
  sb.Append(aString);
  sb.Replace('&gt;', '>');
  sb.Replace('&lt;', '<');
  Result := sb.ToString();
  FreeAndNil(sb);
  StopTimer;
end;

function TForm1.TestStringReplace(const aString: string): string;
begin
  StartTimer;
  Result := StringReplace(aString,'&gt;','>',[rfReplaceAll]) ;
  Result := StringReplace(Result,'&lt;','<',[rfReplaceAll]) ;
  StopTimer;
end;
7 голосов
/ 26 сентября 2008

Попробуйте FastStrings.pas от Питера Морриса.

6 голосов
/ 28 сентября 2008

Обязательно посмотрите страницы проекта Fastcode: http://fastcode.sourceforge.net/

Они выполнили испытание для более быстрого StringReplace (испытание Ansi StringReplace), и «победитель» оказался в 14 раз быстрее, чем Delphi RTL.

Некоторые из функций быстрого кода были включены в сам Delphi в последних версиях (я думаю, что D2007 включен), поэтому повышение производительности может существенно различаться в зависимости от используемой версии Delphi.

Как упоминалось ранее, вы действительно должны смотреть на решение на основе Unicode, если вы серьезно относитесь к обработке XML.

3 голосов
/ 26 сентября 2008

Проблема в том, что вы дважды перебираете весь размер строки (один для замены & gt; на>, а другой для замены & lt; на <). </p>

Вам следует выполнить итерацию для for и просто проверять заранее, когда вы найдете & для gt; или lt; и сделайте немедленную замену, а затем пропустите 3 символа ((g | l) t;). Таким образом, он может сделать это за пропорциональное время по отношению к размеру строки xml.Text .


Простой пример C #, так как я не знаю Delphi, но должен помочь вам получить общее представление.

String s = "&lt;xml&gt;test&lt;/xml&gt;";
char[] input = s.ToCharArray();
char[] res = new char[s.Length];
int j = 0;
for (int i = 0, count = input.Length; i < count; ++i)
{
    if (input[i] == '&')
    {
        if (i < count - 3)
        {
            if (input[i + 1] == 'l' || input[i + 1] == 'g')
            {
                if (input[i + 2] == 't' && input[i + 3] == ';')
                {
                    res[j++] = input[i + 1] == 'l' ? '<' : '>';
                    i += 3;
                    continue;
                }
            }
        }
    }

    res[j++] = input[i];
}
Console.WriteLine(new string(res, 0, j));

Это выводит:

<xml>test</xml>
2 голосов
/ 01 февраля 2017

Когда вы имеете дело с многострочными текстовыми файлами, вы можете добиться некоторой производительности, обрабатывая их построчно. Этот подход сократил время примерно на 90%, чтобы заменить XML-файл размером более 1 МБ.

procedure ReplaceMultilineString(xml: TStrings);
var
  i: Integer;
  line: String;
begin
  for i:=0 to xml.Count-1 do
  begin
    line := xml[i];
    line := StringReplace(line, '&gt;', '>', [rfReplaceAll]);
    line := StringReplace(line, '&lt;', '<', [rfReplaceAll]);
    xml[i] := line;
  end;
end;
2 голосов
/ 26 сентября 2008

Systools (Turbopower, теперь с открытым исходным кодом ) имеет функцию ReplaceStringAllL, которая делает их все в строке.

2 голосов
/ 26 сентября 2008

Непроверенное преобразование кода C #, написанного Хорхе Феррейрой.

function ReplaceLtGt(const s: string): string;
var
  inPtr, outPtr: integer;
begin
  SetLength(Result, Length(s));
  inPtr := 1;
  outPtr := 1;
  while inPtr <= Length(s) do begin
    if (s[inPtr] = '&') and ((inPtr + 3) <= Length(s)) and
       (s[inPtr+1] in ['l', 'g']) and (s[inPtr+2] = 't') and
       (s[inPtr+3] = ';') then
    begin
      if s[inPtr+1] = 'l' then
        Result[outPtr] :=  '<'
      else
        Result[outPtr] := '>';
      Inc(inPtr, 3);
    end
    else begin
      Result[outPtr] := Result[inPtr];
      Inc(inPtr);
    end;
    Inc(outPtr);
  end;
  SetLength(Result, outPtr - 1);
end;
0 голосов
/ 07 октября 2018

это работает как шарм, так быстро, верь ему

    Function NewStringReplace(const S, OldPattern, NewPattern: string;  Flags: TReplaceFlags): string;
var
  OldPat,Srch: string; // Srch and Oldp can contain uppercase versions of S,OldPattern
  PatLength,NewPatLength,P,i,PatCount,PrevP: Integer;
  c,d: pchar;
begin
  PatLength:=Length(OldPattern);
  if PatLength=0 then begin
    Result:=S;
    exit;
  end;

  if rfIgnoreCase in Flags then begin
    Srch:=AnsiUpperCase(S);
    OldPat:=AnsiUpperCase(OldPattern);
  end else begin
    Srch:=S;
    OldPat:=OldPattern;
  end;

  PatLength:=Length(OldPat);
  if Length(NewPattern)=PatLength then begin
    //Result length will not change
    Result:=S;
    P:=1;
    repeat
      P:=PosEx(OldPat,Srch,P);
      if P>0 then begin
        for i:=1 to PatLength do
          Result[P+i-1]:=NewPattern[i];
        if not (rfReplaceAll in Flags) then exit;
        inc(P,PatLength);
      end;
    until p=0;
  end else begin
    //Different pattern length -> Result length will change
    //To avoid creating a lot of temporary strings, we count how many
    //replacements we're going to make.
    P:=1; PatCount:=0;
    repeat
      P:=PosEx(OldPat,Srch,P);
      if P>0 then begin
        inc(P,PatLength);
        inc(PatCount);
        if not (rfReplaceAll in Flags) then break;
      end;
    until p=0;
    if PatCount=0 then begin
      Result:=S;
      exit;
    end;
    NewPatLength:=Length(NewPattern);
    SetLength(Result,Length(S)+PatCount*(NewPatLength-PatLength));
    P:=1; PrevP:=0;
    c:=pchar(Result); d:=pchar(S);
    repeat
      P:=PosEx(OldPat,Srch,P);
      if P>0 then begin
        for i:=PrevP+1 to P-1 do begin
          c^:=d^;
          inc(c); inc(d);
        end;
        for i:=1 to NewPatLength do begin
          c^:=NewPattern[i];
          inc(c);
        end;
        if not (rfReplaceAll in Flags) then exit;
        inc(P,PatLength);
        inc(d,PatLength);
        PrevP:=P-1;
      end else begin
        for i:=PrevP+1 to Length(S) do begin
          c^:=d^;
          inc(c); inc(d);
        end;
      end;
    until p=0;
  end;
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...