Autor Beitrag
hRb
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 100
Erhaltene Danke: 2



BeitragVerfasst: Di 11.04.17 14:33 
In der Delphi-Hilfe (sowohl D7 als XE3) gibt es Beispiel-Code zum Finden eines Textes. Bei mir funktioniert dieser Code nur in Vorwätsrichtung, nicht jedoch rückwärts. Ebenso beachtet die Funktion nicht die Auswahl "nur grosse Buchstaben", sondern stoppt immer.
In der beigefügten zip-Datei habe ich die Finddialog-Routine und ein Richedit als Anwendung aufbereitet, damit ein evtl. Helfer es einfacher hat [Compiliert wurde unter XE3, fertige Anwendung im Unterordner Win32].
Speziell geht es um die Routine
ausblenden Delphi-Quelltext
1:
FoundAt := FindText(FindDialog1.FindText, StartPos, ToEnd, mySearchTypes);					

Ich verstehe die Parameter StartPos und ToEnd so, dass wenn wenn ToEnd < StartPos ist, automatisch rückwärts gesucht wird.
Erkennt jemand den/die Fehler in meinem Programm?
hrb
Einloggen, um Attachments anzusehen!
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1645
Erhaltene Danke: 304

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Di 11.04.17 17:49 
Guten Tag hRb,

die TRichEdit.FindText() Methode sucht in Wirklichkeit die Wörter, und nicht der TFindDialog. Der Dialog hat einzig die .FindText Eigenschaft. Das könnte genauso gut irgendeine string Variable sein. In meinen Augen ist die Komponente ein reiner Nonsense.
Zu deiner Frage: Der 3. Parameter ("ToEnd") bestimmt eine Länge, und der 2. Parameter ("StartPos") die gefundene Stelle/Position. Diese Werte zu vertauschen würde nichts bringen. Wie die Suche rückwärts erfolgen soll, müsste ich mir erst (später) anschauen.

_________________
„Wo andere blind der Wahrheit folgen, denk daran ... Nichts ist wahr!" (Assassin's Creed I-II)
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2145
Erhaltene Danke: 42

Windows 7 64 Bit
Delphi XE2 Starter, Delphi 10.1 Starter
BeitragVerfasst: Di 11.04.17 19:10 
user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:
In der Delphi-Hilfe (sowohl D7 als XE3) gibt es Beispiel-Code zum Finden eines Textes. Bei mir funktioniert dieser Code nur in Vorwätsrichtung, nicht jedoch rückwärts. Ebenso beachtet die Funktion nicht die Auswahl "nur grosse Buchstaben", sondern stoppt immer.

Ich habe dein Beispiel nicht getestet, aber vielleicht hilft dir das weiter:

www.swissdelphicente...showcode.php?id=1878

Ich habe dieses für Delphi XE2 auskommentiert:
ausblenden Delphi-Quelltext
1:
2:
if AnsiStrLComp(@findWhat[1], @inString[findPos],
  patternlen) = 0 then

Und verwende stattdessen dieses:
ausblenden Delphi-Quelltext
1:
2:
if AnsiStrLComp(PChar(@findWhat[1]), PChar(@inString[findPos]),
  patternlen) = 0 then

Wenn das auch besser geht, nur zu und bitte um Info!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1645
Erhaltene Danke: 304

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mi 12.04.17 01:21 
Habs leider nicht geschafft die FindText() Methode entsprechend anzupassen, aber eine Lösung habe ich dennoch:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
uses
  StrUtils;

private
  FBackwardPos, FStartPos: Integer; // nicht lokal deklarieren!

function NextPos(S, SubStr: string; StartPos: Integer;
  var bwPos: Integer; Backward: Boolean = false): Integer;
var
  sTemp, subTemp: string;
  i: Integer;
begin
  if not Backward then
  begin
    if (StartPos >= Length(S)) then
      StartPos := 0;
    i := PosEx(SubStr, S, StartPos + Length(SubStr));
  end else
  begin
    sTemp := ReverseString(S);
    subTemp := ReverseString(SubStr);
    i := PosEx(subTemp, sTemp, StartPos + Length(subTemp));
    bwPos := i;
    i := Length(sTemp) - i - Length(subTemp) +2;
  end;
  result := i;
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
var
  Backward: Boolean;
begin
  with RichEdit1, FindDialog1 do
  begin
    Backward := not (frDown in Options);

    if SelLength <> 0 then
      FStartPos := SelStart else
      FStartPos := 0;

    if FStartPos = 0 then
    begin
      if Backward then
        FStartPos := NextPos(Text, FindText, FStartPos, FBackwardPos, true) else
        FStartPos := NextPos(Text, FindText, FStartPos, FBackwardPos, false);
    end else
      if Backward then
        FStartPos := NextPos(Text, FindText, FBackwardPos, FBackwardPos, true) else
        FStartPos := NextPos(Text, FindText, FStartPos, FBackwardPos, false);

    SetFocus;
    SelStart := FStartPos - 1// -1, weil Pos bei 1 anfängt und SelStart bei 0
    SelLength := Length(FindText);
  end;
end;

_________________
„Wo andere blind der Wahrheit folgen, denk daran ... Nichts ist wahr!" (Assassin's Creed I-II)
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 100
Erhaltene Danke: 2



BeitragVerfasst: Do 13.04.17 00:20 
Hallo galagher,
den link www.swissdelphicente...showcode.php?id=1878 habe ich aufgerufen. Allerdings muss ich nachdenken, wie ich diesen Code überhaupt in den FindDialog einbinden kann. (Code hab ich noch nicht verstanden)

Hallo Frühlingsrolle,
Lösung sieht zunächst gut aus. Das große ABER kommt beim Test. Die Funktion PosEx kommt offensichtlich mit einem String der Steuerzeichen enthält nicht zurecht. Konkret: Zeilenumbruch erfolgt mit den Steuerzeichen #0D#0A. Diese beiden Zeichen werden bei PosEx nicht richtig ausgewertet (als ein Zeichen mitgezählt). Das führt bei der späteren Markierung (SelStart, Sellength) zur Markierung falschen Textes; und zwar pro Zeile ein Zeichen zuviel.
Doch selbst wenn hier eine Korrektur gefunden wird: PosEx unterscheidet auch nicht zwischen Groß/Klein. Insoweit wäre es schön, wenn die Findtext-Funktion richtig arbeitet. Könnte ein Fehler in Delphi (Kompiler) vorliegen?
Vielleicht hat jemand Kontakt zu den Entwicklern? Ist einfach schade, wenn Beispiele in der Hilfe nicht funktionieren.
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1645
Erhaltene Danke: 304

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Do 13.04.17 01:13 
Ene mene Miste. Bei welcher Gelegenheit (nach unten / oben) gibt es Probleme mit Zeilenumbrüchen?
Getestet habe ich es nun mit D7 und D10.1. Das Ergebnis ist ident. Es wird keine Zeile ausgelassen oder übersprungen.

findDialog

Die Funktion PosEx() unterscheidet wohl zwischen der Groß und Kleinschreibung:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
var
  i: Integer;
  s: string;
begin
  s := 'ACDC';
  i := Pos('C', s); // = 2
  i := PosEx('C', s, i + 1); // = 4

  i := Pos('c', s); // = 0
  i := PosEx('c', s, i + 1); // = 0

  i := Pos('CD', s); // = 2
  i := PosEx('CD', s, i + 1); // = 0, weil kein zweites 'CD' vorkommt

  i := Pos('Cd', s); // = 0, weil 'Cd' nicht enthalten
  i := PosEx('CD', s, i + 1); // = 2, weil beim zweiten Durchsuchen 'CD' gefunden wird
end;
Einloggen, um Attachments anzusehen!
_________________
„Wo andere blind der Wahrheit folgen, denk daran ... Nichts ist wahr!" (Assassin's Creed I-II)
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2145
Erhaltene Danke: 42

Windows 7 64 Bit
Delphi XE2 Starter, Delphi 10.1 Starter
BeitragVerfasst: Do 13.04.17 20:35 
user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings muss ich nachdenken, wie ich diesen Code überhaupt in den FindDialog einbinden kann.
Normalerweise hätte ich geantwortet: Musst du nicht einbinden! Übergib an FindTextBackwardseinfach FindDialog1.FindText!
Der Code funktioniert aber nicht, jedenfalls nicht mit Delphi 10.1. Habe ihn wohl irgendwann gefunden, kopiert und in meine Codesammlung eingefügt, um mich später, wenn ich ihn benutzen möchte, damit auseinander zu setzen.

Ich habe einen Code, der findet das erste Vorkommen des Suchstrings, und wenn das letzte Vorkommen gefunden wurde, beginnt er wieder mit dem ersten. Ist aus der Hilfe für Delphi XE2. Aber einen Code, der rückwärts sucht, habe ich, so wie's aussieht nicht. :(

//Edit:
Mit einem TMemo funktioniert es. Es liegt offenbar an der Art und Weise, wie TRichEdit Zeilenumbrüche behandelt. Da muss ich noch feilen!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2145
Erhaltene Danke: 42

Windows 7 64 Bit
Delphi XE2 Starter, Delphi 10.1 Starter
BeitragVerfasst: Do 13.04.17 21:56 
Nützt dir das etwas?
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
var
  i: Integer;
begin
  for i := RichEdit1.CaretPos.Y-1 downto 0 do
  begin
    if Pos(Edit1.Text, RichEdit1.Lines[i]) > 0 then
    begin
      RichEdit1.SelStart :=
        RichEdit1.Perform(EM_LINEINDEX, i, 0);

      RichEdit1.SelStart := RichEdit1.SelStart+Pos(Edit1.Text, RichEdit1.Lines[i])-1;
      RichEdit1.SelLength := Length(Edit1.Text);
      break;
    end;
  end;

Na ja, sehr einfach und kann nicht viel, habe es auch noch nicht viel getestet, findet aber Text rückwärts und in verschiedenen Zeilen. Kann man ja noch ausbauen!

_________________
gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 100
Erhaltene Danke: 2



BeitragVerfasst: Di 18.04.17 21:52 
Ostern vorbei, wieder etwas Zeit. Wo fang ich an, wo hör ich auf?
1. Bei ene mene mu: Frühlingsrolle
Was ist bei mir anders? Habe Deinen Originalcode in mein Beispiel eingefügt und gegen den Code aus dem Delphibeispiel ersetzt. Es wird zwar vorwärts- und rückwärts gesucht, aber es wird falscher Text markiert (Selstart-Ergebnis ist falsch). Beigefügt eine zip-Datei. Compiliert mit XE3. Die zweite Anlage ist ein Screen-Ausschnitt vom Suchergebniss (schaffe es irgenwie nicht mein Bildchen hier sichtbar anzuhängen);

2. Die folgenden Befehle würde ich entfernen, da sie Suche ohnen Markierung immer bei Null beginnt.
ausblenden Delphi-Quelltext
1:
2:
3:
    if SelLength <> 0 then
      FStartPos := SelStart else
      FStartPos := 0;


2. Allen vorgeschlagenen "Ersatzlösungen" haben den Mangel, dass sie das Suchwort nicht finden, wenn die Groß-Klein-Schreibweise nicht passt. Dies liegt an den Funktionen Pos bzw. PosEx, die zeichengenau suchen. Zumindest habe ich keine Parameter gefunden, die Groß-Klein ignorieren.

3. Lösung von galagher findet den Suchbegriff in einer Zeile natürlich nur einmal (erstes Vorkommen von vorn). Ein Rückwärtssuchen von Pos

4. Bei galagher muss zusätzlich zwischen Zeile 11 und 12 noch SetFocus eingefügt werden. Ansonsten ist die Markierung nicht sichtbar
ausblenden Delphi-Quelltext
1:
2:
3:
  RichEdit1.SelStart := RichEdit1.SelStart+Pos(FindDialog1.FindText, RichEdit1.Lines[i])-1;
  Richedit1.SetFocus;
  RichEdit1.SelLength := Length(FindDialog1.FindText);


Fazit: Lösungsansatz von Frühlingsrolle ist weiter zu verfolgen. Werde versuchen die Ursache zu finden, warum bei mir Markierung falsch ist. Soviel für heute (bin schon zweimal aus dem Forum wegen Zeitüberschreitung raus geflogen)
Einloggen, um Attachments anzusehen!
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1645
Erhaltene Danke: 304

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Di 18.04.17 22:17 
Mein Ansatz war auch nicht ganz perfekt, denn das "Weitersuchen" soll auch irgendwann aufhören, wenn die letzte Zeile überschritten oder die erste Zeile unterschritten wird.
Ich werde mir dein Beispiel anschauen und es entsprechend "zurecht biegen".

Zu 2., der Code sollte nicht ausgelassen werden, denn beim ersten Suchen soll es auch ab Position 0 erfolgen, und im Anschluss an jener Position, wo die Selektion zuletzt erfolgt ist.
Das Prinzip der Suche "Rückwärts" basiert darauf, dass der Text im RichEdit, Rückwärts! in einer Variable vermerkt wird. Dabei wird nicht nur das Wort verdreht "Hallo -> ollaH", sondern auch der gesammte Text wird mit ReverseString() komplett "verdreht" ... erste Zeile wird zur letzten Zeile, vorletzte Zeile zur zweite Zeile usw.
Daher gilt die Position 0 für beide Fälle, also sowohl für die Suche Vorwärts wie auch für die Suche Rückwärts.

_________________
„Wo andere blind der Wahrheit folgen, denk daran ... Nichts ist wahr!" (Assassin's Creed I-II)
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 100
Erhaltene Danke: 2



BeitragVerfasst: Mi 19.04.17 18:43 
So, ich habe eine Lösung für AnsiString. Die funktioniert aber nicht bei Unicode!
Der Lösungsansatz folgt den Ideen von Frühlingsrolle (Text umkehren). Danach verwende ich die Standardfunktion "Findtext". Unter Delphi7 ist alles ok. Daher funktioniert vermutlich auch das Beispiel von Frühlingsrolle.
Übersetzt man das ganze jedoch mit XE3 (String=Unicode), dann gibt's Probleme mit der Positionierung (Berechnung von SelStart) und zwar:
a) mit fortschreitender Zeilenanzahl und
b) bei mehrfachem Vorkommen des Suchstrings in einer Zeile, wobei es bei nur einer Zeile funktioniert.
Im Debugger ist zu erkennen, dass nach der Anweisung "ReverseString" sich die Textlänge verändert hat. Warum dies so ist, kann ich derzeit noch nicht beurteilen, zumal ich beim Test keine Umlaute verwende. Vielleicht müsste man mit einem Null-terminierten String arbeiten (?). Aber die Zeichen nur Byteweise drehen geht bei Unicode ja nicht.
(Hat jemand Beziehung zu Embarcadero, wie die es machen?)


ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, StrUtils;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    FindDialog1: TFindDialog;
    Finden: TButton;
    procedure FindenClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FindDialog1Find(Sender: TObject);
  private
    { Private-Deklarationen }

  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FindenClick(Sender: TObject);
begin
  FindDialog1.Position :=
    Point(Form1.Left + RichEdit1.Width div 2, Form1.Top + Panel1.Height);
  FindDialog1.FindText:=Richedit1.SelText;
  FindDialog1.Execute;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i : integer;
    s : string;
begin
 s := '';
 for i:=1 to 20 do  begin
  Richedit1.Lines.Add(s +'Eines dieser Worte kann gesucht werden' );
  s := s + ' ';
  Richedit1.SelStart:=0;
 end;
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
//Befehle zur Vorwärts-Suche gemäß Delphi-Hilfe
var
  FoundAt, LgText: LongInt;
  StartPos, SelPos, ToEnd: LongInt;
  lgFindText: integer;
  mySearchTypes : TSearchTypes;
  myFindOptions : TFindOptions;
  sTemp,subTemp : string;
begin
  mySearchTypes := [];
  with RichEdit1 do
  begin
    lgFindText:= length(FindDialog1.FindText);
    if frMatchCase in FindDialog1.Options then
       mySearchTypes := mySearchTypes + [stMatchCase];
    if frWholeWord in FindDialog1.Options then
       mySearchTypes := mySearchTypes + [stWholeWord];
     {Beginne die Suche nach der aktuellen Auswahl, wenn es eine gibt. }
     {Andernfalls beginnen Sie am Anfang des Textes. }
    if frdown in FindDialog1.Options then
    begin {Vorwärtssuche}
     subTemp:=FindDialog1.FindText;
     if SelLength <> 0
       then StartPos := SelStart + SelLength    {dann nach Markierung starten}
       else StartPos := SelStart; {:=0 Start von Anfang}
       {ToEnd ist die Länge von StartPos bis zum Ende des Textes in Richedit}
       ToEnd := Length(Richedit1.Text) - StartPos;
    end
    else   {Rückwärtssuche}
     begin {Richedit.Text und FindText umkehren, SelStart merken, }
      Lines.Beginupdate;
      SelPos:=SelStart;
      lgText:=length(Richedit1.Text);
      sTemp:=Text;
      subTemp := ReverseString(FindDialog1.FindText);
      Text := ReverseString(sTemp);
      StartPos:= lgText-SelPos;
      ToEnd:= lgText;
     end;
  {Klein-Groß-Suche funktioniert nicht korrekt. Mit [] wird alles gefunden,
   aber Button im Feld wird ignoriert}

   FoundAt := FindText(subTemp, StartPos, ToEnd, mySearchTypes);
   if not (frdown in FindDialog1.Options) then
      Text:=sTemp; //Text wiederherstellen
   Lines.EndUpdate;
   if FoundAt <> -1 then  //gefunden
    begin
     if not (frdown in FindDialog1.Options) then
      begin //nach rückwärts-suchen
       SelStart:= lgText-FoundAt-lgFindText;
       FoundAt:=SelStart;
      end;
      SelStart := FoundAt;
      Perform(EM_SCROLLCARET, 00);
      Richedit1.SetFocus;
      SelLength := lgFindText;
    end
    else Beep;  //nicht gefunden, Dateigrenze erreicht
  end;  {with Richedit}
end;

end.
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 100
Erhaltene Danke: 2



BeitragVerfasst: Mi 03.05.17 20:23 
Hallo,
Was lange währt, wird endlich gut. Nun funktioniert die Rückwärtssuche! Anbei das Beispiel in der zip-Datei.
Worin bestand das Problem? Nach zahlreichen Tests zeigt sich:
1. FindText sucht nur in Vorwärtsrichtung (!), d.h. StartPos muss <= ToEnd sein. Ein Rückwärtssuchen nur weil StartPos > ToEnd ist, funktioniert nicht!
2. Die Funktion ReverseString beim Compiler (XE3, Delphi-Berlin 10.1) zeigt Fehler. Jeder Zeilenumbruch wird verdoppelt (s. Beispiel), so dass die spätere Markierung falsch positioniert, wenn der gesamte Text gesspiegelt wird.
3. FindText sucht nur in Richedit.Text (nicht z.B. in Edit.Text oder einer String-Variablen).
4. Die Lösung ist ein unsichtbares Richedit, in der immer nur eine Zeile steht.
5. Im Gegensatz zur Suche mit PosEx greifen nun auch alle Optionen (Groß/Klein, ganzes Wort).
Hoffe, das Beispiel hilft. Ich danke nochmals allen "Vor-Denkern".
hRb
Einloggen, um Attachments anzusehen!