UTF8 und Character

Anfängerfragen zum Programmieren mit PureBasic.
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

UTF8 und Character

Beitrag von SebastianJu2 »

Wie ist das eigentlich mit der Char-Größe, also dessen Speichergröße, wenn man ein Programm braucht das sowohl mit UTF8 als auch Ascii klarkommen muss?

In der Hilfe steht dass das Char als Datentyp eine verschieden große Anzahl an Bytes fasst. Je nachdem ob man die Option Unicode-Executable aktiviert hat oder nicht. Wenn man die Option nicht aktiviert hat dann enthält Char immer ein Byte, bei Unicode Executables 2. Man müsste dann also als Unicode Exe kompilieren wenn man Char nutzen will.

UTF8 soll aber bis zu 4 Bytes pro Character brauchen. Wie funktioniert das dann?
...
SizeOf() kann ja die Speichergröße in Byte einer Variable bzw dessen Typ angeben. ZB 2 bei einem Char als Unicode Exe.
Wenn man zB den Zeiger auf das nächste Char in einem String erhalten will dann nutzt man SizeOf() um die entsprechende Anzahl Bytes zu ermitteln die nach vorn gegangen werden muss. Was aber wenn der String ein Ascii-String ist und das Executable im Unicode? Dann würde doch ein Zeichen übersprungen weil die Größe der Unicode-Chars zurückgegeben würde oder? Oder wäre der String dann intern mit leeren Bytes oder so vorgehalten?
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: UTF8 und Character

Beitrag von STARGÅTE »

Du kannst eine EXE nur als Ascii oder Unicode compilieren.
Das bedeutet das im Speicher entweder nur AsciiStrings liegen oder nur UnicodesStrings.
Damit meine ich die Strings von PureBasic.

UTF8 dient nur dazu selber SpeicherBuffer oder Dateien mit dem UTF8 Format zu beschreiben, um etwas ersparnis gegenüber Unicode zu haben.

Zum konvertieren kannst du PokeS() und PeekS() nutzen mit dne entsprechenden Flags ...

Ein Ascii-String würde in UNICode exe müll ausgeben, außer du konvertierst ihn. dann hätte der UnicodeString im speicher immer Nullen dazwicshen weil Ascii ja nicht über 255 geht ...

EDIT: Ja ein UTF-8 zeichen welches über Unicode $FFFF liegt, wird nicht unterstützt !
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

Re: UTF8 und Character

Beitrag von SebastianJu2 »

Dann wären im Unicodemodus die Stringzeichen immer 2 Byte lang? Oder bis zu 4 und es ist nur das Char dass nicht mehr als 2 Byte pro Zeichen speichern kann?

Ich habe gerade ein wenig getestet mit SizeOf() und die Größe einer gefüllten Stringvariable zurückgeben wollen. In der Hilfe heißt es Speicherverbrauch ist Länge des Strings + 1. Aber SizeOf() gibt immer 4 zurück für die Variable. Sie gibt wohl nicht die Länge des Strings zurück. Vielleicht die Größe der Stringdefinition?
Um die Byteanzahl eines einzelnen Stringzeichens zurückzugeben ist dieser Befehl dann wohl nicht geeignet.

Hat es denn Nachteile wenn man im Unicodemodus kompiliert? ZB RAM-Verbrauch?

Wenn ja dann wäre es vielleicht am Besten wenn man im Asciimodus kompiliert und Strings werden nur mit Funktionen eingelesen bei denen man die Kodierung setzen kann. Um sicherzugehen immer die volle Byte-Größe eines Zeichens einzulesen könnte man dann mit PeekS(*Start, 1, #PB_...) arbeiten. Und der zurückgegebene String sollte mit StringByteLength() die tatsächliche Byteanzahl zurückgeben. Wäre damit vielleicht sogar UTF8 über Unicode $FFFF möglich? Man würde ja nicht mehr mit Char arbeiten. Oder bedeuten die Konstanten einfach nur dass halt immer 2 oder 1 Byte pro Character eingelesen werden?

Was sind das denn für UTF8-Charactere die über Unicode $FFFF liegen? Die sind doch bestimmt nicht alle unwichtig.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: UTF8 und Character

Beitrag von STARGÅTE »

>> Dann wären im Unicodemodus die Stringzeichen immer 2 Byte lang?
Im StringPool von PB ja !

SizeOf(String$) gibt die die größe des Pointers aus (also 4 bei x86)

also entwerden:
StringByteLength(String$, Format)
oder
MemoryStringLength(@String$, Format)

>> Hat es denn Nachteile wenn man im Unicodemodus kompiliert? ZB RAM-Verbrauch?
Naja alle Zeichenketten sind doppelt so lang ... aber so viele wirst du wohl kaum haben.


>> Wenn ja dann wäre es vielleicht am Besten wenn man im Asciimodus kompiliert und Strings werden nur mit Funktionen
>> eingelesen bei denen man die Kodierung setzen kann. Um sicherzugehen immer die volle Byte-Größe eines Zeichens
>> einzulesen könnte man dann mit PeekS(*Start, 1, #PB_...) arbeiten. Und der zurückgegebene String sollte mit
>> StringByteLength() die tatsächliche Byteanzahl zurückgeben. Wäre damit vielleicht sogar UTF8 über Unicode $FFFF
>> möglich? Man würde ja nicht mehr mit Char arbeiten. Oder bedeuten die Konstanten einfach nur dass halt immer 2 oder 1
>> Byte pro Character eingelesen werden?

Auf was willst du eigentlich hinaus ?
Wenn du eine Datei hast in der ein UTF-8 String ist dann liest du den mit ReadString(...#PB_UTF8) ein, damit du sicherstellt, das der String richtig eingelesen wird.
Ob du nun Unicode oder Ascii brauchst hängt davon ab, ob du dnen wirklich "höhere" Sonderzeichen direkt im programm darstellen lassne willst.
Also zB griechische Zeichen in einer MessageBox oder so ...




>> Was sind das denn für UTF8-Charactere die über Unicode $FFFF liegen? Die sind doch bestimmt nicht alle unwichtig.
http://de.wikipedia.org/wiki/UTF-8
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

Re: UTF8 und Character

Beitrag von SebastianJu2 »

STARGÅTE hat geschrieben:>> Hat es denn Nachteile wenn man im Unicodemodus kompiliert? ZB RAM-Verbrauch?
Naja alle Zeichenketten sind doppelt so lang ... aber so viele wirst du wohl kaum haben.
Das könnte schon viel werden. Es geht um CSV-Dateien die ausgewertet und bearbeitet werden.

>> Wenn ja dann wäre es vielleicht am Besten wenn man im Asciimodus kompiliert und Strings werden nur mit Funktionen
>> eingelesen bei denen man die Kodierung setzen kann. Um sicherzugehen immer die volle Byte-Größe eines Zeichens
>> einzulesen könnte man dann mit PeekS(*Start, 1, #PB_...) arbeiten. Und der zurückgegebene String sollte mit
>> StringByteLength() die tatsächliche Byteanzahl zurückgeben. Wäre damit vielleicht sogar UTF8 über Unicode $FFFF
>> möglich? Man würde ja nicht mehr mit Char arbeiten. Oder bedeuten die Konstanten einfach nur dass halt immer 2 oder 1
>> Byte pro Character eingelesen werden?
STARGÅTE hat geschrieben:Auf was willst du eigentlich hinaus ?
Wenn du eine Datei hast in der ein UTF-8 String ist dann liest du den mit ReadString(...#PB_UTF8) ein, damit du sicherstellt, das der String richtig eingelesen wird.
Ob du nun Unicode oder Ascii brauchst hängt davon ab, ob du dnen wirklich "höhere" Sonderzeichen direkt im programm darstellen lassne willst.
Also zB griechische Zeichen in einer MessageBox oder so ...
Ja die Sonderzeichen brauche ich schon... die Daten enthalten Domains die eben auch zB in thailändischen, russischen usw Buchstaben geschrieben werden. Außerdem Kategorien die natürlich auch in der dem entsprechenden Sprache geschrieben sind. Scheinbar sind diese Sprachen alle in den ersten beiden Byte von UTF8 untergebracht. Das Eurozeichen braucht aber wohl schon 3 Byte und das könnte gut vorkommen.

Worauf ich hinaus will ist... ob ich die Exe in Ascii kompilieren kann um zB den Speicherverbrauch nicht zu steigern und gleichzeitig alle UTF8-Zeichen lesen kann selbst wenn sie über 2 Byte lang sind.
Also anstatt ein Char (das ja auf 1 oder 2 Byte beschränkt ist) zu benutzen um ein Zeichen auszulesen dann einen String auslesen der ein Zeichen lang ist und dann eben auch aus 3 oder 4 Byte bestehen kann. ZB mit PeekS(*Start, 1, #PB_...).

Ich habe mal einen Test gemacht. Ich habe das kyrillische Zeichen "з" in einen inputrequester kopiert. Und dann geschaut ob ich das Zeichen so wieder herausbekomme. Code:

Code: Alles auswählen

Define c.Character, s$

s$ = InputRequester("","", "")
c = @s$

Debug Chr(c\c)
Debug s$
Debug PeekS(@s$,2,#PB_UTF8)
Debug StringByteLength(s$)
Debug MemoryStringLength(@s$,#PB_UTF8)
Also eine Zuweisung an einen String. Beim PeekS habe ich die Längen 1-4 versucht. Als Ergebnis kamen immer 3 Fragezeichen und 2 mal eine 1 raus. Es wurde also wohl direkt als Ascii gespeichert in der Variable.
Dann habe ich es als Unicode Executable versucht. (Das gilt doch auch wenn ich nur F5 drücke und keine echte Exe speichere oder?) Und beim Unicode war das Ergebnis identisch.
Bei Eingabe einer 0 gibt es 0,0,0,2,1 aus. Bei Eingabe von "aaa" gibt StringByteLength komischerweise 6 aus. Egal ob ich im Unicodemodus oder im Asciimodus kompiliere...

Daraufhin habe ich das Ganze noch mal mit einem Editorgadget versucht weil die CSV-Daten aus einem Editorgadget gelesen werden sollen und das selbe Ergebnis wie vorher.

Code: Alles auswählen

Define c.Character, s$
*HandleWindowMain = OpenWindow(#PB_Any, 0, 0,700, 400, "URLTool", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_ScreenCentered)
*HandleButtonInputToOutput = ButtonGadget(#PB_Any, 5, 167, 30, 30, "V", #PB_Button_MultiLine)

*HandleEditorCSVInput1 = EditorGadget(#PB_Any, 5, 5, 200, 150)

Repeat
  EventID = WaitWindowEvent()
  
  If EventWindow() = *HandleWindowMain And EventID = #PB_Event_Gadget
    
    If EventGadget() = *HandleButtonInputToOutput
      s$ = GetGadgetText(*HandleEditorCSVInput1)
      c = @s$

      Debug Chr(c\c)
      Debug s$
      Debug PeekS(@s$,1,#PB_UTF8)
      Debug StringByteLength(s$)
      Debug MemoryStringLength(@s$,#PB_UTF8)
    EndIf
  EndIf
Until EventID = #PB_Event_CloseWindow
Ein Bedienungsfehler? Unter Einstellungen - Compiler - Standards habe ich Unicode Executable erstellen angehakt.
Irgendwas passt da noch nicht... Na vielleicht wirds morgen klarer nach etwas Schlaf :)
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: UTF8 und Character

Beitrag von ts-soft »

PureBasic Strings sind immer ASCII oder Unicode, aber niemals UTF-8!
UTF-8 kann sich also lediglich in einer Datei oder im Speicher befinden,
wenn Du es als Daten in den Speicher gelesen hast.

Für volle Unicode-Kompatibilität ist es auch empfehlenswert, die IDE auf UTF-8
zu stellen, bzw. sollte dies sowieso der Standard-Modus sein.

UTF-8 aus dem Speicher bekommst Du nur mit entsprechendem Modus bei PeekS,
das Ergebnis ist dann ein String im Format, welches Du in den Compileroptionen
eingestellt hast. Im ASCII-Modus können dabei natürlich Zeichen verloren gehen,
also für Dein Vorhaben ist Unicode in jedem Falle unbedingt erforderlich!

Speicher sparen usw. ist bei heutigen PCs plumpaquatsch und Unicode ist seit W2K
der Standard.

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: UTF8 und Character

Beitrag von STARGÅTE »

Scheinbar sind diese Sprachen alle in den ersten beiden Byte von UTF8 untergebracht. Das Eurozeichen braucht aber wohl schon 3 Byte und das könnte gut vorkommen.
Das Eurozeichen braucht in UTF8 3 Bytes ... weil dort ein paar Bits für die ByteErkennung drauf gehen, in Unicode braucht das Eurozeichen "auch" nur 2 Bytes.
Worauf ich hinaus will ist... ob ich die Exe in Ascii kompilieren kann um zB den Speicherverbrauch nicht zu steigern und gleichzeitig alle UTF8-Zeichen lesen kann selbst wenn sie über 2 Byte lang sind.
Also anstatt ein Char (das ja auf 1 oder 2 Byte beschränkt ist) zu benutzen um ein Zeichen auszulesen dann einen String auslesen der ein Zeichen lang ist und dann eben auch aus 3 oder 4 Byte bestehen kann. ZB mit PeekS(*Start, 1, #PB_...).
Das ist aber gerade das Problem, ein String hat in ASCII immer genau 1 Byte/Zeichen.

Wenn du wirklich mit UTF arbeiten möchtest/willst/musst, dann darfst du kein der normalen String-Funktionen nutzen, sonden alles selber "per Hand" über eigene Memorys machen...

So jetzt noch mal zu PeekS und PokeS() und Strings

MODUS: Ascii

Ein Character hat immer 1 Byte!
Ein String (also s$) wird immer Ascii sein. höhere Zeichen gehen einfach verloren werden in das Intervall 0-255 gepresst.
PeekS(@s$,2,#PB_UTF8)
Hier versuchst aus dem Memory wo ein Ascii String drin ist, UTF8 zu lesen, da kommt Müll raus.
StringByteLength(s$) wird immer Zeichen*1Byte ausgeben!
MemoryStringLength(@s$,#PB_UTF8) gibt müll aus!

MODUS: Unicode

Ein Character hat immer 2 Byte!
Ein String (also s$) wird immer Unicode sein.
PeekS(@s$,2,#PB_UTF8)
Hier versuchst aus dem Memory wo ein Unicode String drin ist, UTF8 zu lesen, da kommt Müll raus.
StringByteLength(s$) wird immer Zeichen*2Byte ausgeben!
MemoryStringLength(@s$,#PB_UTF8) gibt müll aus!

Jetzt zum Verständnis:

Code: Alles auswählen

Text$ = "Füße"

*Buffer = AllocateMemory(48)
PokeS(*Buffer, Text$, #PB_Default, #PB_Ascii)

PokeS(*Buffer+16, Text$, #PB_Default, #PB_UTF8)

PokeS(*Buffer+32, Text$, #PB_Default, #PB_Unicode)

ShowMemoryViewer(*Buffer, 48)
Siehst du was im "Speicher steht"


Wenn du also wirklich kyrillische Zeichen usw. nutzen willst, musst du im Unicode compilieren, damit die Zeichen nicht verloren gehen, wenn du mit Strings in PB arbeitest.

Ließt du nun zB aber eine UTF8-Datei, musst du dort angeben das du UTF8 lesen willst, und er den String dann zu Unicode macht.

Willst du später wieder nach UTF8 speichern (damit die Datei kleiner ist) musst auch dort UFT8 angeben.

Damit du es nicht falsch verstehst:
Sowas MemoryStringLength(@s$,#PB_UTF8)
gibt dir nicht aus, wieviele Bytes der String unter UTF8 hätte, sonden er versucht UTF8 im Speicher zu lesen und gibt dir dann an wie viele Zeiche er gelesen hat! wenn da natürlich kein UTF8 drin steht, kommt müll raus.
Selbiges gibt für PeekS()
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
SebastianJu2
Beiträge: 180
Registriert: 24.09.2010 10:39

Re: UTF8 und Character

Beitrag von SebastianJu2 »

Ich muss mir das dann noch mal alles durchlesen und ein paar Testfunktionen schreiben. Danke euch...
Antworten