Entwickler-Ecke
Grafische Benutzeroberflächen (VCL & FireMonkey) - Markierter Text in Richedit bearbeiten
hRb - Mi 19.02.20 19:24
Titel: Markierter Text in Richedit bearbeiten
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:
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 |
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:
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);
var j, lg : longint; SelSt,SelLg:longint; sL,sM,sR : string; ChaNr:integer; Begin with Form1.Richedit1 do begin if (Sellength=0) or (length(text)=0) then putalarm(ma2SelLg0) 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); 2: sM:=AnsiLowerCase(sM); 3: for j:=1 to SelLg do begin ChaNr:=ord(sM[j]); if ChaNr>64 then begin if (ChaNr<=90) then inc(ChaNr,32) else if (ChaNr<97) then else if (ChaNr<=122) then dec(ChaNr,32) else if (ChaNr<192) then else if (ChaNr<=222) and not (ChaNr=215) then inc(ChaNr,32) else if (ChaNr<=254) and not (ChaNr=215) and not (ChaNr=247) then dec(ChaNr,32); sM[j]:= chr(ord(ChaNr)); end; end; End; Text:=sL+sM+sR; end; SelStart:=SelSt; Sellength:=SelLg; end; end; |
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 Th69: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Do 20.02.2020 um 09:37
hRb - 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.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| function ReadSelWort: string; begin Result:=''; if SelLength > 0 then 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:
Delphi-Quelltext
1:
| x:=SendMessage(Richedit1.Handle, EM_LINEFROMCHAR, SelStart, 0) |
Ein Aufruf der die Zeilen des markierten Bereiches zählt (y) ist mir nicht bekannt.
Richtig wäre also:
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 - Mi 26.02.20 08:24
So, wie du es machst, habe ich es noch nicht versucht.
Laut Doku müsste es so funktionieren:
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 - 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:
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);
var j : longint; SelSt,SelLg: longint; s : string; ChaNr : integer; begin with Richedit1 do begin Fi[FiNrAktiv] if (Sellength=0) or (length(text)=0) then putalarm(ma2SelLg0) else begin SelSt:=Selstart; SelLg:=Sellength; s:=SelText; Case Big of 1: s:=UpperCase(s); 2: s:=LowerCase(s); 3: for j:=1 to SelLg do begin ChaNr:=ord(s[j]); if ChaNr>64 then begin if (ChaNr<=90) then inc(ChaNr,32) else if (ChaNr<97) then else if (ChaNr<=122) then dec(ChaNr,32) else if (ChaNr<192) then else if (ChaNr<=222) and not (ChaNr=215) then inc(ChaNr,32) else if (ChaNr<=254) and not (ChaNr=215) and not (ChaNr=247) then dec(ChaNr,32); s[j]:= chr(ord(ChaNr)); end; end; End; SelText:=s; SelStart:=SelSt; Sellength:=SelLg; end; end; end; |
Anmerkung: Mit UTF-16 Zeichen dürfte der Code unter Case 3: nicht funktionieren
jasocul - Do 27.02.20 08:05
hRb hat folgendes geschrieben : |
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.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!