SynEdit, UTF8, String-Bearbeitung

Rund um die LCL und andere Komponenten
lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

SynEdit, UTF8, String-Bearbeitung

Beitrag von lzuser »

Laz. 1.8.4 auf Win10 Experimente mit SyEdit
Ziel: Verständnis und Praxis im Umgang mit dem UTF8-Code

Ich habe mir jetzt eine Prozedur geschrieben, die mir zu einem Zeichen an der Cursorposition die Codierung ausgibt. Ich poste die mal, vielleicht interessiert es andere auch:

Code: Alles auswählen

 
uses LazUTF;
procedure TMyEditForm.OptionenUTF8CodeMenuItemClick(Sender: TObject);
{----------------------------------------------------------------------------}
function IntToBin(a:string):string;
{nicht von mir, Quelle unbekannt, funktioniert}
var b,c:string; u,i,z:integer;
begin
b:='';
repeat
  c:=a;  a:='';  u:=0;
  for i:=1 to length(c) do
      begin
      z:= Ord(c[i]) - 48 + (u*10);
      u:=z mod 2; z:=z div 2; a:=a + Chr(48+z);
      end;
  if a[1]='0' then delete(a,1,1);
  b:=Chr(48 + u) + b;
until length(a)=0;
result:=b;
end;
{----------------------------------------------------------------------------}
var Aus:string;
    P: PChar;
    AnzByte,Stelle:integer;
    UniCode:Cardinal;
 
begin
With SynEdit do
     begin
     //Zeichen im Text
     AnzByte:=UTF8CharacterLength(@SE.Text[SE.SelStart]);
     Aus := copy(Text, SelStart, AnzByte);
     Lines.Add('Zeichen im Text: '+Aus);
 
     //Unicode dez hex bin
     P:=PChar(copy(Text,SelStart,4)); //UTF8 bis 4byte
     //Man kopiert doch ein Zeichen, warum also nicht Len=1 statt 4
     //Kopiert der copy-Befehl nicht automatisch alle Bytes zu einem Zeichen?
     Unicode:=UTF8CharacterToUnicode(P,AnzByte);
     Aus:=IntToStr(Unicode);
     Aus:=Aus+' $'+IntToHex(UniCode,0);
     Aus:=Aus+' '+IntToBin(IntToStr(UniCode));
     Lines.Add('UniCode: '+Aus);
 
     //UTF8-Codierung: AnzBytes dez hex bin
     Aus:='AnzByte='+IntToStr(AnzByte);
     For Stelle:=0 to AnzByte-1 do Aus:=Aus+' '+IntToStr(Ord(P[Stelle]));
     For Stelle:=0 to AnzByte-1 do Aus:=Aus+' $'+IntToHex(Ord(P[Stelle]),0);
     For Stelle:=0 to AnzByte-1 do Aus:=Aus+' '+IntToBin(IntToStr(Ord(P[Stelle])));
     Lines.Add('UTF8-Codierung: '+Aus);
     end;
end;
 


Als Beisppielergebnis erhalte ich:

Code: Alles auswählen

 
A߀(Violinschlüssel)
Zeichen im Text: A
UniCode: 65 $41 1000001
UTF8-Codierung: AnzByte=1 65 $41 1000001
Zeichen im Text: ß
UniCode: 223 $DF 11011111
UTF8-Codierung: AnzByte=2 195 159 $C3 $9F 11000011 10011111
Zeichen im Text:
UniCode: 8364 $20AC 10000010101100
UTF8-Codierung: AnzByte=3 226 130 172 $E2 $82 $AC 11100010 10000010 10101100
Zeichen im Text: (Violinschlüssel)
UniCode: 119070 $1D11E 11101000100011110
UTF8-Codierung: AnzByte=4 240 157 132 158 $F0 $9D $84 $9E 11110000 10011101 10000100 10011110
 

Man kann sehr schön die Bitzuordnung zu den 1, 2, 3 oder 4 Bytes erkennen.

Meine Fragen bezogen sich auf die Ausgabe von Zeichen.
Allgemein: Wie arbeitet man mit den ganzen Stringbearbeitungsbefehlen unter UTF8?
Bei Zeichenorientierung müssten die entsprechenden Befehle (copy, inseret, delete, ...)
doch automatisch 1,2 oder 3 Byte benutzen oder muss ich das alles selbst handeln?
Gibt es eine andere Befehlsgruppe, die ich nutzen sollte?
Ein paar Erläuterungen wären mir sehr hilfreich - vielen Dank.
Zuletzt geändert von lzuser am Do 28. Feb 2019, 16:52, insgesamt 1-mal geändert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von mschnell »

"UTF8-Codierung: AnzByte=3 226 130 172 $E2 $82 $AC 11100010 10000010 10101100"

Da kann was nicht stimmen. Bei UTF8 ist beim letzten Byte einer "Codierung" das oberste Bit nicht gesetzt. Daran wird erkannt, dass es das letzte Byte ist.

-Michael

martin_frb
Beiträge: 572
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von martin_frb »

mschnell hat geschrieben:"UTF8-Codierung: AnzByte=3 226 130 172 $E2 $82 $AC 11100010 10000010 10101100"
Da kann was nicht stimmen. Bei UTF8 ist beim letzten Byte einer "Codierung" das oberste Bit nicht gesetzt. Daran wird erkannt, dass es das letzte Byte ist.


Wirklich?
Dann hat wikipedia das Falsch: https://en.wikipedia.org/wiki/UTF-8
;)

martin_frb
Beiträge: 572
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von martin_frb »

Code: Alles auswählen

 P:=PChar(copy(Text,SelStart,4))


AFAIK, kann der Kode crashen.

"Copy()" liefert einen neuen String mit ref-count=1 zurueck. Dieser ist in einer gespeichert.
Da der String nicht explizit einer Variablen zugewiesen wird, ist er nicht dauerhaft.

Sobald die TempVar woanders verwendet wird, zeigt der Pointer (PChar) ins Leere.

Bei der kurzen Lebenszeit des Pointer wird der Speicher wahrscheinlich nicht so schnell recycelt. Daher merkt man das nicht. Aber das Problem existiert...

martin_frb
Beiträge: 572
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von martin_frb »

UTF8 bis 4byte

Bis zu 4 Byte = Codepoint.
1 Zeichen kann laenger sein. "Combining Codepoints".

Kopiert der copy-Befehl nicht automatisch alle Bytes zu einem Zeichen?

Nein.
Utf8Copy tut das.

Aber Achtung

Code: Alles auswählen

i := Utf8Pos('a', s);
s2 := Utf8Copy(s,i,1);
 

Ist langsamer.

Um das i-te Zeichen zu finden, muss Utf8Copy alle Zeichen davor erneut analysieren. (Um den ByteIndex zu errechnen)

Utf8Copy, UTf8Len, Utf8Pos, ... basieren alle auf CodePoints

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von mschnell »

martin_frb hat geschrieben:Dann hat wikipedia das Falsch: https://en.wikipedia.org/wiki/UTF-8;)


Da hast Du wohl recht. Da steht "Das erste Byte beginnt immer mit 11, die folgenden Bytes mit 10. "

Danke für die Aufklärung.
-Michael
Zuletzt geändert von mschnell am Mi 27. Feb 2019, 12:37, insgesamt 1-mal geändert.

lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von lzuser »

Nach meiner Quelle
https://wiki.selfhtml.org/wiki/Zeichencodierung#ASCII
habe ich das so verstanden:
In allen Bytes steht vor den wertigen Stellen eine 0.
Wenn n>1 Bytes vorhanden sind, werden im ersten Byte n mal 1 davor gesetzt, in den folgenden Bytes eine 1 davor gesetzt.
Dann hat man im ersten Byte 8-n-1 wertige Stellen, in den folgenden Bytes 8-2 wertige Stellen.
Vielleicht kann man die Regel auch noch anders formulieren.
Aber danach sollten die Beispielausgaben von Lazarus stimmen.

Gibt es die "Combining Codepoints" =? "diakritische Zeichen" noch nicht alle vorkomponiert?
Wie geht denn SynEdit damit um?
Mit è=232 habe ich leider kein Beispiel.
Für 4 Byte fehlt mir auch noch ein Beispiel. Wie kann man denn den Violinschlüssel in SynEdit erzeugen?
Zuletzt geändert von lzuser am Mi 27. Feb 2019, 10:26, insgesamt 1-mal geändert.

martin_frb
Beiträge: 572
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von martin_frb »

Es gibt viele Buchstaben "vorkombiniert", nicht alle.

Eine Anwendung kann aber selbst wenn es vorkombiniert gibt, de-composed einfügen.
viewtopic.php?p=107906#p107906
viewtopic.php?p=107915#p107915

Wenn du in SynEdit tippst, nimmt SynEdit was immer das OS sendet (Utf8KeyPress).

Wie kann man denn den Violinschlüssel in SynEdit erzeugen?

SynEdit selbst hat da keine Hilfs-tools. Was immer das OS sendet. Also was immer das OS an Hilfstools bietet.

z.B. Character-Map und Kopieren/Einfügen
oder IME, wie für Smileys (Win10: Win-Key + Semikolon)

IME Unterstützung in SynEdit ist nur für Windows implementiert. In den Settings kann zwischen 2 Integrations-Stufen gewählt werden (IDE, Menu: Tools > Options > Editor > Misc: "Ime handled by System". Ohne diese Option ist die bessere Integration)

Man kann z.b.:
"win-;", dann "sun" tippen, das Wort erscheint im Editor. Return. Das Wort "sun" wird dann durch das Sonnen-Emoticon ersetzt.

Ohne die Option "Ime handled by System", stellt SynEdit das Wort "Sun" korrekt da. Mit korrekten Farben, z.B. "current line" background-color.
Mit der Option "Ime handled by System", malt Windows ueber den SynEdit. Und die Farben stimmen manchmal nicht.

Violinschlüssel => musical score ?

lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von lzuser »

Ich muss da noch mal nachhaken:
Wenn ich mit SynEdit umgehe, muss ich dann für die Stringbearbeitung immer die Utf8-Befehle verwenden?
Gilt das auch für die anderen Editierkomponenten TMemo, TEdit, ...?
Wenn in ganz(?) Lazarus Utf8 verwendet wird, warum leisten die normalen String-Befehle nicht das notwendige, sondern es wurden neue mit Utf8... eingeführt? Vielleicht kann ich die ganze Systematik irgendwo einigermaßen verständlich nachlesen?

In meinem SynEdit-Beispieltext "A߀" habe ich jetzt verwendet:
Aus:=Utf8Copy(SE.Text,SelStart,1);
SE.Lines.Add(Aus);

Die Zeichen A und ß werden angezeigt, nicht aber € .Die ausgegebene Codierung ist aber richtig.
Fragen über Fragen ...

wp_xyz
Beiträge: 4889
Registriert: Fr 8. Apr 2011, 09:01

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von wp_xyz »

Das Problem an deinem Code ist, dass hier UTF8 und Bytes durcheinandergewürfelt werden. UTF8Copy setzt voraus, dass sich der als Argument angegebene Startindex für die Kopier-Operation auch auf die UTF8-Codepoints bezieht. SelStart meint jedoch die Byte-Position im Text, die ist - wenn der Cursor vor dem € steht 4 ('A' ist 1 Byte, 'ß' sind 2 Bytes, Startindex ist 1), und nicht 3 (drittes Zeichen).

Der folgende Code arbeitet in der "Byte"-Welt::

Code: Alles auswählen

var
  aus: String;
  n: Integer;
begin
  n := UTF8CodepointSize(@SE.Text[SE.SelStart]);
  Aus := copy(SE.Text, SE.SelStart, n);
  SE.Lines.Add(Aus);   

UTF8CodePointSize aus LazUTF8 ermittelt die Byte-Länge des Codepoint ("Zeichen") an der aktuellen Cursorposition; sie benötigt einen PChar als Parameter, den man sich mit dem @-Operator erzeugen kann). Das "Standard"-copy holt sich dann genausoviele Bytes aus dem Originalstring.

Alternativ könntest du natürlich auch in der "Codepoint"-Welt arbeiten. Dazu müsstest du aber die Anzahl der Codepoints vor der aktuellen Cursorposition ermitteln, denn diese ist ja i.A. nicht dasselbe wie SelStart - und das ist relativ aufwending, gerade am Ende eines längeren Textes.

Zur Frage, ob UTF8 in ganz Lazarus verwendet wird: ja. Die UTF8-spezifischen Funktionen brauche ich aber fast nie. Das Problem liegt im Hirn: Solange man immer wieder davon ausgeht, dass 1 Zeichen 1 Byte groß ist, hat man Probleme. Erst als ich verstanden hatte, warum man mit einem Nicht-UTF8-Befehl wie "copy('ÄÖÜ-äöü', Pos('ü', 'ÄÖÜ-äöü'), Length('ü'))" das 'ü' aus dem Test-String herauskopieren kann, hat das ganze seinen Schrecken verloren.

Und warum das ganze? Die Welt ist zusammengewachsen. Während es zur DOS-Zeit sehr unwahrscheinlich war, dass auf dem Bildschirm ein Zeichen außerhalb der eigenen Code-Page auftaucht, ist es heute zu Zeiten des Internet ganz anders.

lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von lzuser »

Danke erst mal für deine umfangreichen Erläuterungen. Langsam kommt die UTF8-Welt aus dem "Nebel".
Noch ein Fragezeichen:
UTF8CodePointSize gibt es auch lt. Suche in LazUTF8 nicht, in welcher Unit dann?

wp_xyz
Beiträge: 4889
Registriert: Fr 8. Apr 2011, 09:01

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von wp_xyz »

Ach ja, das wurde neu aufgenommen, weil ein "Character" bei UTF8 eine schlecht definierte Größe ist. Nimm entweder UTF8CharacterLength aus LazUTF8 (das ist aber aus dem genannten Grund "deprecated" und kommt irgendwann weg), oder CodePointSize aus LazUnicode (mit denselben Aufrufparametern).

martin_frb
Beiträge: 572
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von martin_frb »

Utf8Copy/Pos/... vs Copy/Pos/...

1) Ein String kann, muss aber nicht Utf8 Data enthalten.
Selbst wenn er Utf8 enthält, macht manchmal Byte zugriff Sinn.

2) Utf8Copy ist langsamer als Copy.
Es ist wesentlich effektiver "Copy(s, Pos('ä',s), Lenght('ä'))" zu verwenden, als "Utf8Copy(Utf8Pos('ä',s), Utf8Lenght('ä'))" (wobei im letzteren Utf8Lenght('ä') = 1)

3) SynEdit bietet nur Byte Indeces. Keine CodePoint basierten Indeces.

lzuser
Beiträge: 97
Registriert: Sa 20. Jun 2009, 16:00
OS, Lazarus, FPC: Win10 20H2, Laz 2.0.8 auch Linux Mint Mate 20, Laz 2.0.6
CPU-Target: 64Bit

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von lzuser »

Für die Interessenten: Ich habe mein obiges Beispiel dank einiger Hinweise korrigiert.
Probleme macht nur noch die Darstellung des Violinschlüssels als 4Byte-Zeichen.
Im Firefox-Browser wird es richtig dargestellt, im Quelltext ist der richtige 4Byte-Code zu erkennen.
Dieses Zeichen habe ich nach SynEdit kopiert: keine Zeichendarstellung, aber die Codeermittlung klappt (siehe Ergebnis).
Bei der obigen Änderung habe ich beim Editieren das Violinzeichen auch erfolgreich (ist ja im Browser) darstellen können, beim Abschicken des Textes ins Forum kam aber eine Fehlermeldung bzgl. dieses "fehlerhaften" Zeichens. Nach Löschen des Zeichens und Ersatz durch "(Violinschlüssel)" konnte ich abschicken.
Sind solche Zeichen nicht erlaubt, oder...?

wp_xyz
Beiträge: 4889
Registriert: Fr 8. Apr 2011, 09:01

Re: SynEdit, UTF8, String-Bearbeitung

Beitrag von wp_xyz »

Ist der Violinschlüssel überhaupt in dem von SynEdit verwendeten Font enthalten?

Antworten