length(s) bei Umlauten

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

length(s) bei Umlauten

Beitrag von siro »

Hallo zusammen,

mir ist grade eine "Merkwürdigkeit" aufgefallen mit der Funktion length.

Hier mal der Code dazu:

Code: Alles auswählen

var s:ShortString;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  s:='äöüÄÖܵ°';
  caption:=IntToStr(length('äöüÄÖܵ°'))// ergibt 16
  caption:=IntToStr(length(s));           // ergibt 16
  caption:=IntToStr(Byte(s[0]));          // ergibt 16
end;         


Ich vermute mal, das liegt daran, dass die Texte im UTF-8 Format abgelegt werden.
Kann man das irgendwie steuern, weil eigentlich habe ich ja nur 8 Zeichen.
Ich dachte bei einem ShortString belegt jeder Buchstabe auch nur ein Byte.

Wie macht man das am besten "richtig" damit auch die Länge bei Umlauten stimmt ?

Siro

Windows 7 64 Bit Lazarus 2.0.0RC2 FPC 3.0.4
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: length(s) bei Umlauten

Beitrag von m.fuchs »

Nimm UTF8Length aus der Unit LazUTF8 und vergiss den Zugriff auf das 0-Byte.

Code: Alles auswählen

var s:ShortString;
begin
  s:='äöüÄÖܵ°';
  caption:=IntToStr(UTF8Length('äöüÄÖܵ°'))// ergibt 8
  caption:=IntToStr(UTF8Length(s));           // ergibt 8
  caption:=IntToStr(Byte(s[0]));          // ergibt 16
end;
 
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: length(s) bei Umlauten

Beitrag von siro »

Supi, vielen Dank, diese Funktion kannte ich noch nicht, (bin noch ein alter Turbo Pascaller) :oops:

In Delphi 6 kommt da auch 8 raus, hier muss man also "älteren" Code entsprechend anpassen.

Dann wünsche ich allen ein erholsamens Wochenende.

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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: length(s) bei Umlauten

Beitrag von martin_frb »

UTF8Length ist zwar besser aber nicht perfekt. (afaik)
UTF8Length gibt die Länge in "Codepoints", nicht in Buchstaben.

Die meisten Buchstabes haben auch nur einen CodePoint.
Alle gängigen Umlaute können als ein Codepoint vorkommen.

"können" => müssen aber nicht. "ä" kann als ein codepoint existieren. "ä" kann aber auch als "a" gefolgt von "2 punkte über dem vorherigen Buchstaben" existieren.
Im 2ten Fall sind es 2 Codepoints. (google combining codepoints)

Das Problem geht dann noch weiter: pos("ä", "Länge") kann ggf nix finden, wenn "ä" in verschiedenen Kodierungen vorliegt. (google utf8 normalization)

--
"combining codepoints" gibt es auch in Utf16 und Utf32. Also auch widestrings haben dieses Eigenart.

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: length(s) bei Umlauten

Beitrag von pluto »

UTF8Length ist zwar besser aber nicht perfekt. (afaik)
UTF8Length gibt die Länge in "Codepoints", nicht in Buchstaben.

und was für Alternativen gibt es um, die länge jetzt richtig zu ermitteln?
MFG
Michael Springwald

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: length(s) bei Umlauten

Beitrag von martin_frb »

Gute Frage, aber die Antwort hab ich auch nicht.

Eigentlich sollten (?) die meisten OS irgendeine Unicode (gilt für alle Encodings utf8,16,32...) haben. Da wäre der Vorteil das die Updates kriegen, wenn der Unicode Standard erweitert wird. Alte apps würden dann trotzdem korrekt funktionieren.

Die Anzahl der "Combining Codepoints" ist gering. SynEdit hat eine Liste. Damit kann man schnell die Length Funktion basteln.

Allerdings alle anderen Funktionen von LazUtf8 (UtfCopy, Utf8Pos, ...) basieren auf Codepoints.

Und zum Vergleichen braucht man wesentlich mehr. Nicht nur das "ä" = "a"+"Umlaut". Uppercase "i" ist Sprachabhängig, es gibt ein großes I mit Punkt: İ. Und ein kleines i ohne Punkt. Und keine Ahnung was noch alles.

Außerdem: Was genau ist "Ein Buchstabe" ? Weil man kann an ein Utf8 Codepoint 1000 und mehr combinings hängen: ḓ̵̙͎̖̯̞̜̞̪̠


Wenn man beim Buchstaben danach geht, was die kleinste "editierbare" Einheit ist, dann kann man "a"+"Umlaut" als zwei Zeichen sehen. Man kann jeden der beiden Teile löschen/ersetzen und erhält gültigen Unicode.

Ja "ä" mag ein "Bildschirm Zeichen" (Glyph) sein. In den meisten Fonts zumindest....
Aber Ti ist in manchen Fonts auch nur ein Glyph (Ligatur), wie viele Buchstaben sind Ti dann?

Sind Leerzeichen Buchstaben?

Sind Zeichen, die keine Darstellung haben Buchstaben? (zero-width space, RTL/LTR marker, and some others)
Oder https://en.wikipedia.org/wiki/Zero-width_joiner => Sind die beiden Umgebenen Zeichen dann immer noch 2 Buchstaben, und ist der Joiner ein dritter Buchstabe....

Die Antworten hängen davon ab, was man mit den Zeichen machen will.

Wenn man zum Beispiel einen Text in 2 Teile teilen will, dann natürlich nicht mitten in einem Zeichen. Aber RTL/LTR marker, auch wenn nicht an der Trennstelle müssen ggf in beiden Teilen erscheinen. Und wenn an der Trennstelle, dann müssen Sie im korrekten Teilstring erhalten bleiben.

Ebenfalls wäre es schlecht an einem Joiner zu trennen... Der Joiner und die beiden Zeichen darum, müssen zusammen bleiben.
Aber Text markieren (für Ctrl-C), kann ok sein für nur das Zeichen davor, oder nur danach.

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: length(s) bei Umlauten

Beitrag von siro »

UiJuiJui,
ich wollte doch nur die Länge haben..... :shock:
Hier muss ich nochmal Danke sagen, für die "reichhaltigen" Informationen.

Was war das früher einfach, da hatte jeder Buchstabe sein eigenes Byte und fertig.
Umlaute und Sonderzeichen waren aber damals schon teils unterschiedlich,
es ist halt nur bis 0x7F genormt und dann beginnt das Chaos.
Momentan scheint ja eher der UTF8 ein inoffizieller Standard zu sein.

Eigentlich bin ich garnicht durch Lazarus auf diese Eigenheit gestossen,
sondern durch meine C Source Code Dateien. Ich habe mir eine .c Datei mit einem
Hexeditor angesehen, weil ich wissen wollte welchen Uni-Code das "Mikro" Zeichen hat.
Dann hab ich zum verblüffen gesehen, dass die ersten 3 Bytes meiner Datei sehr merkwürdig aussahen.
So habe ich in die aller erste Zeile, ganz oben links ein A Buchstaben gesetzt und
mir wieder den Hexcode angesehen.
Nun hätte ich dort ein 0x41 vermutet, aber so ist es nicht, sondern dort stand
vorab eine 3 Byte Sequenz 0xEF 0xBB 0xBF

Nach etwas googeln findet man Folgendes:
Diese Bytefolge dient als Kennung zur Definition der Byte-Reihenfolge und Kodierungsform in UCS/Unicode-Zeichenketten, insbesondere Textdateien.

So fragte ich mich wie man denn die Textlänge überhaupt noch ermitteln kann und habe es mit Lazarus probiert.
Und dann stellte ich fest, das es ja auch garnicht mehr richtig funktioniert. :cry:

Das scheint ja heutzutage ein "riesen" Problem zu sein mit Texten zu arbeiten.
Aber verwunderlich finde ich schon, dass "einige" meiner Sourcen vorn eine 3 Byte Sequenz haben und andere nicht.

Mit dem Codepoint habe ich zwar gelesen aber nicht wirklich verstanden.
Ich wollte da eigentlich auch keine Wissenschaft draus machen, aber Textverarbeitung kommt mir nicht mehr in die Tüte.
Nur das nötigste für Beschriftungen, da es hier anscheind keine wirkliche Norm mehr gibt.
Das ist in meinen Augen das reinste Chaos :shock: , oder ich bin schon zu alt dafür. :mrgreen:

Mit den UTF8 und der entsprechenden Unit kann und muss ich wohl erstmal leben, das scheint ja richtig zu funktionieren.

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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: length(s) bei Umlauten

Beitrag von martin_frb »

siro hat geschrieben:UiJuiJui,
ich wollte doch nur die Länge haben..... :shock:

Und... Die Antworten sind halt "länger" geworden. ;)

Momentan scheint ja eher der UTF8 ein inoffizieller Standard zu sein.

Utf8 ist ganz offiziell.

Die meisten beschriebenen Eigenarten sind UNICODE.
Utf8 ist eine Kodierung für den Unicode.

vorab eine 3 Byte Sequenz 0xEF 0xBB 0xBF

Utf8 BOM (google)

So fragte ich mich wie man denn die Textlänge überhaupt noch ermitteln kann und habe es mit Lazarus probiert.
Und dann stellte ich fest, das es ja auch garnicht mehr richtig funktioniert. :cry:

Wie gesagt, was genau ist die Laenge eines Textes. Was wird erwartet?

Mit dem Codepoint habe ich zwar gelesen aber nicht wirklich verstanden.


CodePoint ist ein unicode "zeichen". Ein Zeichen kann alles moegliche seien. Ein Buchstabe, eine control Sequence, eine Erweiterung fuer einen Buchstaben, ....

U+200D ist zB ein Codepoint.
https://www.fileformat.info/info/unicod ... /index.htm

In UTF8 braucht der 3 CodeUnits (Utf8 CodeUnit = 1 Octed = 1 byte) 0xE2 0x80 0x8D
In Utf16 braucht der 1 CodeUnit (utf16 CodeUnit = 1 word = 2 byte).

"ä" kann 1 Codepoint sein: https://www.fileformat.info/info/unicod ... /index.htm

"a"+ "Umlaut" sind 2 Codepoints: https://www.fileformat.info/info/unicod ... /index.htm und https://www.fileformat.info/info/unicod ... /index.htm

"a"+ "Umlaut" formen zusammen ein "ä".
Ist derselbe Buchstabe, aber anders repräsentiert.

Die Codepoints sind Unicode, d.h. "a"+ "Umlaut" gibt es in utf8/16/32/...
Denn utf8/16/32/... sind alles Kodierungen für Unicode.

----------------------
Anmerkung: UTF16 macht die Sache noch etwas komplizierter.
https://www.fileformat.info/info/unicod ... /index.htm
Ein Codepoint.
In Utf16 ist das ein "surrogate pair": Zwei 16-bit words
In Utf8 sind das eine 4 byte sequence, nach denselben regeln wie alle andern Codepoints (allerdings hallten sich nicht alle dran, und manche machen 2 Sequencen daraus...)

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: length(s) bei Umlauten

Beitrag von theo »

Meine alten UTF8Tools enthalten einigen Beispielcode dazu http://www.theo.ch/lazarus/utf8tools.zip

In der Demo "charandscan" gibt es ein Beispiel für "Normalisierung". Also die Rückführung von verschiedenen Arten z.B. einen Umlaut zu codieren auf eine einzige Art, um Vergleiche möglich zu machen.
Auch eine Demo für Digraphen ist dabei wie lj -> Lj .
In der Demo ist lj ein einzelner Buchstabe, lässt sich also nicht getrennt auswählen. Nach der Normalisierung sind es zwei.

(Falls der Compiler meckert, einfach "uses LazUTF8;" hinzufügen.)

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: length(s) bei Umlauten

Beitrag von siro »

Ach Du heiliger Strohsack :shock: , oder erstmal einen schönen guten Morgen zusammen,
Einen großen Dank an martin_frb für die Mühe der Zusammenstellung/Erklärungen mit den LINKs
und auch an "theo" für die Tools, darin stöbere ich grade.

Ich dachte immer "C" ist das sprichwörtliche "C"haos, aber die Stringverwaltung
ist ja ein absoluter Albtraum geworden, da blickt doch keiner mehr durch,
okay, ihr seid da jetzt ausgenommen... :lol:
Bischen meckern muss sein, ich find das echt sau kompliziert.

Die Typvielfalt ist hier auch nochmal schön aufgeführt, habe ich grad gesehen:
http://wiki.freepascal.org/Character_and_string_types

Hier gibt es ja auch einige von euch die das FPC schon auf kleineren Controllern verwenden,
was stellt ihr da ein ?, habe ich das richtig verstanden, dass man mit dem Paramter

{$H+} den "ursprünglichen Turbo-Pascal String aktiviert.
Das wäre dann der Shortstring oder Ansistring.

{$LONGSTRINGS ON} tut scheinbar das Gleiche.
Es werden immer 256 Bytes reserviert, daher kommt wahrscheinlich auch der Name LONGSTRING

var String[len] wobei len 1..255, erzeugt generell einen Ansistring
unabhängig von der globalen Einstellung und reserviert len+1 Byte Speicher.

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: length(s) bei Umlauten

Beitrag von theo »

siro hat geschrieben:Bischen meckern muss sein, ich find das echt sau kompliziert.


Unicode ist halt so, weil alle Schriften der Welt da rein müssen und es auch noch oft mehrere Repräsentationen gibt.

Das bedeutet aber nicht, dass man für jedes Progrämmchen auf alles Mögliche Rücksicht nehmen muss.

Ich denke Unicode bzw. Mehrsprachigkeit wird in Anwendungsprogrammen fast immer nur bis zu einer gewissen Schmerzgrenze implementiert.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: length(s) bei Umlauten

Beitrag von Timm Thaler »

siro hat geschrieben:aber die Stringverwaltung
ist ja ein absoluter Albtraum geworden


Ich hatte da am Anfang auch mit zu kämpfen, weil ich nich die guten alten Strings von Turbo Pascal im Hinterkopf hatte.

Aber: Einfach immer konsequent Unicode / Utf8 verwenden. Aufpassen, dass auch Config-Files in Unicode sind. Zum Schreiben und Lesen von XML muss man dann die laz2_XML Units nehmen, das ist unkonsequent umgesetzt. Zum Glück können auch die meisten aktuellen Editoren Utf8, so dass es mit Textdateien selten Probleme gibt. Eine Hilfe ist auch die Konvertierung von Notepad++.

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

Re: length(s) bei Umlauten

Beitrag von wp_xyz »

siro hat geschrieben:{$H+} den "ursprünglichen Turbo-Pascal String aktiviert.
Das wäre dann der Shortstring oder Ansistring.

{$LONGSTRINGS ON} tut scheinbar das Gleiche.
Es werden immer 256 Bytes reserviert, daher kommt wahrscheinlich auch der Name LONGSTRING

var String[len] wobei len 1..255, erzeugt generell einen Ansistring
unabhängig von der globalen Einstellung und reserviert len+1 Byte Speicher.

Möglicherweise verstehe ich dich nicht. Aber {$H+} bewirkt, dass der Typ "string" gerade die neuen Strings unbegrenzter Länge meint, die auf dem Heap gespeichert sind, genauso wie {$LONGSTRINGS ON}. {$H-} dagegen nimmt die alten Turbo-Pascal-Strings mit max 255 Zeichen Länge. Je nach Compiler-Modus ist der Default manchmal {$H+}, manchmal {$H-}. Damit man die "neuen" Strings hat, wird der Compiler-Modus fpcobj immer mit H+ kombiniert: {$MODE fpcobj}[$H+}

Und lass dich durch den Begriff "AnsiString" nicht verwirren, das kann sich sowohl auf code-seiten-basierte Strings beziehen, die 256 Zeichen im Zeichenvorrat haben, als auch auf die UTF8-codierten Strings, bei denen ggfs mehrere Bytes zu einem "Zeichen" (eigentlich: Code-Point) zusammengefasst sind und alle möglichen Zeichen dargestellt werden können. Es ist nur gemeint, dass der String aus 1-Byte Einheiten besteht, im Gegensatz zu "WideString" oder "UnicodeString", wie die Einheit 2 Byte groß ist.

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: length(s) bei Umlauten

Beitrag von siro »

Danke wp_xyz für den Hinweis,
das habe ich tatsächlich falsch interpretiert. :oops:

Dann muss ich also für Turbo-Pascal Strings ein {$H-} einstellen.

Ist echt nicht einfach in meinem Alter... :wink:
danke nochmal an Alle für eure Geduld und Mühe.

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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: length(s) bei Umlauten

Beitrag von martin_frb »

siro hat geschrieben:Ach Du heiliger Strohsack :shock: , oder erstmal einen schönen guten Morgen zusammen,
{$H+} den "ursprünglichen Turbo-Pascal String aktiviert.
Das wäre dann der Shortstring oder Ansistring.


ShortString => Ja

Der Name "Ansistring" wird mittlerweile für alles mögliche verwandt (egal ob Ansi oder was anderes drin ist).
Allerdings wird Ansistring üblicherweise für Longstring (mananged strings / string mit beliebiger Länge verwandt).

Nur zur Info/Warnung:
Egal ob H+ oder H- / Egal ob Short oder Long-string...
Der Inhalt wird davon nicht beeinflusst.

Code: Alles auswählen

 TurboShortstring := 'ä';
 ModernLongString := 'ä'

In beiden Fällen gibt das UTF8 (außer wenn der SourceCode explizit in einer anderen (ANSI) code page ist, und man das dem Compiler ggf auch mitteilt. Dann lässt sich in *beiden* Fällen auch ASCII/ANSI benutzen. (Allerdings verliert man dann alle Buchstaben die nicht in der verwendeten ANSI code page enthalten sind.)

Ein Unterschied gibt es: ModernLongString kann intern die Info speichern welche Codepage verwandt wird. (Und ggf in einigen Fällen Konvertierungen auslösen)

Die eigentlichen Unterschied sind:
- Ablageort im Speichen
- ref counting / Copy on write
- Argument/Parameter als Referenz
- Maximale Länge

Antworten