Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Neuen Beitrag zur Code-Library hinzufügen (https://www.delphipraxis.net/33-neuen-beitrag-zur-code-library-hinzufuegen/)
-   -   Delphi PosExUltra - Ultimative Stringsuche/Parser (https://www.delphipraxis.net/148055-posexultra-ultimative-stringsuche-parser.html)

Novo 21. Feb 2010 15:19


PosExUltra - Ultimative Stringsuche/Parser
 
Wenn ihr schon immer mal eine Funktion gebraucht habt, die euch Webseiten und große Texte parsen/filtern/auslesen kann,
OHNE dass ihr ewig euch mit Pos und PosEx rumärgern müsst, dann hat eure Suche ein Ende!

PS:
Für Verbesserungen, optimierungen wären wir sehr Dankbar!

Delphi-Quellcode:
{
* --------------------------------------------------------------------------------------
* "THE BEER-WARE LICENSE"
* YaNNiC und Novo schrieben diese Datei. Solange Sie diesen Vermerk nicht entfernen,
* können Sie mit der Datei machen, was Sie möchten. Wenn wir uns eines Tages treffen
* und Sie denken, die Datei ist es wert, können Sie mir dafür ein Bier ausgeben.
* Wir hatten auf jeden Fall genug davon, als wir diese Funktion geschrieben haben :D
* --------------------------------------------------------------------------------------
}

procedure PosExUltra(source, searchwordbefore, searchwordafter, addbefore, addafter: string; continuesearch, reversesearch: boolean; liste: Tstringlist);
var
  Anfang, Ende, Posi: Integer;
  Ergebnis: string;
begin
  if reversesearch then
  begin //if reversesearch = true:
    if continuesearch then
    begin //if continuesearch = true:
      Ende := 0;
      while PosEx(searchwordafter, source, ende) > 0 do
      begin
        Anfang := PosEX(searchwordafter, source, Ende);
        Posi := Anfang;
        if Anfang > 0 then
        begin
          while (PosEX(searchwordbefore, source, Posi) = 0) or (PosEX(searchwordbefore, source, Posi) > Posi) do
          begin
            Dec(Posi);
          end;
          Ergebnis := Copy(source, Posi + length(searchwordbefore), Anfang - (Posi + length(searchwordbefore)));
          if (Ergebnis <> '') or (Ergebnis <> ' ') then liste.Add(addbefore + Ergebnis + addafter);
          Ende := Posi + length(searchwordbefore) + Length(Ergebnis) + Length(searchwordafter);
        end;
      end;
    end
    else //if continuesearch = false:
    begin
      Ende := 0;
      Anfang := PosEX(searchwordafter, source, Ende);
      Posi := Anfang;
      if Anfang > 0 then
      begin
        while (PosEX(searchwordbefore, source, Posi) = 0) or (PosEX(searchwordbefore, source, Posi) > Posi) do
        begin
          Dec(Posi);
        end;
        Ergebnis := Copy(source, Posi + length(searchwordbefore), Anfang - (Posi + length(searchwordbefore)));
        if (Ergebnis <> '') or (Ergebnis <> ' ') then liste.Add(addbefore + Ergebnis + addafter);
      end;
    end;
  end
  else //if reversesearch = false:
  begin
    if continuesearch then
    begin //if continuesearch = true:
      Ende := 0;
      Anfang := PosEX(searchwordbefore, source, Ende);
      while Anfang > 0 do
      begin
        Anfang := PosEX(searchwordbefore, source, Ende);
        if Anfang > 0 then
        begin
          Ende := PosEX(searchwordafter, source, Anfang);
          Ergebnis := Copy(source, Anfang + length(searchwordbefore), (Ende - Anfang) - length(searchwordbefore));
          if (Ergebnis <> '') or (Ergebnis <> ' ') then liste.Add(addbefore + Ergebnis + addafter);
          Anfang := Anfang + length(Ergebnis);
        end;
      end;
    end
    else //if continuesearch = false:
    begin
      Ende := 0;
      Anfang := PosEX(searchwordbefore, source, Ende);
      if Anfang > 0 then
      begin
        Ende := PosEX(searchwordafter, source, Anfang);
        Ergebnis := Copy(source, Anfang + length(searchwordbefore), (Ende - Anfang) - length(searchwordbefore));
        if (Ergebnis <> '') or (Ergebnis <> ' ') then liste.Add(addbefore + Ergebnis + addafter);
      end;
    end;
  end;
end;
Erklärung:
Code:
procedure PosExUltra(source, searchwordbefore, searchwordafter, addbefore, addafter: string; continuesearch, reversesearch: boolean; liste: Tstringlist);

In "source" befindet sich der große Text, den man Filtern möchte.
"searchwordbefore" ist der Text, der sich VOR dem Wort was man will befindet.
"searchwordafter" ist der Text, der sich NACH dem Wort was man will befindet.
"addbefore" ist der Text, den man alternativ VOR dem GEFUNDENEN Wort anhängt.
"addafter" ist der Text, den man alternativ NACH dem GEFUNDENEN Wort anhängt.
"continuesearch": Es durchsucht den gesamten Text, wenn es MEHRERE Ergebnisse gibt (z.B. ALLE URLS),
bei False bricht es nach dem 1. Fund ab.
"reversesearch": Wenn "searchwordbefore" zu klein oder zu allgemein (zu oft vorkommend, oder gar variabel ist),
dann sucht es erst nach "searchwordafter" und geht dann rückwärts, bis er "searchwordbefore" gefunden hat.
Bsp: <ID=20>TEXT1<hierimmergleich> <ID=25>TEXT2<hierimmergleich> <ID=30>TEXT3<hierimmergleich> und man möchte alle!
How To Use:
Delphi-Quellcode:
  uses ... StrUtils;

  public
    stringlist: TStringList;

---

  stringlist := TStringList.Create;
  source := 'groooßer Text mit Inhalt was ich will';

  // wahlweise Absätze und Leerzeichen entfernen, um ein Besseres Ergenbis zu bekommen oder
  // weiteren Text zur Filterung zu erhalten:
  //source := StringReplace(source, ''#$A'', '', [rfReplaceAll]);
  //source := StringReplace(source, ''#13'', '', [rfReplaceAll]);
  //source := StringReplace(source, ' ', '', [rfReplaceAll]);

                                          //continue, reverse
  PosExUltra(source, 'VORDEMTEXT', 'NACHDEMTEXT', '', '', true, false, stringlist);
 
  stringlist.free;
Beispiele:

Einfache Vorwärtssuche bei eindeutigen Worten:
Delphi-Quellcode:
source := '<text>DASWILLICH</text>';
PosExUltra(source, '<text>', '</text>', '', '', true, false, stringlist);
//Suchwörter für Anfang und Ende sind leicht Filterbar, man kann vorwärts suchen.
Man möchte alle URL Links, die mit .com aufhören haben:
Delphi-Quellcode:
source := 'blatext xD [url]http://imagexxx.com[/url] und weiter text';
PosExUltra(source, 'http://', '.com', 'http://', '.com', true, false, stringlist);
//Suchwörter für Anfang und Ende sind leicht Filterbar, man kann vorwärts suchen.
//Da "http://" und ".com" abgeschnitten werden, fügen wir das dem gefundenen Wort nacher wieder an.
Schwierige Stelle zum Suchen: Die Rückwärtssuche!

Nehmen wir an, der Text wäre:
Code:
source :=
<td>[url="?content=dictionary&amp;task=term&amp;dir=bla&amp;ID=456"]DASWILLICH[/url]</td>
      <td style="line-height: 6pt;">
Der Anfangs und End Suchwert wäre:
Anfang: '">'
Ende: '</a></td>'
Das würde nicht funktionieren, da Anfang SEHR häufig vorkommt.
Also geht man zuerst so vor:

Delphi-Quellcode:
source := StringReplace(source, ''#$A'', '', [rfReplaceAll]);
source := StringReplace(source, ''#13'', '', [rfReplaceAll]);
source := StringReplace(source, ' ', '', [rfReplaceAll]);
Das würde dann so aussehen:
Code:
source := <td>[url="?content=dictionary&amp;task=term&amp;dir=bla&amp;ID=456"]DASWILLICH[/url]</td><tdstyle="line-height:6pt;">
nun kann man einfach die Rückwärtssuche bemühen, sie sucht zuerst nach Ende, und geht dann rückwärts bis er Anfang findet:

Delphi-Quellcode:
source := '<td>[url="?content=dictionary&amp;task=term&amp;dir=bla&amp;ID=456"]DASWILLICH[/url]</td><tdstyle="line-height:6pt;">';
PosExUltra(source, '">', '</a></td><tdstyle="line-height:6pt', '', '', true, true, stringlist);


Mfg Novo

himitsu 21. Feb 2010 15:32

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Sowas wie
Delphi-Quellcode:
if reversesearch = true then
bitte durch
Delphi-Quellcode:
if reversesearch then
ersetzen (die Gründe wurden schon oft im Forum erwähnt)

Und der Name ist definitiv verwirrend, denn gesucht wird hier nix, also nicht entsprechend des Funktionsergebnisses.
StringReplaceUltra wäre da schon besser.

Novo 21. Feb 2010 15:37

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von himitsu
Sowas wie
Delphi-Quellcode:
if reversesearch = true then
bitte durch
Delphi-Quellcode:
if reversesearch then
ersetzen (die Gründe wurden schon oft im Forum erwähnt)

Was ist denn der Grund?

Zitat:

Zitat von himitsu
Und der Name ist definitiv verwirrend, dann ggesucht wird hier nix, also nicht entsprechend des Funktionsergebnisses.
StringReplaceUltra wäre da schon besser.

Hm?
Klar wird hier gesucht!
ist sozusagen eine bessere PosEx -> PosExUltra :)

Matze 21. Feb 2010 15:40

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Jupp, "StringReplace" wäre falsch. Es wird zwar nicht die Position zurückgegeben (also doch eher kein "PosEx"), aber es wird gesucht.

Zitat:

Zitat von Novo
Was ist denn der Grund?

Über den Umgang mit Boolean

Deine Funktion sieht viel versprechend aus bzw. was sie alles kann. Kannst du etwas über die Performance sagen, da du schreibst, sie sei für große Texte geeignet?
Es gibt zahlreiche, schnelle Suchalgorithmen (Rabin-Karp & Co.), aber du verwendest keinen davon, wenn ich das richtig sehe.

alzaimar 21. Feb 2010 15:43

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Das ist eine nette Funktion. Nenne sie doch lieber
Delphi-Quellcode:
Function PosEierlegendeWollmilchSau
Der Zusatz 'Ultra' erinnert so an Waschmittel.

Im Ernst: Was kann die Funktion, was ein TRegExpr nicht kann?

Novo 21. Feb 2010 15:45

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von Matze
Deine Funktion sieht viel versprechend aus bzw. was sie alles kann. Kannst du etwas über die Performance sagen, da du schreibst, sie sei für große Texte geeignet?
Es gibt zahlreiche, schnelle Suchalgorithmen (Rabin-Karp & Co.), aber du verwendest keinen davon, wenn ich das richtig sehe.

Nunja die Performance hängt ganz nach dem Aufbau der suchquelle ab, ob du vorwärts oder Rückwärts suchen musst, und natürlich auch wie viele Zeichen dein searchwordbefore und searchwordafter hat.

Ein Beispiel:

http://dict-navi.com/api.php
800 Wörter -> Vorwärts -> SAUSCHNELL
( ja ich weis ist XML, dazu gibts ne XML Komponente, die bekomm ich aber partout nicht zum laufen :( FAIL )

http://dict-navi.com/?content=dictio...=list&type=all
Das gleiche mit ebenfalls 800 Wörter -> Rückwärtssuche nötig -> 30 Sekunden lang 25% CPU auslastung :D

Sind aber schon heftig viele Zeilen.

Wenn man z.B. Quellcode von Youtube parst, um die Video ID zu finden, ist das ruckzuck!


Zitat:

Zitat von alzaimar
Im Ernst: Was kann die Funktion, was ein TRegExpr nicht kann?

ähm z.B. das einfache und komfortable universelle einsetzen, OHNE Regular Expression Regeln zu kennen?

himitsu 21. Feb 2010 15:52

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Ich hätte aer noch was:
Delphi-Quellcode:
Application.ProcessMessages;
Dieses hat in soeiner Funktion besser nichts drin zu suchen.

Novo 21. Feb 2010 15:53

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von himitsu
Ich hätte aer noch was:
Delphi-Quellcode:
Application.ProcessMessages;
Dieses hat in soeiner Funktion besser nichts drin zu suchen.

ok dann mach ich sie raus, aber dann freezed halt bei großen Texten solang die Form, das ist auch nicht schön.

Namenloser 21. Feb 2010 15:56

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von Novo
ok dann mach ich sie raus, aber dann freezed halt bei großen Texten solang die Form, das ist auch nicht schön.

Die sauberere Lösung wäre in dem Fall eh, die Funktion in einem zweiten Thread auszuführen.

Novo 21. Feb 2010 16:00

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von NamenLozer
Die sauberere Lösung wäre in dem Fall eh, die Funktion in einem zweiten Thread auszuführen.

Das einzige, was ich je Threaded gemacht habe, war ein idhhttp.get :D
Mal schaun, ob das denn funktioniert, bin mal weiter am Rumbasteln.
Falls du es schon kannst, codeschnipsel :)

Matze 21. Feb 2010 16:24

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Ich würde die genannte Zeile einfach löschen. Der, der deine Funktion nutzen möchte, soll diese selbst in einen Thread auslagern. Da musst du nichts basteln. ;)

Novo 21. Feb 2010 17:39

Re: PosExUltra - Ultimative Stringsuche/Parser
 
ok habs mal verbessert.

himitsu 21. Feb 2010 18:21

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Wenn eine Funktion oft sehr lange dauert:
Was du da machen könntest, wäre eine alternativer Parameter, bei welchem man eine Callback-Funktion angeben kann ... dort könnte der Benutzer deiner Funktion dann selber entscheiden, wie er dieses bei sich behandelt.

Wobei man bei sowas eigentlich Funktion besser in Threads auslagert.

PS: Wenn in deinem Code auf Application zugegriffen würde, dann könnte man diese Funktion nicht in einen Thread auslagern.
Allgemein sollte bei derartigen Funktionen sowieso nicht auf Externes zugegriffen werden (dazu zählen Application oder eine Form).

alzaimar 21. Feb 2010 20:19

Re: PosExUltra - Ultimative Stringsuche/Parser
 
So, ich hab mich auch mal versucht und kurz getestet.
Delphi-Quellcode:
Procedure ExtractBetween (Const aSource, aPrefix, aSuffix : String;
  aFindAll : Boolean; aNewPrefix, aNewSuffix : String; aWords : TStrings);
Var
  PrefixLength, PosPrefix, PosSuffix, BestPos : Integer;
Begin
  PosPrefix := PosEx (aPrefix, aSource, 1);
  if PosPrefix=0 then exit;
  PrefixLength := Length (aPrefix);
  Repeat
    PosSuffix := PosEx (aSuffix, aSource, PosPrefix + PrefixLength);
    if PosSuffix=0 then Exit;

    While (PosPrefix<>0) and (PosPrefix<PosSuffix) Do Begin
      BestPos := PosPrefix;
      PosPrefix := PosEx (aPrefix, aSource, PosPrefix + PrefixLength);
    End;
    sWords.Add (aNewPrefix + Copy (aSource, BestPos+PrefixLength,PosSuffix-BestPos-PrefixLength) + aNewSuffix);
    If PosPrefix=0 then exit;
  Until not aFindall;
End;
Die ganzen Sonderfälle (leere Suchstrings usw) habe ich nicht abgefangen.

omata 21. Feb 2010 20:44

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Benötigte Unit: StrUtils, Classes

Novo überarbeitet:
Delphi-Quellcode:
procedure PosExUltra(source, searchwordbefore, searchwordafter,
                     addbefore, addafter: string;
                     continuesearch, reversesearch: boolean;
                     liste: TStrings);

  function Search(var Anfang, Ende, Posi: Integer): string;
  var P: Integer;
  begin
    P:=PosEx(searchwordbefore, source, Posi);
    while (P = 0) or (P > Posi) do begin
      Dec(Posi);
      P:=PosEx(searchwordbefore, source, Posi);
    end;
    Result := Copy(
      source,
      Posi + length(searchwordbefore),
      Anfang - (Posi + length(searchwordbefore))
    );
    if (Result <> '') or (Result <> ' ') then
      liste.Append(addbefore + Result + addafter);
  end;

var
  Anfang, Ende, Posi: Integer;
  Ergebnis: string;
begin
  Ende := 0;
  if reversesearch then begin
    if continuesearch then begin
      Anfang := PosEx(searchwordafter, source, Ende);
      while Anfang > 0 do begin
        Posi := Anfang;
        if Anfang > 0 then begin
          Ergebnis:=Search(Anfang, Ende, Posi);
          Ende :=
              Posi
            + length(searchwordbefore)
            + Length(Ergebnis)
            + Length(searchwordafter);
          Anfang := PosEX(searchwordafter, source, Ende);
        end;
      end;
    end
    else begin
      Anfang := PosEx(searchwordafter, source, Ende);
      Posi := Anfang;
      if Anfang > 0 then
        Ergebnis:=Search(Anfang, Ende, Posi);
    end;
  end
  else if continuesearch then begin
    repeat
      Anfang := PosEx(searchwordbefore, source, Ende);
      if Anfang > 0 then begin
        Ende := PosEx(searchwordafter, source, Anfang);
        Ergebnis := Copy(
          source,
          Anfang + length(searchwordbefore),
          (Ende - Anfang) - length(searchwordbefore)
        );
        if (Ergebnis <> '') or (Ergebnis <> ' ') then
          liste.Append(addbefore + Ergebnis + addafter);
        Anfang := Anfang + length(Ergebnis);
      end;
    until Anfang = 0;
  end
  else begin
    Anfang := PosEx(searchwordbefore, source, Ende);
    if Anfang > 0 then begin
      Ende := PosEx(searchwordafter, source, Anfang);
      Ergebnis := Copy(
        source,
        Anfang + length(searchwordbefore),
        (Ende - Anfang) - length(searchwordbefore)
      );
      if (Ergebnis <> '') or (Ergebnis <> ' ') then
        liste.Append(addbefore + Ergebnis + addafter);
    end;
  end;
end;
alzaimar überarbeitet:
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         aFindAll : Boolean; aNewPrefix, aNewSuffix : string;
                         aWords : TStrings);
var
  PrefixLength, PosPrefix, PosSuffix, BestPos : Integer;
begin
  PosPrefix := PosEx(aPrefix, aSource, 1);
  if PosPrefix > then begin
    PrefixLength := Length (aPrefix);
    repeat
      PosSuffix := PosEx(aSuffix, aSource, PosPrefix + PrefixLength);
      if PosSuffix > 0 then begin
        while (PosPrefix <> 0) and (PosPrefix < PosSuffix) do begin
          BestPos := PosPrefix;
          PosPrefix := PosEx(aPrefix, aSource, PosPrefix + PrefixLength);
        end;
        aWords.Append(
            aNewPrefix
          + Copy(aSource, BestPos + PrefixLength, PosSuffix-BestPos - PrefixLength)
          + aNewSuffix
        );
      end;
    until not aFindall or (PosPrefix = 0);
  end;
end;

alzaimar 21. Feb 2010 20:51

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Hi omata,

Danke.

GPRSNerd 22. Feb 2010 09:17

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Hallo omata,

da hat sich ein kleiner Copy&Paste-Fehler in der 2. Procedure versteckt:

In
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         aFindAll : Boolean; aNewPrefix, aNewSuffix : string;
                         aWords : TStrings);
var
  PrefixLength, PosPrefix, PosSuffix, BestPos : Integer;
begin
  PosPrefix := PosEx(aPrefix, aSource, 1);
  if PosPrefix > then begin
sollte die letzte Zeile wohl so lauten:

Delphi-Quellcode:
  if PosPrefix > 0 then begin
Gruß,
Stefan

Hawkeye219 22. Feb 2010 11:03

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Hallo,

noch einige Anmerkungen:

- Wenn aPrefix gefunden wird, aSuffix aber nicht, kommt es zu einer Endlosschleife für aFindAll = True. Im Code von alzaimar war dieser Fall durch eine Exit-Anweisung abgedeckt.
- Der Compiler warnt, dass die Variable BestPos möglicherweise nicht initialisiert wird.
- Das Füllen der Stringliste sollte mit einem BeginUpdate..EndUpdate geklammert werden.

Ist eine Routine mit sieben bzw. acht Parametern wirklich sinnvoll? Wie testet man solch ein Ungetüm?

Gruß Hawkeye

himitsu 22. Feb 2010 11:07

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von Hawkeye219
Ist eine Routine mit sieben bzw. acht Parametern wirklich sinnvoll? Wie testet man solch ein Ungetüm?

Jupp, als Klasse wäre es bestimmt nicht schlecht.

omata 22. Feb 2010 11:24

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von GPRSNerd
da hat sich ein kleiner Copy&Paste-Fehler in der 2. Procedure versteckt...

Zitat:

Zitat von Hawkeye219
...kommt es zu einer Endlosschleife ... Im Code von alzaimar war dieser Fall durch eine Exit-Anweisung abgedeckt.

Ups, ja ihr habt recht, danke für die Hinweise.

Hier die Korrektur...
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         aFindAll : Boolean; aNewPrefix, aNewSuffix : string;
                         aWords : TStrings);
var
  PrefixLength, PosPrefix, PosSuffix, BestPos : Integer;
begin
  PosPrefix := PosEx(aPrefix, aSource, 1);
  if PosPrefix > 0 then begin
    PrefixLength := Length (aPrefix);
    repeat
      PosSuffix := PosEx(aSuffix, aSource, PosPrefix + PrefixLength);
      if PosSuffix > 0 then begin
        while (PosPrefix <> 0) and (PosPrefix < PosSuffix) do begin
          BestPos := PosPrefix;
          PosPrefix := PosEx(aPrefix, aSource, PosPrefix + PrefixLength);
        end;
        aWords.Append(
            aNewPrefix
          + Copy(aSource, BestPos + PrefixLength, PosSuffix-BestPos - PrefixLength)
          + aNewSuffix
        );
      end;
    until not aFindall or (PosSuffix = 0) or (PosPrefix = 0);
  end;
end;

Novo 23. Feb 2010 09:47

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von omata
Ups, ja ihr habt recht, danke für die Hinweise.
Hier die Korrektur...

Ich bin schwer beeindruckt!
Bei deiner Suche klappt sogar eine Suche nach:
Delphi-Quellcode:
">
und:
Delphi-Quellcode:
</a></td>
obwohl
Delphi-Quellcode:
">
SEHR häufig vorkommt.
Ausserdem scheint deine Funktion schneller zu sein, als meine, weniger Code ist es auf jedenfall.

Respekt und danke!


Mfg Novo

HeikoAdams 25. Feb 2010 09:51

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Gibt es einen bestimmten Grund, warum Du
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         aFindAll : Boolean; aNewPrefix, aNewSuffix : string;
                         aWords : TStrings);
verwendest und nicht
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         const aFindAll : Boolean; const aNewPrefix, aNewSuffix : string;
                         var aWords : TStrings);
?

Da bei keinem der Parameter eine Wertzuweisung stattfindet, kann man sie ohne Probleme auch alle als Konstanten deklarieren, mit Ausnahme vom aWords. Der muss als Var-Parameter deklariert werden, da ja dort die Ergebnisse gespeichert werden.

Man könnte das ganze auch noch eleganter mittels
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         const aNewPrefix, aNewSuffix : string;
                         var aWords : TStrings; const aFindAll : Boolean = True);
umsezen. In diesem Fall bräuchte man aFindAll nur dann zu übergeben, wenn nur der erste Treffer zurückgegeben werden soll.

Um das ganze richtig elegant zu machen, würde ich das ganze so umbauen:
Delphi-Quellcode:
function ExtractBetween(const aSource, aPrefix, aSuffix, aNewPrefix, aNewSuffix : string;
                         const aFindAll : Boolean = True): TStrings;
var
  PrefixLength, PosPrefix, PosSuffix, BestPos : Integer;
begin
  PosPrefix := PosEx(aPrefix, aSource, 1);

  if (PosPrefix > 0) then
  begin
    PrefixLength := Length (aPrefix);

    repeat
      PosSuffix := PosEx(aSuffix, aSource, PosPrefix + PrefixLength);

      if (PosSuffix > 0) then
      begin
        while (PosPrefix <> 0) and (PosPrefix < PosSuffix) do
        begin
          BestPos := PosPrefix;
          PosPrefix := PosEx(aPrefix, aSource, PosPrefix + PrefixLength);
        end;

        Result.Append(
            aNewPrefix
          + Copy(aSource, BestPos + PrefixLength, PosSuffix-BestPos - PrefixLength)
          + aNewSuffix
        );
      end;
    until not aFindall or (PosSuffix = 0) or (PosPrefix = 0);
  end;
end;
dann kann man sich den Parameter aWords nämlich komplett sparen :wink:

Hawkeye219 25. Feb 2010 10:34

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Hallo Heiko,

Zitat:

Zitat von HeikoAdams
dann kann man sich den Parameter aWords nämlich komplett sparen :wink:

Und wer erzeugt die Stringliste dann? Wie möchtest du das Ergebnis beispielsweise einem Memo zuweisen? In solchen Fällen gibt es eine feste Regel, an die man sich halten sollte: Der Aufrufer muss eine Stringliste bereitstellen, die von der Routine gefüllt wird. Er ist es auch, der die Stringliste wieder freigeben muss. Jede andere Vorgehensweise wird früher oder später zu einem Problem führen.

Gruß Hawkeye

schlecki 25. Feb 2010 10:48

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von HeikoAdams
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix : string;
                         const aFindAll : Boolean; const aNewPrefix, aNewSuffix : string;
                         var aWords : TStrings);
Da bei keinem der Parameter eine Wertzuweisung stattfindet, kann man sie ohne Probleme auch alle als Konstanten deklarieren, mit Ausnahme vom aWords. Der muss als Var-Parameter deklariert werden, da ja dort die Ergebnisse gespeichert werden.

Meiner Meinung nach, kann auch aWords als const deklariert werden. Du willst ja keine andere Liste zuweisen, sondern nur ihre Werte ändern. Das sollte auch so gehen.

DeddyH 25. Feb 2010 10:51

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Richtig. Ich selbst deklariere übergebene Objekte immer als const, damit ich mir nicht versehentlich "ins Knie schieße", indem ich die Referenz ändere.

xZise 25. Feb 2010 11:56

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Übrigens musst du bei Objektreferenzen kein var davor schreiben.
Du bekommst ja quasi einen Pointer auf die Stringliste und den änderst du ja nicht, also kein Grund für var ;) Und wie bereits gesagt wurde ist const sogar angebrachter.

MfG
Fabian

Novo 25. Feb 2010 17:33

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von HeikoAdams
Um das ganze richtig elegant zu machen, würde ich das ganze so umbauen:
Delphi-Quellcode:
function ExtractBetween(const aSource, aPrefix, aSuffix, aNewPrefix, aNewSuffix : string;
                         const aFindAll : Boolean = True): TStrings;
dann kann man sich den Parameter aWords nämlich komplett sparen :wink:

Keine gute idee, daraus ne funktion zu machen.
Ausserdem geht das nichtmal:
Delphi-Quellcode:
      ListBox1.Items.AddStrings(FuncExtractBetween(source, '[img]', '[/img]</img>', '', '', True));
Bei:
Code:
<img src="http://blub.de
" alt="album"></img>
<img src="http://blub.de
" alt="bild"></img>
<img src="http://blub.de
" alt="bild"></img>
<img src="http://blub.de
" alt="album"></img>
<img src="http://blub.de
" alt="album"></img>
<img src="http://blub.de
" alt="bild"></img>
<img src="http://blub.de
" alt="album"></img>
mal alle album links ziehn.
öhm und ist die rückwärtssuche nun wirklich überflüssig?

GPRSNerd 26. Feb 2010 08:57

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Jau, das mit der direkten Zuweisung der TStrings als Funktionsrückgabe funktioniert wirklich nicht.
Beim ersten Zugriff auf Results in der Funktion (z.B. durch Results.Beginupdate oder .Append) kommt eine Read/write access exception.
Weiss jemand, warum das nicht geht und ob es da einen Trick gibt?

DeddyH 26. Feb 2010 09:02

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Das dürfte daran liegen, dass Result nirgends erzeugt wird. Aber wie bereits weiter oben gesagt sollte man das Objekt besser an die Routine übergeben, sonst fängt man sich schnell ein Speicherleck ein.

xZise 26. Feb 2010 09:07

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Moin,
naja wo ist denn das Objekt erstellt worden? Vorgesehen war es eigentlich das dies außerhalb passiert, aber wenn die nun zurückgegeben wird, musst du es wohl in der Funktion erstellen. Allerdings gewöhne dir das lieber nicht an, weil das ist nicht gerade sauberer Stil.

Es stellt sich nämlich die Frage, wo wird das denn wieder freigegeben? Bei der ursprünglichen Version war das klar:
Delphi-Quellcode:
var
  o : TObject;
begin
  o := TObject.Create;
  try
    func(o);
    // do sth.
  finally
    o.Free;
  end;
Bei der veränderten Fassung, kannst du das nicht mehr so strikt trennen. Deshalb ist der Trick, nimm die Fassung von omata (oder?):
Delphi-Quellcode:
ExtractBetween(…, Memo.Lines);
Nebenbei, wenn der letzte Parameter ein var-Parameter wäre, geht das glaub ich nicht, weil TCustomMemo.Lines eine Property ist.

MfG
Fabian

GPRSNerd 26. Feb 2010 09:27

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Erzeugt und wieder freigegeben habe ich die Stringlist natürlich ausserhalb, eine Property ist diese ja auch nicht:

Delphi-Quellcode:
  slSplitString:=TStringList.Create;
  try
    slSplitString:=ExtractBetween(dummy, 'http://', '.de', '', '', true);
    Memo.Lines.AddStrings(slSplitString);
  finally
    slSplitString.Free;
  end;

DeddyH 26. Feb 2010 09:29

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Und damit hast Du Dir die Referenz der erzeugten Liste mit einer nichtexistenten überschrieben. Mach es doch wie vorgeschlagen: übergib die Liste als (const-)Parameter und alles wird gut :)

[edit] Da waren mir zu viele "und"s drin :mrgreen: [/edit]

GPRSNerd 26. Feb 2010 10:07

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Hab ich schon längst wieder gemacht. :)
Es ging mir nur darum, zu verstehen, was hier schief läuft.

Danke für die Infos und die Geduld.

xZise 26. Feb 2010 10:21

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Hast du es schon bereits verstanden? :D

Ansonsten eine kleine Erklärung:
Du gibst mit der Funktion via Result etwas zurück. Das macht auch deine Zuweisung deutlich. Wenn man schreibt a := b dann schreibt man quasi den Inhalt von b in a. Das Problem dabei ist, das du nirgends der Funktion sagst, wie denn das erstellte Objekt lautet, dass zurückgegeben wird. Ich mach mal ein Beispiel:
Delphi-Quellcode:
function answer : Integer;
begin
  Result := 42 * Result;
end;

var
  a : Integer;
begin
  a := 10;
  a := answer;
  ShowMessage(IntToStr(a));
end;
Das ist quasi dein Beispiel: Du initialisierst a und schreibst dann den Wert der Funktion rein, und versuchst a in der Funktion zu benutzen. Wahrscheinlich steht nachher im Dialog 0, weil Result normalerweise mit 0 initalisiert wird. Wenn man das jetzt etwas umschreibt und die Funktion umgeht sieht das dann so aus (woraus klar werden dürfte, warum das nicht geht):
Delphi-Quellcode:
var
  a, b : Integer;
begin
  a := 10;

  // Code von answer
  b := 42 * b;

  a := b;
  ShowMessage(IntToStr(a));
end;
Das heißt, Result ist wie eine lokale Variable und nur in der Funktion sichtbar, womit es nicht den Wert von a hat.
Ich hoffe das ist soweit verständlich.

MfG
Fabian

PS: Es kann auch sein, dass a ≠ 0 ist, weil man nicht sicher sein kann, mit was Result oder b initalisiert werden. In der Regel wird a aber 0 sein ;)

GPRSNerd 26. Feb 2010 10:31

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Jetzt hats Klick gemacht, danke.
Schon witzig, wenn einem so ein Grundlagenproblem nach vielen Jahren Programmiertätigkeit noch nicht untergekommen ist.

DeddyH 26. Feb 2010 10:34

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Um das noch weiter auszuführen: TStrings ist ja eine Klasse. Will man nun ein Objekt dieser Klasse in einer Funktion zurückgeben, muss sie auch innerhalb der Klasse Funktion instanziert werden. Da aber die Freigabe nicht innerhalb des Blocks (also der Funktion) erfolgen kann, in dem sie instanziert wurde (der Rückgabewert wäre ja sonst hinfällig), muss man sehr genau aufpassen, dass man sich kein Speicherleck einfängt. Aus genau diesem Grund gilt diese Vorgehensweise als unschön. Der einzige mir bekannte sinnvolle Einsatzzweck einer Funktion, die ein Objekt zurückgibt, ist der Getter einer Property. Es mag evtl. noch weitere geben, aber es fallen mir spontan keine ein. Zur angesprochenen Property aber noch schnell ein Beispiel:
Delphi-Quellcode:
type
  TMyClass = class
  private
    //privates Feld vom Typ der Klasse (initial nil)
    FStrings: TStrings;
    //Getter-Methode
    function GetStrings: TStrings;
  public
    //Property
    property Strings: TStrings read GetStrings;
  end;

...

function TMyClass.GetStrings: TStrings;
begin
  if not Assigned(FStrings) then
    FStrings := TStringlist.Create;
  Result := FStrings;
end;
Der Sinn einer solchen Vorgehensweise liegt darin, Objekte erst dann zu erzeugen, wenn diese auch wirklich gebraucht werden, im obigen Beispiel also beim ersten Zugriff auf die Property.

[edit] Blödsinn korrigiert (s.o.) [/edit]

Blup 26. Feb 2010 16:41

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von DeddyH
Und damit hast Du Dir die Referenz der erzeugten Liste mit einer nichtexistenten überschrieben. Mach es doch wie vorgeschlagen: übergib die Liste als (const-)Parameter und alles wird gut :)

[edit] Da waren mir zu viele "und"s drin :mrgreen: [/edit]

Die Liste als const-Parameter zu übergeben ist fast genauso falsch wie als var-Parameter.
In beiden Fällen wird eine Referenz auf eine Objektvariable erwarted, damit ist die direkte Übergabe von TStrings-Property nicht möglich.

Vor Parametern für einfache Datentypen ohne Referezzählung braucht auch kein const zu stehen.
Ein Boolean nimmt auf dem Stapel ebenso viel Speicher ein, wie ein Zeiger auf eine Boolean-Variable/Konstante.
Innerhalb der Prozedur kann einfacher auf den Wert zugegriffen werden, als diesen bei jedem Zugriff über den Zeiger zu lesen/schreiben.

Mein Vorschlag:
Delphi-Quellcode:
procedure ExtractBetween(const aSource, aPrefix, aSuffix, aNewPrefix, aNewSuffix : string;
                         aWords : TStrings;
                         aFindAll : Boolean = True);

DeddyH 26. Feb 2010 16:46

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Zitat:

Zitat von Blup
..., damit ist die direkte Übergabe von TStrings-Property nicht möglich.

Delphi-Quellcode:
procedure FillWithNumbers(const aList: TStrings);
var i: integer;
begin
  aList.Clear;
  for i := 0 to 100 do
    aList.Add(Format('%d',[i]));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FillWithNumbers(Memo1.Lines);
end;
Komischerweise funktioniert das bei mir, obwohl ich das wohl schon seit Jahren falsch mache.

xZise 26. Feb 2010 18:54

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Ich vermute, er verwechselt das mit var was natürlich nicht geht ;)

MfG
Fabian

Blup 1. Mär 2010 12:18

Re: PosExUltra - Ultimative Stringsuche/Parser
 
Ich habe mich zu kurz und damit falsch ausgedrückt :oops:
Zitat:

Zitat von Blup
Die Liste als const-Parameter zu übergeben ist fast genauso falsch wie als var-Parameter.

Ich hätte schreiben sollen "Die Übergabe als const-Parameter ist überflüssig".
Zitat:

Zitat von Blup
In beiden Fällen wird eine Referenz auf eine Objektvariable erwarted,

Tatsächlich behandelt der Compiler "const" nur, in dem er Zuweisungen auf die Variable innerhalb der Prozedur verbietet und entsprechend auch die Weitergabe als var-Parameter an weitere Funktionsaufrufe. Der erzeugte Code ist identisch, ob "const" angegeben wird oder auch nicht.
Zitat:

Zitat von Blup
damit ist die direkte Übergabe von TStrings-Property nicht möglich.

Der Teilsatz bezieht sich natürlich auf var-Parameter. Als weitere Einschränkung muss die Variable auch genau dem Typ entsprechen. Wird die Variable z.B. als TStringList deklariert, ist eine direkte Übergabe unzulässig.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:57 Uhr.
Seite 1 von 2  1 2      

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