Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Live Stichwortsuche (https://www.delphipraxis.net/179275-live-stichwortsuche.html)

Ajintaro 24. Feb 2014 14:06

Live Stichwortsuche
 
Hallo DP,

Ich habe folgende ini-Datei erstellt:

[Delphi]
Delphi in die Praxis umsetzen=Jederzeit
Delphi in die Tat umsetzen=Aber sicher
Was ist Delphi eigentlich=Schön
Mein Name ist Borland=Nicht wirklich

Folgender Code bietet eine Art live-suche mit Filter:

Delphi-Quellcode:
procedure FilterItems(const sl: TStrings; const substr: string);
var i: integer;
begin
  sl.BeginUpdate;
  try
    for i := Pred(sl.Count) downto 0 do
      if not StrUtils.AnsiContainsText(sl[i],substr) then
        sl.Delete(i);
  finally
    sl.EndUpdate;
  end;
end;
Delphi-Quellcode:
procedure TForm1.e_inputChange(Sender: TObject);
begin
 if e_input.text <> '' then
  begin
    FilterItems(Listbox1.Items, e_input.Text);
  end;
  if e_input.text = '' then
  begin
    mif.ReadSectionValues(ComboBox1.Text, Listbox1.items);
  end;
end;
2 Kleine Probleme gibt es dabei noch:

a) Tippt man Delphi in das Editfeld, werden richtigerweise die ersten drei Einträge angezeigt:

...
Delphi in die Praxis umsetzen=Jederzeit
Delphi in die Tat umsetzen=Aber sicher
Was ist Delphi eigentlich=Schön
...

Wenn ich nun als 2. Wort umsetzen eintippe, sollten nur noch die ersten beiden Einträge sichtbar sein:

...
Delphi in die Praxis umsetzen=Jederzeit
Delphi in die Tat umsetzen=Aber sicher
...

Das klappt leider noch nicht ganz. Muss ich das gefilterte Ergebnis erneut durchsuchen?

b) Sobald man einen Tippfehler macht und ein Wort wieder aus dem Editfeld löscht, wird das bis dahin gefilterte Ergebnis nicht mehr angezeigt. Kann man das mit einer Hilfs-Stringlist umgehen ?

Und noch eine Frage bezüglich ini-Dateien:
Gibt es eine ini-funktion, welche den Key und die Section getrennt lesen kann? Ich würde gerne beide Werte getrennt in einem Editfeld darstellen. Bisher habe ich aber immer nur den VALUE Wert über den KEY ausgelesen.

Vielen Dank,

Jaimy

Sir Rufo 24. Feb 2014 14:22

AW: Live Stichwortsuche
 
Du musst nach jedem Suchwort einzeln suchen. Dazu musst du aber auch vorher den Suchtext in einzelne Worte auftrennen und dann der Suchroutine übergeben (z.B. als Array)

DeddyH 24. Feb 2014 14:34

AW: Live Stichwortsuche
 
Und Du wirst schon 2 Stringlisten brauchen, wenn Du nicht nach jedem Verkleinern des Filters alles neu abrufen willst. Ich denke da an so etwas (Sir Rufos Vorschlag gleich mit übernommen):
Delphi-Quellcode:
procedure FilterItems(const SrcList, DestList: TStrings; const substr: array of string);
var
  Line, SearchwordIndex: integer;
  Containing: Boolean;
begin
  Assert(Assigned(SrcList), 'Keine gültige Quellliste');
  Assert(Assigned(DestList), 'Keine gültige Zielliste');
  DestList.BeginUpdate;
  try
    DestList.Clear;
    for Line := 0 to SrcList.Count - 1 do
      begin
        Containing := true;
        for SearchwordIndex := Low(substr) to High(substr) do
          if not AnsiContainsText(SrcList[Line], substr[SearchwordIndex]) then
            begin
              Containing := false;
              break;
            end;
        if Containing then
          DestList.Add(SrcList[Line]);
      end;
  finally
    DestList.EndUpdate;
  end;
end;
Ungetestet, hat bestimmt auch noch Optimierungspotential, sollte aber funktionieren.

michaelthuma 24. Feb 2014 15:27

AW: Live Stichwortsuche
 
Ganz habe ich die Frage nicht verstanden.

Lies aus dem IniFile mit
Code:
ReadSectionValues('Delphi', LValues)
um Wertepaare zu erhalten und

greife auf die einzelnen Werte über
Code:
 LValues.Values['Delphi in die Praxis umsetzen']
zu.

Andere umständliche Variante wäre. Lade die IniFile in eine TMemIniFile Instanz und lösche die nicht benötigten Sections raus und anschließend den Eintrag für die Section selbst.

Delphi-Quellcode:
 LMemIniFile.EraseSection('Another');
 LMemIniFile.GetStrings(LStrList);
  (* Hier den Section Eintrag '['Delphi']' löschen*)
Zitat:

Zitat von Ajintaro (Beitrag 1249315)
Und noch eine Frage bezüglich ini-Dateien:
Gibt es eine ini-funktion, welche den Key und die Section getrennt lesen kann? Ich würde gerne beide Werte getrennt in einem Editfeld darstellen. Bisher habe ich aber immer nur den VALUE Wert über den KEY ausgelesen.

Vielen Dank,

Jaimy


Ajintaro 25. Feb 2014 18:22

AW: Live Stichwortsuche
 
Zitat:

Zitat von Sir Rufo (Beitrag 1249318)
Du musst nach jedem Suchwort einzeln suchen. Dazu musst du aber auch vorher den Suchtext in einzelne Worte auftrennen und dann der Suchroutine übergeben (z.B. als Array)

Kann ich denn zur Laufzeit ein dynamisches array mit den Buchstaben aus dem Edit Feld erstellen ? Wenn ich Hallo Welt in das Edit Feld schreibe, besteht mein Array aus 2 Elementen (der Leerschritt trennt die einzelnen Elemente?)
Was ist, wenn das 2. Wort noch nicht vollständig ausgeschrieben ist, aber die Filterfunktion schon mit dem bisher eingegebenen Buchstaben bereits lossuchen soll ?

Oder denk ich viel zu kompliziert?

Sir Rufo 25. Feb 2014 19:49

AW: Live Stichwortsuche
 
Ich glaube ja ;)

Nehmen wir doch mal den Text hier
Code:
Delphi in die Praxis umsetzen
und das als Sucheingabe
Code:
Delphi ums
Jetzt prüfen wir ja mit
Delphi-Quellcode:
StrUtils.AnsiContainsText( Zeile, SuchWort )
ob das erste Wort enthalten ist ("Delphi") und ob das zweite Wort enthalten ist ("ums").

Und, was denkst du liefert uns die Funktion jeweils zurück? ;)

Ajintaro 26. Feb 2014 07:38

AW: Live Stichwortsuche
 
Guten Morgen,

Ja
Code:
StrUtils.AnsiContainsText( Zeile, SuchWort )
wird das gewünschte Ergebnis liefern allerdings mache ich mir sorgen um:

Delphi-Quellcode:
Edit.Text
, da das ja kein
Delphi-Quellcode:
Array of string
ist. Ich habe mich gefragt, wie ich zur Laufzeit aus
Delphi-Quellcode:
Edit.Text
ein dynamisches
Delphi-Quellcode:
Array of string
erstelle. Ich weiß ja zu Beginn meiner Eingabe nicht, wie viele Suchbegriffe ich eingeben muss bis die gewünschte Zeile herausgefiltert wird.:?:

Sir Rufo 26. Feb 2014 07:46

AW: Live Stichwortsuche
 
Indem du den string durch einen kleinen Parser schickst?

Jedesmal wenn du auf ein Whitespace-Zeichen (Trenner für Worte) triffst, dann wird das Wort an das Array gehängt.

user0815 26. Feb 2014 08:38

AW: Live Stichwortsuche
 
Als ich "Tippfehler" gelesen habe, musste ich an den Wikipedia: Levenshtein-Distanz Algorithmus denken.
Hier im Forum: http://www.delphipraxis.net/58685-le...n-distanz.html

Ajintaro 26. Feb 2014 09:14

AW: Live Stichwortsuche
 
Ich bin gerade dabei den "Parser" zu erstellen, welcher mir während der Editfeld-Eingabe ein Array erstellt. Das Leerzeichen soll als Trenner dienen.

Ich hab das Array auf 3 Elemente limitiert, da ich davon ausgehen kann dass man nach 3 Wörtern den gewünschten Eintrag herausfiltern kann.

Delphi-Quellcode:
procedure TForm1.e_inputChange(Sender: TObject);
var i:integer;
my_arr: Array[1..3] of String;
begin
 if e_input.text <> '' then
  begin
    //Nicht leer

    for i := 1 to length(e_input.text) do
    begin
      if e_input.text[i] = ' ' then
      begin
        //add to array
        my_arr[i] := e_input.Text;
      end;
    end;

 end;
Das funktioniert natürlich nur bis zum 1. Leerschritt, beim 2. knallts. Ich vermute mal da werde ich Pos und PosEx bemühen müssen um mir die Whitespace zu merken?

Sir Rufo 26. Feb 2014 09:22

AW: Live Stichwortsuche
 
Entweder ist das Zeichen Whitespace oder nicht.

Wenn kein Whitespace dann das Zeichen an den Buffer (string) geben
Wenn Whitespace und der Buffer nicht leer ist, den Buffer an das Array und den Buffer wieder leeren.
Delphi-Quellcode:
function ParseSearchStr( const SearchStr : string ) : TArray<string>;
var
  LCount : integer;
  LBuffer : string;
  LChar : Char;
begin
  SetLength( Result, Length( SearchStr ) div 2 + 1 );
  LCount := 0;
  for LChar in SearchStr do
  begin
    if ( LChar = ' ' ) and ( LBuffer <> '' ) then
    begin
      Result[LCount] := LBuffer;
      Inc( LCount );
      LBuffer := '';
    end
    else
      LBuffer := LBuffer + LChar;
  end;
  if LBuffer <> '' then
  begin
    Result[LCount] := LBuffer;
    Inc( LCount );
  end;
  SetLength( Result, LCount );
end;
Bevor du mich fragst, warum ich das Ausgabe-Array auf
Delphi-Quellcode:
Length( SearchStr ) div 2 + 1
initialisiere, überlege dir wieviele Teile ein String maximal haben kann, wenn diese durch ein Trennzeichen abgegrenzt werden

DeddyH 26. Feb 2014 09:30

AW: Live Stichwortsuche
 
Ähnlich habe ich das auch gerade umgesetzt (nur ohne Generics):
Delphi-Quellcode:
type
  TDynStringArray = array of string;

function StrToArray(const InputStr: string): TDynStringArray;
var
  PLast, PCurrent: PChar;
  s: string;
  ArrayIndex: integer;
begin
  SetLength(Result, Length(InputStr) div 2);
  ArrayIndex := Low(Result);
  PLast := PChar(InputStr);
  PCurrent := PChar(InputStr);
  while PCurrent^ <> #0 do
    begin
      if PCurrent^ = ' ' then
        begin
          SetString(s, PLast, PCurrent - PLast);
          Result[ArrayIndex] := s;
          inc(ArrayIndex);
          PLast := CharNext(PCurrent);
        end;
      PCurrent := CharNext(PCurrent);
    end;
  SetString(s, PLast, PCurrent - PLast);
  Result[ArrayIndex] := s;
  SetLength(Result, ArrayIndex + 1);
end;

Ajintaro 26. Feb 2014 10:11

AW: Live Stichwortsuche
 
:shock:das das zaubert ihr mal eben so raus ?

Das ist natürlich ein schlauer Ansatz: nur wenn der buffer leer ist und im suchstring ein whitespace vorkommt, findet eine Übergabe an das array statt. Dabei muss das Suchwort aus mindestens ein 2 Chars bestehen. D.h. man muss nicht einmal das Wort voll ausschreiben, man kann es auch abgekürzt übergeben! Genial.
Bei erfolgreicher Übergabe wird der Puffer wieder geleert, andernfalls um ein weiteres Zeichen ergänzt.

Das hat mir sehr geholfen und wird vermutlich vielen anderen auch eine große Hilfe sein.

Sir Rufo 26. Feb 2014 19:47

AW: Live Stichwortsuche
 
Zitat:

Zitat von Ajintaro (Beitrag 1249683)
:shock:das das zaubert ihr mal eben so raus ?

Ähm, ja ... ich überlege mir immer wie ich das "zu Fuß" bzw. "per Hand" machen würde.
Also auf einem Blatt Papier steht ein Satz und ich soll alle Worte einzeln untereinander schreiben.

Wenn man jetzt einmal ganz genau darauf achtet, was man wirklich da macht, und sich zusätzlich einmal vorstellt, dass die Worte in einer mir völlig fremden Sprache (aber schon links-nach-rechts) sind, dann muss ich genau so vorgehen wie das der Rechner machen muss.

Das kleide ich jetzt in Befehle und der Drops ist gelutscht ;)

michaelthuma 26. Feb 2014 21:20

AW: Live Stichwortsuche
 
Ja. Das gute alte C und Pascal:) So wie es sich gehört. Programmieren ist noch nicht verboten. :-D

An sich gehört das Ergebnis in eine Liste. Aber nur in der Javawelt :-D Da musst du zum Architekten gehen und Buße tun. Der labert dann wirres Zeug von Matrizen und so. So ist das in der Pascal Welt nicht. Da reichts wenn es läuft und schnell ist, davon haben die Benutzer ein Nutzen und sind nicht die Benutzten.

Delphi intern ist auch eine Liste wie gehabt ein dynamisches Array.

Rein zur Ergänzung ...

Meine Erfahrung je nach DB eher Anzahl der Sätze sollten ein Kriterium ca. 3 Zeichen lang sein, wenn du Objekte erzeugst. Sonst gehen 2. Wenn 3 nicht reichen dann besser mit Tastatur bestätigen lassen die Suche. Hängt an sich von der Arbeitsumgebung ab. Zumeist je mehr Sätze desto der müder der Rechner. Die Hardwareausstattung ist sehr oft umgekehrt proportional zum Nutzen im Arbeitsprozess oder die Hardware einfach älter, da solche Systeme sich ungern jemand traut anzugreifen.

Es gibt Datenbanken die mit %LIKE% nicht so wirklich ein Freude haben. Einer ordentlichen Datenbank ist das heute egal. Das gute alte Access war da etwas pingelig. Selbst bei bescheidenen ca. 100k Sätzen auf einem Celeron oder P IV. Solche Rechner gibt es noch ... da entsteht ein Verzögerung bei der Eingabe selbst ohne Objekterzeugung.

Es sind nicht mehr Zeichen unbedingt weniger Datensätze. 'Love' respektive 'Liebe' in einem Musikrepertoire ist überrepräsentiert.

Dann nimmt man mal einen Kellner im Stress bei der Suche. Der soll ja servieren und nicht auf der Anlage rumspielen. Aber was soll der tun wenn sich die Granny aufrafft und was modernes sich wünscht fürs Enkerl so etwas wie 'Mit BO von U2'. Ist am Ende 'Born in the USA vom Springsteen und das Enkerl kennt das trotzdem nicht...

Früher musste man noch berücksichtigen, dass die Leute das Telefon unterm Kinn hatten... die haben auch nicht viel Freude mit dem Warten. Das Szenario an sich spielt eine Rolle. Suchen und Hektik gehören irgendwie zusammen. Deswegen soll das Suchen wirklich schnell gehen.

An sich aber die beiden vorgeschlagene Variante der bessere Weg im Mittel. Der hält an sich recht gut.

Zitat:

Zitat von Ajintaro (Beitrag 1249683)
:shock:das das zaubert ihr mal eben so raus ?


Sir Rufo 27. Feb 2014 08:03

AW: Live Stichwortsuche
 
Zitat:

Zitat von DeddyH (Beitrag 1249678)
Ähnlich habe ich das auch gerade umgesetzt (nur ohne Generics):
Delphi-Quellcode:
type
  TDynStringArray = array of string;

function StrToArray(const InputStr: string): TDynStringArray;
var
  PLast, PCurrent: PChar;
  s: string;
  ArrayIndex: integer;
begin
  SetLength(Result, Length(InputStr) div 2);
  ArrayIndex := Low(Result);
  PLast := PChar(InputStr);
  PCurrent := PChar(InputStr);
  while PCurrent^ <> #0 do
    begin
      if PCurrent^ = ' ' then
        begin
          SetString(s, PLast, PCurrent - PLast);
          Result[ArrayIndex] := s;
          inc(ArrayIndex);
          PLast := CharNext(PCurrent);
        end;
      PCurrent := CharNext(PCurrent);
    end;
  SetString(s, PLast, PCurrent - PLast);
  Result[ArrayIndex] := s;
  SetLength(Result, ArrayIndex + 1);
end;

Das Array sollte mit
Delphi-Quellcode:
Length( InputStr ) div 2 + 1
initialisiert werden, denn
Delphi-Quellcode:
'a b'
ergibt 2 Suchworte, aber du initialisierst mit
Delphi-Quellcode:
3 div 2 = 1
und es knallt ;)

Dafür würde bei meiner Version das letzte Wort nicht in das Array geschrieben :shock: und du müsstest bei deiner Version nur 2 Zeilen tauschen, damit es trotzdem funktioniert. :oops:
(Ist gefixed)

Ein weiteres Problemchen ist, dass bei
Delphi-Quellcode:
'a b '
als Ergebnis
Delphi-Quellcode:
['a','b','']
geliefert wird, aber dafür gibt es ja auch die UnitTests ;)

DeddyH 27. Feb 2014 08:29

AW: Live Stichwortsuche
 
Da war noch ein anderer Denkfehler drin: mehrere Trennzeichen hintereinander oder nur Trennzeichen im String sollten IMO ignoriert werden. Das sieht nun so aus:
Delphi-Quellcode:
type
  TDynStringArray = array of string;

function StrToArray(const InputStr: string): TDynStringArray;
var
  PLast, PCurrent: PChar;
  s: string;
  ArrayIndex: integer;
begin
  SetLength(Result, Length(InputStr) div 2 + 1);
  ArrayIndex := Low(Result);
  PLast := PChar(InputStr);
  PCurrent := PChar(InputStr);
  while PCurrent^ <> #0 do
    begin
      if PCurrent^ = ' ' then
        begin
          SetString(s, PLast, PCurrent - PLast);
          if s <> '' then
            begin
              Result[ArrayIndex] := s;
              inc(ArrayIndex);
            end;
          PLast := CharNext(PCurrent);
        end;
      PCurrent := CharNext(PCurrent);
    end;
  SetString(s, PLast, PCurrent - PLast);
  if s <> '' then
    begin
      Result[ArrayIndex] := s;
      inc(ArrayIndex);
    end;
  SetLength(Result, ArrayIndex);
end;

Jumpy 27. Feb 2014 08:53

AW: Live Stichwortsuche
 
Hätte man das nicht einfach mit 'ner TStringlist machen können und das Leerzeichen als Delimiter?
OK, im Falle doppelter Leerzeichen hintereiander hätte man leere Strings in der Liste, die man erst rauslöschen müsste.
Wäre wahrscheinlich langsamner, oder?

Sir Rufo 27. Feb 2014 09:13

AW: Live Stichwortsuche
 
Zitat:

Zitat von Jumpy (Beitrag 1249802)
Hätte man das nicht einfach mit 'ner TStringlist machen können und das Leerzeichen als Delimiter?
OK, im Falle doppelter Leerzeichen hintereiander hätte man leere Strings in der Liste, die man erst rauslöschen müsste.
Wäre wahrscheinlich langsamner, oder?

Hier ist als Whitespace nur das Leerzeichen definiert. Die Erfahrung zeigt aber, dass in der Realität mehrere Zeichen als Whitespace genommen werden (z.B.
Delphi-Quellcode:
' ,.?!;'
) und diese Funktion berücksichtigt das dann einfach und bei einer StringList muss ich erst vorher alles umwandeln.

Beliebt ist es auch zusammenhängende Wörter in "" zu setzen, die dann trotz Whitspace zusammenbleiben.

Input:
Delphi-Quellcode:
'"Hallo du",da!'
Output:
Delphi-Quellcode:
['Hallo du','da']
Auch wäre eine Erweiterung denkbar um bestimmte Begriffe auszuschließen, dann müsste man sich aber ein anderes Ausgabeformat überlegen.

Furtbichler 27. Feb 2014 11:19

AW: Live Stichwortsuche
 
Delphi-Quellcode:
Procedure SentenceToWords (const sentence : String; words : TStringList)
Var
  c : Char;
  word : String;

begin
  for c in sentence do
    if IsWhiteSpace(c) then
      if Length(word)>0 then begin
        words.Add(word);
        word := '';
      end else
    else if IsLetterOrDigit(c) then
      word := word + c;

  if Length (word)>0 then
    words.Add(word);
end;
Ungetestet. Die Funktionen 'IsWhiteSpace' und 'IsLetterOrDigit' sind natürlich nachzuliefern (falls es sie noch nicht gibt).
Ist zwar vielleicht ein wenig langsamer, aber ob nun 10 Nanosekunden oder 30 ist -denke ich- nicht ganz so tragisch. Dafür ist es imho einfacher zu verstehen und wenigstens kann ich mit Strings arbeiten und muss mich nicht mit PChar rumschlagen, wieso hab ich mich denn für Delphi entschieden? Bei dem PChar-Geraffel hätte ich gleich C nehmen können. :mrgreen:


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:43 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