Hallo
Ich denke den einen oder anderen könnte der Artikel "Widechar sets for
Unicode" von P. Below, welcher im
forums.codegear veröffentlicht wurde, interessieren.
Assertor war so freundlich, und hat den Artikel ins Deutsch übersetzt. Herzlichen Dank!
Delphi 2009 hat nun endlich den vollständige Wechsel zu
Unicode geschafft, der Standard-Stringtyp ist nun ein
Unicode-String und Char ist ein WideChar (16 bit). Eins der Opfer dieser Änderungen ist die gute alte Praxis die Eingabedaten mit Ansätzen wie folgt zu prüfen:
if Key In ['0'..'9'] then
....
Da in Delphi ein set maximal 256 Elemente haben kann, ist ein set of Char nicht länger möglich. Stattdessen wird ein set wie oben genannt als set of Ansichar (TSysCharset) kompiliert und der Quellcode (mit Key als Datentyp Char) erzeugt eine Warnung. Das funktioniert solange Key in dem Bereich von #0 bis #127 ist (dem 7-bit
ASCII Bereich), aber das Ergebnis ist undefiniert wenn dies nicht der Fall ist.
Was können wir nun als Gegenstück verwenden? Delphi bietet die CharInSet Funktion, die zumindest die Warnung unterdrückt aber ebenfalls für alle Zeichen oberhalb von #127 versagt. Das nutzt vielleicht Amerikanern aber nicht dem Rest der Welt <g>.
Die WidechatSetU-
Unit in diesem Archiv (
Download bei cc.codegear.com) zielt darauf ab dieses Problem zu lösen, in dem ein Gegenstück zu set of WideChar geboten wird. Die Verwendung ist etwas abweichend von dem klassischem set, da es keine eingebaute Kompilerunterstützung für "widechar set" gibt und die Programmiersprache auch keine Unterstützung für überladene Operatoren in Interface-Typen enthält (wobei ich Interfaces als Basis für die Implementation gewählt habe).
Ein üblicher Test wie
if Key In ['0'..'9'] then
....
würde nun so durchgeführt
if Digits.Contains(Key) then
...
Dies nutzt eine der vordefinierten widechar sets. Die Sets sind Singletons, so daß deren Nutzung einen geringen Overhead erzeugt. Wenn Ihr ein eigenes set erzeugen müsst, funktioniert dies mit Hilfe der WidecharSets Factory:
if Key in ['0'..'9', 'a'..'z', 'A'..'Z', '$'] then
...
würde zu
if Widecharsets.Create(['0'..'9', 'a'..'z', 'A'..'Z','$']).Contains(Key) then
Da dies für jeden Aufruf ein neues widechar set erzeugen würde, wäre es effizienter das set einmalig zu erstellen und für die Lebenszeit des Objekts in dem der if Test enthalten ist zu behalten. Der Konstruktor (oder der OnCreate-Event des Formulars) wäre ein guter Platz dafür:
Delphi-Quellcode:
// Objektfeld
FAllowedChars: IWidecharSet;
// Konstrukturaufruf
FAllowedChars := Widecharsets.Create(['0'..'9', 'a'..'z', 'A'..'Z','$']);
Damit würde der Test zu
if FAllowedChars.Contains(Key) then
Da die Lebenszeit der widechar sets über die Interface-Referenzzählung gesteuert wird, muß man sich nicht selbst darum kümmern, daß der von diesen genutze Speicher freigegeben wird wenn sie nicht mehr gebraucht werden.
Die Erzeugung eines widechar set mit Eingabedaten in Form eines set von
ANSI Zeichen funktioniert auch, selbst wenn das
ANSI-set Zeichen über #127 enthält. Die Widecharsets.Create Methode, die das set als Eingabedaten entgegen nimmt (widechar sets können auch von
Unicode-Strings oder anderen widechar sets erstellt werden) enthält einen optionalen Encoding-Parameter, der die CodePage für die übergeben sets enthält. Dieses Encoding Objekt wird zur Konvertierung der
ANSI Zeichen oberhalb von #127 des Eingabe-set zu
Unicode-Zeichen verwendet (UTF-16), damit diese im widechar set genutzt werden können.
Widechar sets (und die Widechar Factory) unterstützt alle set Operationen. In der
HTML Hilfedatei des Archivs sind die Mitglieder des IWidecharSet (stellvertretend für ein widechar set) und IWidecharSets (das Factory-Interface welches von WidecharSets Funktionen zurückgeliefert wird) beschrieben.
Der Quellcode der Units in diesem Archiv enthält keinerlei Nutzungseinschränken, nutzt es so wie Ihr wollt (ausgenommen des Verkaufs!), aber es gibt keinerlei direkte oder indirekte Garantien bzw. Gewährleistung. Ich habe einen Satz von Unittests für den Code (im Archiv enthalten) und ich bin ziemlich sicher, daß der Code so funktioniert wie er ist. Der Code nutzt die
JEDI.INC Datei von der
JEDI Code Bibliothek (JCL, see
http://delphi-jedi.org) für Kompilerdirektiven. Wenn Ihr die Units aus diesem Archiv mit Delphi Versionen neuer als 2009 verwendet, stellt sicher das Ihr eine neue Version dieser Datei nutzt. Anderenfalls könnte der Code die Kompilerversion möglicherweise nicht korrekt feststellen. Die JCL hat ihre eigene Distributionslizenz, bitte lest den Kommentar am Anfang der
JEDI.INC.
Have fun!
Peter Below
--
Peter Below (TeamB)
(Übersetzt von Assertor)