Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Stringverarbeitung - Leerzeichen reduzieren (https://www.delphipraxis.net/120550-stringverarbeitung-leerzeichen-reduzieren.html)

Tyrael Y. 12. Sep 2008 14:42


Stringverarbeitung - Leerzeichen reduzieren
 
Hallo zusammen,

ich habe eine Stringverabeitungsfunktion geschrieben, die mehrere aufeinanderfolgende
Leerzeichen innerhalb eines Strings zu einem Leerzeichen reduziert und somit den
String neu aufbaut.

Diese Funktion benutze ich im Zusammhang mit Daten aus einer Datenbank.
Die Funktion wird sehr oft aufgerufen. Es läuft auch gut.
Ich versuche gerade in der Gesamtverarbeitung Performace zu gewinnen.

Hat einer eine Idee, wie ich diese Funktion noch schneller realisieren könnte?

Speicherauslastung spielt keine Rolle, Geschwindigkeit ist primär.

Delphi-Quellcode:
function DeleteBlanksFromStr(const AString: string): string;

  function DeleteBlanks(AStr: string): string;
  var i,
    LIndex,
    LCount: Integer;
    LStr,
    LString: string;
  begin
    //prüfen ob innerhalb des Strings noch zwei aufeinander folgende Leerzeichen sind,
    //wenn keine vorhanden, keine weitere Verarbeitung nötig
    result := AStr;
    LIndex := Pos(' ', AStr);
    if (LIndex > 0) then
    begin
      //falls den leerzeichen ein weiteres folgt prüfen, ob weitere folgen
      i := LIndex + 2;
      while (AStr[i] = ' ') do
      begin
        inc(i);
      end;

      //wenn i sich verändert hat, gab es weitere folgende leerzeichen
          //if not (i = LIndex + 2) then  <- dies würde dazu führen, daß zwei leerzeichen hintereinander nicht
          //begin                            verarbeitet werden
        LStr := Copy(AStr, 1 , LIndex);

        LString := Copy(AStr, i, Length(AStr));
        result := LStr +  DeleteBlanks(LString);
         //end;
    end;

  end;

begin
  //Leerzeichen ganz vor und ganz hinten streichen
  result := Trim(AString);

  //alle leerzeichen innerhalb des strings verarbeiten
  result := DeleteBlanks(result);
end;

Danke fürs Lesen.

jfheins 12. Sep 2008 15:03

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Ich würde es so machen:

2 Indizies, einer für den AusgangsString, einer für den Endstring.

Ein bisschen C-like Pseudocode:

Code:
Setlength(Result, length(AString));
j = 0;
for(i=0; i<length(AString); i++)
  if (AString[i] != ' ')
  {
    Result[j] = AString[i];
    inc(j);
  }
setlength(Result, j);
Das sollte es dann sein ;)

Deutlich weniger Funktionsaufrufe und Seicherschieberei ;)

Edits: So, jetzt müssts jeder verstehn, was ich meine ^^

Oh mann, jetzt hab ich gar nicht gelesen was er überhaupt wollte :wall:
Muss nochmal überlegen :roll:

Tyrael Y. 12. Sep 2008 15:10

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Also die leichteste Lösung ist natürlich eine einfache Schleife, aber sie kann nicht die schnellste Lösung sein, da Strings ohne mehrere Leerzeichen hintereinander auch verabeitet werde.

Ich werde aber mal einen anderen Ansatz testen, ob es schneller als meine oben geschriebene Variante ist.

Eine Schleife mit StringReplace die alle ' ' (zwei Leerzeichen) ducrh ein Leerzeichen ersetzt, bis Pos() keine ' ' (zwei Leerzeichen) mehr findet.

jfheins 12. Sep 2008 15:24

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Soo, hier nochmnal das was du wolltest (Nur Leerzeichen löschen, die doppelt sindd, oder vorne oder hinten)

Code:
Setlength(Result, length(AString));
j = 1;
SpaceBefore = true;
for(i=1; i<=length(AString); i++)
{
  if (not (AString[i] == ' ' and SpaceBefore))
  { 
    Result[j] = AString[i];
    inc(j);
  } 
 
  SpaceBefore = AString[i] == ' ';
}
if (AString[length(AString)] == ' ')
  j = j - 1;
setlength(Result, j-1);
Sollte nicht allzuschwer sein, das nacxh Delphi umzuschreiben.


Zitat:

Zitat von Tyrael Y.
Also die leichteste Lösung ist natürlich eine einfache Schleife, aber sie kann nicht die schnellste Lösung sein, da Strings ohne mehrere Leerzeichen hintereinander auch verabeitet werde.

Warum nicht? KISS-Prinzip ;)
Ja und? ddann werden eben schöne String auch einmal kopiert.
Wenn du vorher mit pos() prüfst, muss der String ja auch einmal durchgegangen werden. Mal ganz von deinen zig maligen copy()-Aufrufen abgesehen.

Zitat:

Eine Schleife mit StringReplace die alle ' ' (zwei Leerzeichen) ducrh ein Leerzeichen ersetzt, bis Pos() keine ' ' (zwei Leerzeichen) mehr findet.
Also eine Schleife, die den ganzen String durchgeht, um zwei Leerzeichen zu suchen, wenn gefunden, wieder den ganzen String durchgehen, um 2 Leerzeichen durch 1 zu ersetzen, und das in einer Schleife????

Da ist meine Lösung imho um einen Faktor 2-5 schneller ;)

nahpets 12. Sep 2008 15:40

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Hallo,

wär' das was?

Delphi-Quellcode:
function DeleteBlanksFromStr(const AString: string): string;
Var
          i   : Integer;
          iLen : Integer;
          iPos : Integer;
          s   : String;
begin
  s     := Trim(AString);
  iPos  := Pos(' ',s);
  if iPos = 0 then begin
    Result := s;
    exit;
  end;
  iLen  := Length(s);
  Result := Copy(s,1,iPos - 1);
  for i := iPos to iLen do begin
    case s[i - 1] of
      ' ' : case s[i] of
             ' ' : ;
            else
              Result := Result + s[i];
            end;
    else
      Result := Result + s[i];
    end;
  end;
end;
Stephan

Sir Rufo 12. Sep 2008 15:53

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Ja oder vielleicht so:

Delphi-Quellcode:
function DeleteBlanksStr( const Value : string ) : string;
begin
  RESULT := Value;
  while Pos( ' ', RESULT ) > 0 do
    RESULT := {SysUtils.}StringReplace( RESULT, ' ', ' ', [ rfReplaceAll ] );
end;
Geht auf jeden Fall in Delphi 2007

Denn in der Kürze liegt die Würze :mrgreen:

cu

Oliver

DeddyH 12. Sep 2008 16:06

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Noch' n Vorschlag:
Delphi-Quellcode:
function DeleteDoubleBlanks(const src: string): string;
var i,j: integer;
begin
  SetLength(Result,Length(src));
  if Length(Result) > 0 then
    begin
      i := 1;
      j := 1;
      while i <= Length(src) do
        begin
          Result[j] := src[i];
          if (src[i] = #32) then
            begin
              while (i <= Length(src)) and (src[i] = #32) do
                inc(i);
            end
          else
            inc(i);
          inc(j);
        end;
      SetLength(Result,j);
    end;
end;

jfheins 12. Sep 2008 16:22

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Ich glaube, jetzt sindd wir an einem Punkt, wo ein quantitativer Vergleich hilfreich wäre :mrgreen:

Ausserdem wäre es interessant, wie die Daten aussehen - also ob im Normalfall alles in Ordung ist, und nur in Ausnahmefällen überhaupt etwas gemacht werden muss, oder ob die doppelten Leerzeichen fast immer vorhanden sind ;)

Sir Rufo 12. Sep 2008 18:16

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Hab ich mal gemacht ...
Delphi-Quellcode:
function DeleteBlanksFromStr1( const AString : string ) : string;

  function DeleteBlanks(AStr: string): string;
  var i,
    LIndex,
    LCount: Integer;
    LStr,
    LString: string;
  begin
    //prüfen ob innerhalb des Strings noch zwei aufeinander folgende Leerzeichen sind,
    //wenn keine vorhanden, keine weitere Verarbeitung nötig
    result := AStr;
    LIndex := Pos(' ', AStr);
    if (LIndex > 0) then
    begin
      //falls den leerzeichen ein weiteres folgt prüfen, ob weitere folgen
      i := LIndex + 2;
      while (AStr[i] = ' ') do
      begin
        inc(i);
      end;

      //wenn i sich verändert hat, gab es weitere folgende leerzeichen
          //if not (i = LIndex + 2) then  <- dies würde dazu führen, daß zwei leerzeichen hintereinander nicht
          //begin                            verarbeitet werden
        LStr := Copy(AStr, 1 , LIndex);

        LString := Copy(AStr, i, Length(AStr));
        result := LStr +  DeleteBlanks(LString);
         //end;
    end;

  end;

begin
  //Leerzeichen ganz vor und ganz hinten streichen
  result := Trim(AString);

  //alle leerzeichen innerhalb des strings verarbeiten
  result := DeleteBlanks(result);
end;

function DeleteBlanksFromStr2( const AString : string ) : string;
Var
  i   : Integer;
  iLen : Integer;
  iPos : Integer;
  s   : String;
begin
  s     := Trim(AString);
  iPos  := Pos(' ',s);
  if iPos = 0 then begin
    Result := s;
    exit;
  end;
  iLen  := Length(s);
  Result := Copy(s,1,iPos - 1);
  for i := iPos to iLen do begin
    case s[i - 1] of
      ' ' : case s[i] of
             ' ' : ;
            else
              Result := Result + s[i];
            end;
    else
      Result := Result + s[i];
    end;
  end;
end;

function DeleteBlanksFromStr3( const AString : string ) : string;
begin
  RESULT := AString;
  while Pos( ' ', RESULT ) > 0 do
    RESULT := {SysUtils.}StringReplace( RESULT, ' ', ' ', [ rfReplaceAll ] );
end;

function DeleteBlanksFromStr4( const AString : string ) : string;
var i,j: integer;
begin
  SetLength(Result,Length(AString));
  if Length(Result) > 0 then
    begin
      i := 1;
      j := 1;
      while i <= Length(AString) do
        begin
          Result[j] := AString[i];
          if (AString[i] = #32) then
            begin
              while (i <= Length(AString)) and (AString[i] = #32) do
                inc(i);
            end
          else
            inc(i);
          inc(j);
        end;
      SetLength(Result,j);
    end;
end;
... und hier die Ergebnisse für jeweils 1.000.000 Durchläufe für den Text
Delphi-Quellcode:
"Peter                     und                        der                           Wolf"
1. ca. 3094 ms/1000
2. ca. 2900 ms/1000
3. ca. 1275 ms/1000 *** allerdings nur 10.000 Durchläufe *** :oops:
4. ca. 900 ms/1000

Ich sach ja schon nix mehr :wink:

cu

Oliver

taaktaak 12. Sep 2008 18:28

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Interessant!
Aber warum bei Variante 3 weniger Durchläufe?
Jetzt fehlt vielleicht noch Detlefs Version mit Zeigern...

Sir Rufo 12. Sep 2008 18:37

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Zitat:

Zitat von taaktaak
Aber warum bei Variante 3 weniger Durchläufe?

Weil wir Weihnachten zeitig Essen, da hatte ich nicht so viel Zeit

alzaimar 12. Sep 2008 18:57

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Performancetuning, au fein...
Hatten wir mal :mrgreen: hier

Hier die Version von Hawkeye219, die alles in den Schatten stellt.
Delphi-Quellcode:
function RemoveDoubleBlanks(const s: string): string;
var
  pRead, pEnd, pWrite : PChar;
begin
  pRead := PChar(s);
  pEnd := pRead + Length(s);

  SetLength (Result, Length(s));
  pWrite := PChar(Result);

  while (pRead <> pEnd) do
    begin

      repeat
        pWrite^ := pRead^;
        Inc (pRead);
        Inc (pWrite);
      until ((pRead[-1] = ' ') or (pRead = pEnd));

      while (pRead^ = ' ') do
        Inc (pRead);

    end;

  SetLength (Result, pWrite - PChar(Result));
end;

Tyrael Y. 13. Sep 2008 01:19

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Uiii, hat sich ja ne Menge getan im Thread.

Danke allen für die vielen Vorschläge.
Ich werde jeden einzelnen Vorschlag intensiv testen, leider erst wieder am Montag,
über das Wochenende bin ich anderweitig voll beschäftigt.

Die Zeiten der Performancetests werde ich natürlich hier posten.

Wer mag kann ja noch weitere Vorschläge posten, ich werde auf jeden Fall
jeden Vorschlag in den test einbeziehen.


P.S.: @alzaimer, wäre es nicht besser wenigtens eine Prüfung mit pos() durchzuführen, um
zu schauen, ob der Teilstring überhaupt vorkommt, bevor man den gesamten String Zeichen für Zeichen durchgeht?
Aber auch diese Möglichkeit werde ich dann gleich mit testen.


Danke allen für die rege Beteiligung.

alzaimar 13. Sep 2008 07:13

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Zitat:

Zitat von Tyrael Y.
P.S.: @alzaimer, wäre es nicht besser wenigtens eine Prüfung mit pos() durchzuführen,

Ausprobieren! :mrgreen: Ich tippe: Nein. Weil das Kopieren kaum Zeit verbrät, die Schleife mit dem Suchen nach Leerzeichen schon. Und das müsste Pos auch machen. Zudem ist Pos grottenschlecht implementiert.

Tyrael Y. 15. Sep 2008 08:54

Re: Stringverarbeitung - Leerzeichen reduzieren
 
Guten Morgen zusammen,
ich habe alle Vorschläge quantitativ überprüft.

Ich bin dabei folgendermassen vorgegangen.
Ich habe eine Testapplikation erstellt, in der ich jede Funktion implementiert habe.
Jede Funktion habe ich einzeln getestet, damit ich "Störeffekte" ausschliessen konnte.

Auf einer Form befinden sich zwei TEdit´s ein TButton und ein TLabel.
Der Teststring in unveränderter Form wird im ersten Edit angezeigt.
Das Ergebnis aus der jeweiligen Funktion, nur zur Prüfung, ob die Funktion richtig läuft, in dem zweiten Edit.
Der Knopf startet die Prüfung.
Am Ende der Prüfung wird im Label die Zeit in ms die gebraucht wurde angezeigt.

Ich habe zwei Teststrings benutzt einmal eine mit vielen Leerzeichen.
Und für die zweite Prüfreihe einen String in der keine zwei Leerzeichen aufeinander folgen.

In der Datenbank sind etwa 20% Strings die zwei oder mehr Leerzeichen, die aufeinander folgen
enthalten. Für etwa 80% der Strings wird diese Funktion demnach nichts machen müssen.


Jede Testreihe habe ich drei mal ausgeführt.


Delphi-Quellcode:
implementation

const C_TEST_STR = 'Dies ist               ein            Test. Hund        Katze           Maus         Haus        Garten   Sonne';
      C_TEST_NB = 'Dies ist ein Test. Hund Katze Maus Haus Garten Sonne';


function DeleteBlanksFromStr_A(const AString: string): string;
begin
...
end;

...

function DeleteBlanksFromStr_G(const AString: string): string;
begin
...
end;


procedure TForm1.Button1Click(Sender: TObject);
const C_COUNT = 1000000;

var LBegin,
    LEnd: int64;
    i: integer;
begin
  Edit1.Text := C_TEST_NB;


  LBegin := GetTickCount;
  try
    for i := 1 to C_COUNT do
    begin
      Edit2.Text := DeleteBlanksFromStr_D(Edit1.Text);
    end;

    LEnd := GetTickCount;
  finally
    Label1.Caption := IntToStr(Lend- Lbegin);
  end;
end;

Hier die Ergebnisse:

String mit zwei oder mehr aufeinanderfolgenden Leerzeichen.
Tyrael Y: 26313 / 26360 / 26328
jfheins : 18656 / 18860 / 18547
nahpets : 23359 / 23344 / 23360
Sir Rufo: 91390 / 91360 / 91219
DeddyH : 18563 / 18547 / 18484
alzaimer_1: 18031 / 18062 / 17969
alzaimer_2: 17953 / 17860 / 17922

String ohne zwei oder mehr aufeinanderfolgenden Leerzeichen.
Tyrael Y: 17750 / 17578 / 17656
jfheins : 17547 / 17469 / 17531
nahpets : 17344 / 17297 / 17391
Sir Rufo: 17125 / 17016 / 17156
DeddyH : 17844 / 17859 / 17843
alzaimer_1: 17484 / 17687 / 17453
alzaimer_2: 17031 / 17062 / 17047



alzaimer_2 ist folgende Ergänzung zum Ursprungscode

Delphi-Quellcode:
function DeleteBlanksFromStr_G(const s: string): string;
var
  pRead, pEnd, pWrite : PChar;
begin
  if Pos(' ', s) > 0 then
  begin

    pRead := PChar(s);
    pEnd := pRead + Length(s);

    SetLength (Result, Length(s));
    pWrite := PChar(Result);

    while (pRead <> pEnd) do
      begin

        repeat
          pWrite^ := pRead^;
          Inc (pRead);
          Inc (pWrite);
        until ((pRead[-1] = ' ') or (pRead = pEnd));

        while (pRead^ = ' ') do
          Inc (pRead);

      end;

    SetLength (Result, pWrite - PChar(Result));
  end
  else
  begin
    result := s;
  end;
end;

Die zusätzliche Prüfung mit Pos scheint nicht wirklich einen großen Unterschied zu machen.
Letzendlich ist es aber über alle Versuche gesehen in allen Fällen die schnellste Variante.

Diese Variante werde ich jetzt auch einsetzen.


Danke allen nochmal für die rege Beteiligung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:47 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz