Autor Beitrag
hRb
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Mi 19.02.20 19:24 
Ich möchte in einem Richedit Text bearbeiten, nämlich:
1. Markierter Text in kleine Buchstaben
2. Markierter Text in große Buchstaben
3. Markierter Text tauschen (klein wird groß, groß wird klein)
Für Punkt 1. und 2. nutze ich fertige Routinen, für Pkt3 eigenen Code.
Lösungsansatz:
Eine Schleife über alle Zeichen mit Zurückschreiben in Text akzeptiert der Compiler nicht. Also so etwas:
ausblenden Delphi-Quelltext
1:
2:
3:
for j:=1 to length(Richedit.Text) do
  Cha j lesen, ändern und zurück mit 
  Richedit.Text[j]:=Char // dieser Befehl liefert Compilerfehler

Habe daher den RichText zerlegt in 3 Stringteile: sL= Text vor Markierung, sM= markierter Text, sR= Text nach Markierung. Bearbeite nun den markierten Text und setze die 3 Teile wieder zusammen. Hier der Code:

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:
Procedure FileCharChange(Big:ShortInt);
{Alle markierten Buchstaben der Datei in große/kleine Buchstaben wandeln
Big: 1=klein-> GROß, 2=GROß->klein, 3=tauschen}

var j, lg : longint;
    SelSt,SelLg:longint;
    sL,sM,sR : string;
    ChaNr:integer;
Begin
 with Form1.Richedit1 do begin
 if (Sellength=0or (length(text)=0then  {exit} putalarm(ma2SelLg0)  {Fehlermeldung: kein Bereich selektiert}
  else begin
   sL:=''; sM:=''; sR:='';
   SelSt:=Selstart; SelLg:=Sellength; lg:=length(Text);
   sL:=AnsiLeftStr(Text,SelSt);
   sM:=AnsiMidStr(Text,SelStart+1,SelLg);
   sR:=AnsiRightStr(Text,lg-SelStart-SelLg);
   Case Big of
     1: sM:=AnsiUpperCase(sM); {klein nach groß inkl. Umlaute}
     2: sM:=AnsiLowerCase(sM); {groß nach klein inkl. Umlaute}
     3for j:=1 to SelLg do   {Zeichen tauschen             }
        begin
          ChaNr:=ord(sM[j]);
          if ChaNr>64 then begin                    //unter 65 unverändert
            if (ChaNr<=90then inc(ChaNr,32)       //A..Z wird a..z
            else if (ChaNr<97then
            else if (ChaNr<=122then dec(ChaNr,32//a..z wird A..Z
            else if (ChaNr<192then                //unverändert,ebenso mal,div am Num-Block
            else if (ChaNr<=222and not (ChaNr=215then inc(ChaNr,32)
            else if (ChaNr<=254and not (ChaNr=215and not (ChaNr=247then dec(ChaNr,32);  //
            sM[j]:= chr(ord(ChaNr));
          end;
        end;
   End;  //Case
   Text:=sL+sM+sR;  //Textteile wieder zusammensetzen
 end;
 SelStart:=SelSt; Sellength:=SelLg;
endend;

Nun stelle ich fest, dass meine Rechnung zur Aufteilung in die 3 Teilstrings nicht korrekt funktioniert. Grund: Ich habe unterstellt, dass z.B. bei komplett markiertem Text die Werte von length(Text) = Sellength sind. Bei Markierungen über mehrere Zeilen ist dies jedoch nicht der Fall. Sellength zählt offenbar evtl. enthaltene Steuerzeichen #0D#0A (Zeilenwechsel) nicht mit. So kommt es bei der Bearbeitung zu einem falsch ausgewählten Textbereich.
Ein Test liefert mir kein logisches Ergebnis. Markiere ich z.B. mit Strg+A die gesamte Datei, dann werden genau entsprechend der Zeilenanzahl die letzten Zeichen nicht bearbeitet. Wenn schon ein solcher "Zählfehler", dann hätte ich die doppelte Anzahl nicht bearbeiteter Bytes wie Zeilenanzahl erwartet, da ja #0D#0A zwei Byte sind. Markiert man einen Zwischenbereich einer Textdatei, dann verschiebt sich sM nach vorn. Mit anderen Worten: Text[SelStart] zeigt nicht auf das am Bildschirm 1. markierte Zeichen.
1. Was denke/mache ich falsch?
2. Wie kann man das Problem lösen?
3. Warum liefert Richedit.Text[j]:=chr(ord(ChaNr)) einen Compilerfehler?
hRb

Moderiert von user profile iconTh69: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Do 20.02.2020 um 09:37
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Di 25.02.20 23:04 
Hallo, nach 6 Tagen Wartezeit gab es keine Antwort. Vielleicht habe ich die Frage zu kompliziert formuliert. Hier ein neuer Ansatz:
Warum liefert die nachfolgende Anweisung in Richedit nicht den selektierten Text, wenn es sich nicht um Zeile eins handelt?
Ich habe erwartet, dass SelStart auf das 1. markierte Byte zeigt und SelLength die Anzahl der markierten Byte. Demnach müsste nachstehende Funktion markierten Text liefern.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function ReadSelWort: string;
//liefert den markierten Text (TUT ES ABER NICHT !!!)
begin
  Result:='';
  if SelLength > 0 then  //es gibt markierten Text
     with Form1.Richedit1 do Result:=copy(Text,SelStart+1,SelLength);
end;

Korrekt wird das Ergebnis erst, wenn man auf SelStart die Zeilenzahl x addiert in dem der markierte Text steht und auf SelLength die Anzahl der Zeilen y, wenn die Markierung über mehrere Zeilen geht. Die aktuelle Zeile ermittelt man mit:
ausblenden Delphi-Quelltext
1:
  x:=SendMessage(Richedit1.Handle, EM_LINEFROMCHAR, SelStart, 0// x liefert Zeilenzahl des Cursors in Richedit1					

Ein Aufruf der die Zeilen des markierten Bereiches zählt (y) ist mir nicht bekannt.
Richtig wäre also:
ausblenden Delphi-Quelltext
1:
    Result:=copy(Text,SelStart+1 + x, SelLength + y);					

Die Funktion Strg+C arbeitet korrekt. Wie also liest man markierten Text korrekt in einen String?
hRb
PS: arbeite mit Delphi 10.1 Berlin mit Update2
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Mi 26.02.20 08:24 
So, wie du es machst, habe ich es noch nicht versucht.
Laut Doku müsste es so funktionieren:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
function ReadSelWort: string;
begin
  Result:= Form1.Richedit1.SelText;
end;


P.S.:
Tu dir selbst einen gefallen und arbeite bitte nicht mit "with"
hRb Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 267
Erhaltene Danke: 12



BeitragVerfasst: Mi 26.02.20 21:45 
Vorbemerkung: Oft lese ich bei einem interessanten Thema: "Lösung gefunden", ohne dass bekannt wird wie die Lösung aussieht. Ich will mich anders verhalten und hoffe auf viele Nachahmer.

Danke, an SelText habe ich irgendwie nicht gedacht. Und SelText liefert tatsächlich den korrekt markierten Bereich. Es hat allerdings etwas gedauert, um die gewünschte Funktion korrekt zu lösen. Ich beschreibe den Weg zur Lösung, damit andere nicht den gleichen Fehler begehen.
Ziel war es - wie oben erwähnt - nur den markierten Textbereich in Richedit zu bearbeiten. Ich bildete Textteile (davor, danach) und wollte diese wieder zusammen setzen. SelText liefert zwar den korrekt markierten Bereich, allerdings blieb das Zeilenproblem beim Berechnen der Textteile (vor, nach der Markierung).
Der Grund:
Sieht man im Debugger den Rich-Text an, so erkennt man als Zeilenumbruch die Steuerzeichen #0D#0A. Nach dem Befehl s:=SelText, zeigt der Debugger in SelText den Zeilenumbruch nur noch mit #0D an, d.h. alle #0A sind entfernt !!!! (Warum??: man frage die Delphi-Entwickler). Arbeitet man nun mit length(s) weiter, so fehlt pro Zeilenumbruch ein Byte und dies erzeugt den Fehler. Über den Tipp mit SelText zu arbeiten fand ich in der Delphi-Hilfe, dass man SelText auch wieder einen Wert zuweisen kann und das war die Lösung (hierbei wird aus #0D im String im Richtext wieder #0D#0A).
Hier die funktionsfähige Prozedur:

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:
Procedure FileCharChange(Big: ShortInt);
{Markierter Text in Richedit wandeln: Big: 1=klein -> GROSS, 2=GROSS -> klein, 3= alternativ tauschen}
var j          : longint;  //Laufvariable
    SelSt,SelLg: longint;  //Markierbereich merken zum Restaurieren
    s          : string;   //Bearbeitungsstring
    ChaNr      : integer;  //Bearbeitungszeichen
begin
  with Richedit1 do begin    Fi[FiNrAktiv] 
    if (Sellength=0or (length(text)=0then  putalarm(ma2SelLg0)  {Fehlermeldung: kein Bereich selektiert}
    else begin             //weitere Bearbeitung
      SelSt:=Selstart; SelLg:=Sellength;
      s:=SelText;          //zu bearbeitender Text
      Case Big of
       1: s:=UpperCase(s); //klein nach groß inkl. Umlaute
       2: s:=LowerCase(s); //groß nach klein inkl. Umlaute
       3for j:=1 to SelLg do
          begin
            ChaNr:=ord(s[j]);
            if ChaNr>64 then begin                    //unter 65 ('A') bleibt unverändert
              if (ChaNr<=90then inc(ChaNr,32)       //A..Z wird a..z
              else if (ChaNr<97then                 //unverändert
              else if (ChaNr<=122then dec(ChaNr,32//a..z wird A..Z
              else if (ChaNr<192then                //unverändert, 
              else if (ChaNr<=222and not (ChaNr=215then inc(ChaNr,32)  //mal,div am Num-Block
              else if (ChaNr<=254and not (ChaNr=215and not (ChaNr=247then dec(ChaNr,32);  
              s[j]:= chr(ord(ChaNr));
            end;
          end;
      End;
     SelText:=s;            //geänderter Text wieder nach Richedit.Text
     SelStart:=SelSt; Sellength:=SelLg;  //Text neu markieren
    end;
  end;
end;

Anmerkung: Mit UTF-16 Zeichen dürfte der Code unter Case 3: nicht funktionieren

Für diesen Beitrag haben gedankt: jasocul
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Do 27.02.20 08:05 
user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:
Vorbemerkung: Oft lese ich bei einem interessanten Thema: "Lösung gefunden", ohne dass bekannt wird wie die Lösung aussieht. Ich will mich anders verhalten und hoffe auf viele Nachahmer.

Danke. Nur so kann ein gutes Forum funktionieren.