Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16 (https://www.delphipraxis.net/194418-mal-wieder-kodierungsprobleme-ansi-utf8-utf16.html)

LTE5 17. Nov 2017 20:43


Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Erstellt wird eine Resource-Datei. Sie beinhaltet Textdokumente.
Ein paar davon sind UTF-8 ohne BOM und ein paar ANSI.

Lade ich die Resource nun mit den hier stehenden Fragmenten, dann kommt bei der UTF8-Datei das korrekte Ergebnis. Bei der ANSI-Datei kommt eine Fehlermeldung.

Muss ich jetzt alle UTF-8-Dateien nach ANSI kodieren, umgekehrt oder die Funktion anpassen? Ehrlich gesagt habe ich nicht mal eine Ahnung worin der Unterschied liegt.

Delphi-Quellcode:
 ResourceStream := TResourceStream.Create(Instance, ResourceName, RT_RCDATA);
 try
  sl := TStringList.Create;

  try
   sl.LoadFromStream(ResourceStream, TEncoding.UTF8);
   Result := sl.Text;
  finally
   sl.Free;
  end;

....

Redeemer 17. Nov 2017 21:01

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Du musst je nach Datei das korrekte Encoding angeben. Wenn du ANSI-Dateien als UTF-8 lädst, macht Delphi überhaupt nichts, weil das Encoding von Emba so definiert und deklariert wurde.

LTE5 17. Nov 2017 21:02

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Wie gehe ich das denn an, wenn ich das Encoding nicht kenne?

Ich habe gelesen, ANSI sei veraltet. Stimmt das? Man solle am besten direkt UTF8 nehmen.

Redeemer 17. Nov 2017 21:10

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
ANSI ist nicht standardisiert und wird von der Locale bestimmt. Während alle westlichen PCs in der Regel dieselbe Locale haben (CP1252), wird sich das Programm auf Rechnern in anderen Regionen anders verhalten.

Das Encoding einer Textdatei kann ohne BOM nicht sicher herausgefunden werden. Ein klassischer Witz unter Windows XP war, eine Textdatei im Windowseditor zu erstellen und "Bill hid the facts" reinzuschreiben. Dann passierten lustige Dinge.
Firefox verhält sich bei ANSI beispielsweise abhängig von der TLD völlig unterschiedlich, obwohl ANSI CP1252 eindeutig als Standardzeichensatz von HTML5 definiert ist.

Aber zurück zum Thema: Alle Dateien sollten als UTF-8 gespeichert werden. Das verbraucht zwar mehr Speicherplatz, ist aber nötigt, denn bei Delphi kannst du die zu verwendende Locale nicht ohne weiteres angeben. Du kannst nur ANSI angeben (TEncoding.Default), aber das ist wie gesagt nicht standardisiert.

LTE5 17. Nov 2017 21:14

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Sollte man dementsprechend auch TMemIniFile.Create mit TEncoding.UTF8 aufrufen? Wenn die Datei schon existiert aber nicht im UTF8-Format, gibts eine schöne Fehlermeldung.

Und:

Memo LoadFromFile ... muss ich dort nun auch TEncoding.UTF8 angeben? Weil wenn die Datei zufälligerweise kein UTF8 ist, gibt es auch eine Fehlermeldung.

mensch72 17. Nov 2017 21:30

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
..."worin liegt der Unterschied"...

https://de.wikipedia.org/wiki/UTF-8

da steht viel Text, aber irgendwo in der Mitte steht, das "quasi" im UTF8 ByteArray nach einem HEX "11xxxxxx" stets mindestens ein weiteres Byte mit "10xxxxxx" folgen wird... und exakt das ist der Trick, wie man mit 98% Sicherheit sich selbst "ohne BOM" eine "IsUTF8" Funktion schreiben kann!
(Man geht davon aus, dass praktisch fast nie in Ansi-EU-Sprachen aufeinanderfolgend solche ZWEI Umlaute(>$80) verwendet, die zulällig mit diesen Bitmasken korrellieren. Die Leute die sich das ausgedacht haben, verwenden diese Bitmasken so definitiv nicht zufällig und waren sich des Erkennungsproblems ohne "BOM" sehr wohl bewußt)

- also lasse es weiter beim UTF8 auf eine Exeption ankommen und schalte damit dann auf deine "AnsiVariante" um, oder:
- programmiere dir selbst schnell eine Funktion, welche deinen Stream als BYTEbyBYTE durchgeht und bei einem HEX "11xxxxxx" nachschaut ob das nachfolgende Byte UNGLEICH "10xxxxxx"... wenn ja dann Break und Result=ANSI, sonst weiter bis zum Ende und dann Result=UTF8
- klar ist das nicht 100% eineindeutig, aber es hat sich in der Praxis so bewährt und wird von eingen so gemacht:)

LTE5 17. Nov 2017 21:33

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ist das hier auch annehmbar?

Delphi-Quellcode:
Memo1.Lines.LoadFromFile('file.txt');
Memo1.Text := UTF8ToString(Memo1.Text);
Das Problem mit der Ini-Datei besteht trotzdem noch. Wenn jemand auf die irrsinnige Idee kommt die Datei nach Ansi zu konvertieren, gibts natürlich ein Problem denn ich hab als Encoding UTF8 beim Create der Ini angegeben.

Und ob das hier eine Lösung ist weiß ich auch nicht
Delphi-Quellcode:
 try
  inherited Create(FileName, TEncoding.UTF8);
 except
  on E: EEncodingError do
   inherited Create(FileName);
 end;

mensch72 17. Nov 2017 21:45

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Solange du nicht weißt, "was" es für Daten sind, vermeide in Delphi alles was mit Char oder String zu tun hat!
-> TBytes, TByteAarray oder ein TMemoryStream sind hier z.B. die passenden Speichertypen.

Dann kannst du es mit
Delphi-Quellcode:
try
  Memo1.Text := DecodeUTF8toString(MemoryStream);
except
  Memo1.Text := DecodeANSItoString(MemoryStream);
end;
oder
Delphi-Quellcode:
if IsUTF8(MemoryStream) then
  Memo1.Text := DecodeUTF8toString(MemoryStream)
else
  Memo1.Text := DecodeANSItoString(MemoryStream);
praktisch recht einfach realisieren.

LTE5 17. Nov 2017 21:48

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Es sind immer Textdaten. Ganz normale Textdateien.

Dieses ganze Encoding-Zeugs macht mich ganz wirr.

Muss man das überhaupt angeben? Speziell bei Ini-Dateien ist es komisch. Speichere ich eine frische Ini-Datei mit einer Section wo äöü vorkommt, wird es als ANSI gespeichert.
Speichere ich ohne Umlaute, Speichert Delphi es als UTF8.

Gibt es hier keine goldene, einfache Regel die man befolgen kann?
Oder wäre es besser zu sagen "wenn jemand die ini-dateien unbedingt verändern muss > sein Pech wenn Fehler auftreten" ?

mensch72 17. Nov 2017 21:57

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
..."Es sind immer Textdaten. Ganz normale Textdateien"...
=> ex gibt keine ganz normalen Textdaten, ausser den 127 ASCII Zeichen!!!

Delphi oder WinAPI jenach dem welche "Ini" Funktionen du benutzt machen da also entweder UTF16, oder ANSI.(Nur Lazerus verwendet selbst UTF8)

- also behandle IniFiles als ByteStream und lade sie in einen TMemmoryStream.
- prüfe ob es UTF8 ist, und mache dann passned daraus ein TMemoryIniFile, sonst eben das TMemoryIniFile per Ansi Daten
- beim Speichern identisch nur anders herum oder hart kodiert auf Ansi oder UTF8, je nachdem was du bevorzugst

LTE5 17. Nov 2017 22:01

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Leider habe ich keine Ahnung wie man das machen soll.
In meiner eigenen Ableitung von TMemIniFile rufe ich im Constructor ganz normal
Delphi-Quellcode:
inherited Create(FileName);
auf (+ andere Dinge passieren dort natürlich, sonst bräuchte ich keine Ableitung).

Update: dieser Thread hat jedenfalls das Problem behoben, dass die Datei als ANSI gespeichert wird, wenn sie vorher nicht existierte, aber Umlaute in der Datei zu finden waren
http://www.delphipraxis.net/126156-d...-encoding.html

p80286 17. Nov 2017 22:17

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Zitat:

Zitat von LTE5 (Beitrag 1386526)
Ich habe gelesen, ANSI sei veraltet.

Es gibt Szenarien, in denen die Anwendung einer Ansi-Codierung nicht optimal ist.
In diesen Fällen ist die Anwendung von UTF8 oder UTF16 auf jeden Fall besser.

Zitat:

Zitat von LTE5 (Beitrag 1386526)
Wie gehe ich das denn an, wenn ich das Encoding nicht kenne?

Zunächst das Encoding heraus bekommen, und dann alle betroffenen Dateien auf das gleiche Encoding umstellen.
Welches Du verwendest, ist egal, aber es muß bei allen das selbe sein!

Gruß
K-H

Edith:
Zitat:

Zitat von LTE5 (Beitrag 1386533)
Muss man das überhaupt angeben? Speziell bei Ini-Dateien ist es komisch. Speichere ich eine frische Ini-Datei mit einer Section wo äöü vorkommt, wird es als ANSI gespeichert.
Speichere ich ohne Umlaute, Speichert Delphi es als UTF8.

Da die Umlaute den Unterschied zwischen ANSI und UTF8 ausmachen, stelle ich mir die Frage woran Du die UTF8-Codierung fest machst?

LTE5 17. Nov 2017 22:22

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ich glaube ich bleibe bei meiner Ini-Ableitung definitiv bei der Zeile
Delphi-Quellcode:
inherited Create(aFileName);
ohne Encoding-Hexerei.
So gibt es keine Fehlermeldung. Wenn jemand meint eine Datei unbedingt selber bearbeiten zu müssen, ist es in diesem Falle das eigene Pech.

Ansonsten habe ich jetzt alle meine Dateien auf UTF8 ohne BOM umgestellt, da selbst Windows neue Dateien so anlegt.
Meinen Code aus Beitrag #1 habe ich unverändert so gelassen.

Da ich zu wenig Ahnung von dieser Hexerei habe, befasse ich mich damit nicht weiter und belasse alles so, wie es fehlerfrei funktioniert.

mensch72 17. Nov 2017 22:23

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
gaaaanz einfach, nix mit Ableitungen... nur einfach selbst VORHER "erkennen" ob UTF8 oder Ansi:)

Delphi-Quellcode:
ms:TMemoryStream
s:string;

ms:=TMemoryStream.Create;
ms.LoadFromFile('filename');

if IsUTF8(ms) then
  s := DecodeUTF8toString(ms)
else
  s := DecodeANSItoString(ms);
ms.free;
 
Memo1.Text:=s;
MemIniFile.Text:=s;
Du musst dir nur fix selbst(wie empfohlen) oder per "Try" mit TEncoding folgende 3 Funktionen basteln:)
Delphi-Quellcode:
function IsUTF8(const stream:TStream):Boolean;
function DecodeUTF8toString(const stream:TStream):string;
function DecodeANSItoString(const stream:TStream):string;

LTE5 17. Nov 2017 22:25

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Da ich eine universelle Lösung brauche für das Laden von Textdateien UND das Laden/Speichern von Ini-Dateien, kann ich das leider nicht gebrauchen.
Ich belasse von nun an aber alles so wie ich es aktuell habe, da es funktioniert.

Dieser ganze Encoding-Kram ist doch der Wahnsinn!

mensch72 17. Nov 2017 22:32

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
..."Da ich eine universelle Lösung brauche für das Laden von Textdateien UND das Laden/Speichern von Ini-Dateien, kann ich das leider nicht gebrauchen."...

Jedem das seine, die paar da geschrieben Code Zeilen ergeben genau DIE UNIVERSELLE Lösung, die man sogar noch per einem weiterem IF auf zusätzlich UTF16 erweitern kann... aber mach wie du willst, nur schimpfe nicht auf das Encoding-Konzept... das hat wirklich seinen Sinn:)

LTE5 17. Nov 2017 22:33

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ich sehe keine Möglichkeit die oben genannten Codezeilen in eine Ini-Klasse einzubauen.
Da steht zwar "IsUTF8" aber davon habe ich ja eben keine Ahnung.
Zitat:

das hat wirklich seinen Sinn
Ich verstehe es nicht einmal und werde es auch nie verstehen. Code soll einfach nur funktionieren und das machen was man ihm sagt. Ich habe keine Lust mich mit 100 verschiedenen Encodings rumschlagen zu müssen.

Habe meine Version aus #1 bearbeitet, damit ich noch weniger Kopfschmerzen wegen diesem komischen Encodingkram habe
Delphi-Quellcode:
try
 sl.LoadFromStream(ResourceStream{*, TEncoding.UTF8*});
 {$WARNINGS OFF}
 Result := UTF8ToString(sl.Text);
 {$WARNINGS ON}
finally
 sl.Free;
end;

p80286 17. Nov 2017 22:41

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Zitat:

Zitat von LTE5 (Beitrag 1386539)
Da ich eine universelle Lösung brauche für das Laden von Textdateien UND das Laden/Speichern von Ini-Dateien, kann ich das leider nicht gebrauchen.
Ich belasse von nun an aber alles so wie ich es aktuell habe, da es funktioniert.

Dieser ganze Encoding-Kram ist doch der Wahnsinn!

Nichts für ungut, aber diese Einstellung halte ich für kurzsichtig. Solange Du die Dateiinhalte auf die ASCII-Zeichen beschränken kannst, mag das gehen. Sobald Du über diesen Tellerrand hinaus gehst, muß Du Dich entscheiden, welche Codierung für Dich, oder besser die Aufgabenstellung, die richtige ist. Solltest Du den Bereich der Lateinische Schrift verlassen (müssen) ist meiner Meinung nach Unicode/UTF16 die richtige Wahl.

Gruß
K-H

LTE5 17. Nov 2017 22:43

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ich hätte absolut kein Problem damit alles nach Unicode umzustellen.
Aber alleine schon die Tatsache, dass es eine Fehlermeldung bei Angabe des TEncodings gibt, wenn die Datei dieses Encoding aber nicht hat lässt mich alles über Board werfen, da ich auf sowas keine Lust habe.

Unicode-Anwendungen scheinen aber nicht so populär zu sein.
Beispiel an Inno Setup.
http://www.jrsoftware.org/isdl.php

1,113,859 Downloads die normale Version, 197,053 die Unicode.

mensch72 17. Nov 2017 22:51

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
..."IsUTF8... Ich verstehe es nicht einmal und werde es auch nie verstehen. Code soll einfach nur funktionieren"...

=> UniCode und WideStrings sowie das "TEncoding" von Delphi sind eben NICHT perfekt, wenn es um in der Praxis durchaus übliche Files OHNE BOM geht... daher SCHREIBT MAN SICH DAS EBEN BESSER FIX SELBST, denn vom schimpfen oder verdrängen/ignorieren löst das Problem ja nicht, egal ob du dazu Lust hast oder nicht.

In #6 steht recht genau wie man diese Funktion sich selbst fix progammiert.... wenn du "später" zufällig doch mal hängst weil Delphi nun plötzlich bei abgeschalteter Warnung nun NICHT MEHR das macht was "du erwartest", sondern dann nun exakt das was du per Code gesagt hast... dann erkennst du eventuell den Vorteil von so scheinbar völlig unbedeutenten typsicheren Hilfsfunktionen;)

LTE5 17. Nov 2017 23:00

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ich glaube greenmiles erster Satz aus Beitrag #20 spiegelt gerade meine Stimmung wieder

http://www.delphipraxis.net/180079-e...-fehler-3.html

Ich habe nicht mal Ahnung von Encodings. Denkst du wirklich ich habe Ahnung von dem was in #6 steht? Nee ;)

ich bin ja nicht so, also hab ichs versucht. Ging nur leider in die hose. Egal welche Datei ich reinwerfe, es ist immer False.
Delphi-Quellcode:
 function IsUTF8(FileName: string): Boolean;
 var
  _FileStream: TFileStream;
  _Word: Word;
 begin
  _FileStream := TFileStream.Create(FileName, fmOpenRead);
  _FileStream.Read(_Word, SizeOf(_Word));

  try
   case _Word of
    $FEFF:
     Result := False; // UTF-16
    $FFFE:
     Result := False; // UTF-16
    $EFBB:
     Result := True; // UTF-8
   end;
  finally
   _FileStream.Free
  end;
 end;

mensch72 17. Nov 2017 23:26

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Stimmungen und Gefühle beim programmieren vermeide ich, weil letztendlich macht die CPU ja doch nur was man ihr gesagt hat und nicht das was man will.
(Selbst Frauen sind da einfacher, die machen zwar selten das was man ihnen sagt, aber oft doch das was (M)man will:) )

Delphi-Quellcode:
function IsUTF8(const data:TStream):boolean;
var
  Value:BYTE;
begin
Result:=False;

data.Position:=0;
while(data.Position<>data.Length) do begin
  Value:=GetByteAndMovePos(data);
  if((Value and $c0)=$c0) and (data.Position<>data.Length) then begin
    Value:=GetByteAndMovePos(data);
    if((Value and $80)=$00) then exit;
  end;
end;

Result:=True;
end;
Wenn dir 5Minuten für so ein paar Zeilen basierend auf #6 zu anstrengend sind, dann investiere eben weiter ständig überall sonst deine Nerven...




ps:
Sichwort "Intel-Speicherformat":
$EFBB: // MSBLSB
Result := True; // UTF-8
$BBEF: // LSBMSB
Result := True; // UTF-8

..."Was ich nicht verstehe ist, warum Google nicht voll von solchen Problemen ist"...
weil die meisten es BYTEbyBYTE auswerten und nicht als WORD, was bei IntelCPUs eben "gedreht" ist... aber alles kein Problem wenn man etwas in HEX denkt:)

LTE5 17. Nov 2017 23:29

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ich dachte man überprüft verschiedene Encodings so wie mein versuch da oben.
Nur mein Code da oben gibt $BBEF zurück, obwohl meine Datei UTF8 sogar mit BOM ist.

Am liebsten wäre mir natürlich alles ohne Streams. Es gibt nichts was ich mehr hasse als Streams.
Außerdem habe ich keine Ahnung woher ich Streams bekommen soll an den Stellen wo ich diese Prüfung durchführen müsste.
Mit Streams sind das bestimmt X Zeilen pro Aufruf mehr...

Was ich nicht verstehe ist, warum Google nicht voll von solchen Problemen ist. Das kann ja nur heißen, dass das total unwichtig ist.

Redeemer 17. Nov 2017 23:31

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Zitat:

Zitat von LTE5 (Beitrag 1386531)
Ist das hier auch annehmbar?

Delphi-Quellcode:
Memo1.Lines.LoadFromFile('file.txt');
Memo1.Text := UTF8ToString(Memo1.Text);
Das Problem mit der Ini-Datei besteht trotzdem noch. Wenn jemand auf die irrsinnige Idee kommt die Datei nach Ansi zu konvertieren, gibts natürlich ein Problem denn ich hab als Encoding UTF8 beim Create der Ini angegeben.

Und ob das hier eine Lösung ist weiß ich auch nicht
Delphi-Quellcode:
 try
  inherited Create(FileName, TEncoding.UTF8);
 except
  on E: EEncodingError do
   inherited Create(FileName);
 end;

1. Nein, denn LoadFromFile konvertiert von ANSI nach UTF-16. UTF-16, UCS-2 sowie ANSI, CP1252 und ISO 8859-1 (LATIN-1) sehen zwar auf westlichen Computern gleich aus, sind es aber nicht. ANSI ist hier zwar das gleiche wie CP1252, aber UTF-16 (bzw. UCS-2) unterscheidet sich davon bei Zeichen 128-159, weil es dort ISO 8859-1 und nicht CP1252 entspricht. Das Laden eines ANSI-Datei bildet diese CP1252-Zeichen auf völlig andere Zeichen ab. UTF-8 erwartet aber 128-159 dort, sprich es schlägt fehl.
2. Ich speichere solche Dinge nicht in der INI. Strings konvertiere ich nach etwas sicherem, Base64 oder Hex.
3. Encoding produziert keine Fehler. Encoding tut im Fehlerfall einfach nichts.

LTE5 17. Nov 2017 23:36

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Zitat:

3. Encoding produziert keine Fehler. Encoding tut im Fehlerfall einfach nichts.
Wenn man eine ANSI-Datei mit TEncoding.UTF8 lädt, gibt es eine hässliche Fehlermeldung.

Aus dem MSDN Forum
Zitat:

The only easily identifiable text files are those encoded in UTF16 and ones with a BOM. If you have neither, and the vast majority of your files won't, you cannot detect the encoding from the content. Every 8 bit encoding uses all 8 bits to encode characters. You'll need human eyes to look at the decoded text to decide if it is correctly decoded. That's especially hard to do with Western European text, it will be correct for quite a while until you encounter a Word "smart quote".
Ich glaube ich lasse es einfach sein. Ich lasse alles so wie es ist. Es funktioniert und wenn jemand in den Daten rumpfuscht ist es nicht mein Problem.

Klar wäre es schön, wenn ich Unicode-support hätte. Ich denke mit einem Ini > Create (..., TEncoding.Unicode); (UC2-Little-Endian) würden auch nicht-westliche Zeichen gespeichert und geladen.
Aber sobald die Datei mal nicht Unicode ist, gibt es leider wieder diese Fehlermeldungen.

Und ehrlich gesagt... den Aufwand wäre es mir auch wert, um Unicode-fähig zu sein! Delphi ist schon länger Unicodefähig was strings angeht, warum also nicht nutzen.

Ich schlafe mal eine Nacht drüber, lasse mein Kopfkissen meine grenzenlose Wut spüren, indem ich ein paar Tränen vergieße und komme morgen wieder.

Letzte Frage bevor ich aufhöre zu nerven. Angenommen ich stelle nach BigEndianUnicode um.. oder Little, egal.
Muss ich dann allen Funktionen die ich so nutze, sei es zum Lesen oder Schreiben, als Encoding das Unicode-Encoding übergeben?

Redeemer 18. Nov 2017 01:13

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Dann wurd das geändert. Bei mir schlägt TEncoding.UTF8 ohne Fehlermeldung fehl und tut einfach überhaupt nichts.

Auch UTF-16 kann nicht sicher erkannt werden. Das ist interessant, dass gerade Microsoft das behauptet, wo sie doch in Windows XP beim Erkennen von UTF-16 versagt haben. In einigen wenigen Fällen kann man sicher sagen, dass eine Datei kein UTF-16 bzw. UCS-2 ist, aber man kann nie sagen, dass eine Datei UTF-16 ist.

Man kann übrigens Dateien nicht in Unicode speichern und laden, denn Unicode ist keine Kodierung.
Windows und Delphi benutzen als Kodierung UTF-16LE, das kann man theoretisch direkt aus der Datei in WideStrings und wieder zurück schreiben. Auch UCS-2LE kann man ohne Veränderung in WideStrings laden, speichern geht aber nicht.

Für westliche Texte benötigen alle UTF-Kodierungen mehr Platz als ANSI, aber dann ist es immerhin auf allen Systemen gleich. UTF-16 benötigt zudem bei westlichen Texten viel mehr Platz als UTF-8.

Ich kann nicht nachvollziehen, was für Daten du hast. Mach es einheitlich und am besten ohne ANSI.

LTE5 18. Nov 2017 09:06

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Ich habe nur Ini-Dateien bei denen die Section-Namen sowie wenn überhaupt eine Hand voll Bezeichner Alle Zeichen enthalten könnten. Aktuell aber nur westliche Zeichen.

Was ich bisher herausgefunden habe:
versuche ich eine Ini-Datei mit TEncoding.UTF8 zu laden und es ist keine > Fehlermeldung
versuche ich eine Ini-Datei mit TEncoding. BigEndian zu laden und es ist keine > keine Fehlermeldung

Ist eine Datei mit UTF8 rund 1,8KB groß, sind es bei BigEndian schon 3,6 circa. LittleEndian ist gleich. Hier ist es nur ein Bit das woanders gespeichert wird wenn ich das richtig verstanden habe.
Von daher kann ich auch direkt TEncoding.Unicode nehmen.

Ist es denn ratsam nach Unicode umzustellen? Alleine wegen der Möglichkeit nicht-westliche Zeichen schreiben/lesen zu können?
Wenn Unicode doch so viel besser ist, warum ist der Inno Setup Installer dann nur nicht-Unicode und die Unicode-Variante hat so wenige Downloads und ist ein Extra-Download?

Redeemer 18. Nov 2017 10:11

AW: Mal wieder Kodierungsprobleme. ANSI UTF8
 
Da der Benutzer vermutlich keine INI-Dateien aus fremden Quellen benutzt, denn dafür sind die nicht da:
Zitat:

Zitat von Redeemer (Beitrag 1386551)
Mach es einheitlich und am besten ohne ANSI.

Wenn die Section plötzlich irgendwas mit "]" heißen muss, hast du ein Problem, daher wie gesagt die Empfehlung, die Daten als Base64 oder Hex zu speichern, dann muss man sich auch nicht mit Encodings herumschlagen, weil es dann ASCII ist.

Da ich mich jetzt schon zum zehnten Mal wiederhole, lasse ich diesen Thread lieber in Frieden.

LTE5 18. Nov 2017 10:59

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Zitat:

Wenn die Section plötzlich irgendwas mit "]" heißen muss, hast du ein Problem
Ich habe das gerade mal getestet.
Code:
[[Testsection]]
klappt erstaunlicherweise ohne Probleme.

Base64 kann ich nicht wirklich verwenden, aufgrund des Unterbaus meines Programms.
Ich gucke aber trotzdem mal was sich machen lässt.
Es sieht aber, das kann ich jetzt schon sagen, eher mager aus. Denn es gibt noch eine weitere Datei die ggf. Unicode-Zeichen enthalten kann.
Alles nun in Base64 zu kodieren und beim Auslesen wieder zu dekodieren, puh, ich weiß nicht. Da setze ich das Encoding lieber auf Unicode.

Aber die Frage nochmal da mir das unklar ist:
wenn Unicode so viel besser ist, da mehr Zeichen gespeichert werden können, warum machen das dann nicht alle so und warum speziell Jordan Russel nicht? Er pflegt mit Compiler-Switches im Grunde zwei Versionen. Einmal Unicode und einmal normal. Warum nicht nur Unicode.

mensch72 18. Nov 2017 11:41

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
..."Warum nicht nur Unicode."...
Weil bis Delphi2007 die VCL NonUniCode war und UniCode mit TEncoding.XXX insbesondere UTF8 eigentlich erst seit XE5 wo erstmals richtig IOS&Android Mobile funktionieren und die NextGen Compiler brauchbar liefen. Da war dann auch die gemeinsame Delphi RTL soweit, dass man entschieden AnsiString unter FMX auf MobileDevices komplett zu entfernen so das ed dort nurnoch UniCode gibt.

Will man also aktuell portable Quelltexte schreiben, muss man für Delphi kleiner gleich D2007 rein NonUnicode mit AnsiStrings für RTL&VCL arbeiten.
Will man mit D2009..XE4 arbeiten, hat man zwar UniCode RTL&VCL in UTF16 UniCode, aber die anderen Varianten wie UTF8 hakeln dort OutOfThe Box.
Ab XE5 muss man dann bei den NextGen Compilern bei DelphiStrings auch noch beachten, das diese unter Mobile "0" nun basiert sind, also man portabel bei Strings immer schön in Schleifen mit Low(stringX) bis High(stringX) arbeitet. Und immer schön Length(stringX) verwendet, statt der früher noch möglichen Variante per [0]Index.

Da es mittlerweile also praktisch 4 Varianten von "default" DelphiStrings gibt erklärt sich dir eventuell, warum einige wenigsten das alte NonUnicode als AnsiOnly getrennt als eigene Version weiter führen.

1. Wenn du erst jetzt mit XE10.2 "einsteigst" bleibt nur der Rat: Realisiere und akzeptiere das deine gesammte Software per Default als UTF16 läuft.
2. Kümmere dich also nicht um UniCode ja/nein, sondern kümmere dich da wo es sein muss bei Kontakt zur WinApi (spziell also bei IniFiles) um eine EIGENE sichere Erkennung und Behandlung und nutze im Programm ausschließlich das per RTL/VCL gepufferte "TMemIniFile".
3. Verlasse dich nicht auf aktuell "zufällig" gerade noch funktionierende Vereinfachung per Cast oder direkter Zuweisung unter Nutzung impliziter Konvertierung durch den Delphikompiler... das wurde seit D2009 bis aktuell XE10.2.1 speziell bei UTF8 und ANSI schon ein paar mal intern angepasst(wie wenn du genau hier mitgelesen hast erkennst, schreiben ja einige das sie bei TEncoding mal keine Exception bekommen, wo du mit XE10.2 jetzt eine bekommst)
4. auch wenn es schwer fällt, lerne aus der Vergangenheit und nimm jetzt nicht den vermeintlich leichtesten Weg... "früher" (vor D2009) war Delphi mal wegen der "CompilerMagic" die Sprache der Wahl, wenn es um einfaches String handlig ging... jetzt kurz vor dem Sprung auf NextGen Kompiler auch für den Desktop tue dir den Gefallen und denke in UTF16 und behandle alles andere SELBST und kapsele die Konvertierungen in eigene kleine Toolfunktionen... so fällt es dir zukünftig leich nur dort mit ein paar IFDEFs deinen Quelltext mit alten und dann neuen Delphi Versionen zu übersetzen

LTE5 18. Nov 2017 11:49

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Danke für die Erklärung.

Könnte haarig werden. Ich verwende hier und dort TFile.AppendAllText. Hier kann ich ja einfach TEncoding.Unicode dranhängen.
Wie ich es beim LESEN mache (TFile.ReadAllText), muss ich noch gucken. Vielleicht eine kleine INterposer-Klasse für TFile oder so, wo ich dann im überschriebenem ReadAllText das Encoding prüfe.. Mal gucken

Habe nun diese drei Proceduren/Funktionen in meine Shared-Utils-Sammlung aufgenommen. Bei Bedarf erweiterbar. Das hier ist erst der Anfang und soll nur eine zentrale Stelle Bilden, wo sich der Code zum Schreiben/Lesen befindet. Muss ich etwas ändern, ist es nur an einer Stelle und nicht an 100.
Bisher funktioniert alles einwandfrei. UTF-16 LE wird problemlos geschrieben und gelesen.

Delphi-Quellcode:
class procedure TFileUtils.FileAppendText(const Path, Contents: string; const Encoding: TEncoding);
begin
 TFile.AppendAllText(Path, Contents, Encoding);
end;

class procedure TFileUtils.FileWriteText(const Path, Contents: string; const Encoding: TEncoding);
begin
 TFile.WriteAllText(Path, Contents, Encoding);
end;

class function TFileUtils.FileGetTextReadAllText(const Path: string; const Encoding: TEncoding): string;
begin
 Result := TFile.ReadAllText(Path, Encoding);
end;
Ich habe nun nach ein paar Stunden alles nach Unicode umgestellt. Alle Daten, egal wo im Programm, werden korrekt geschrieben und gelesen.
Lediglich da wo ich zu 100% weiß, dass niemals etwas anderes als a-z und 0-9 gespeichert wird, schreibe und lese ich trotzdem noch im utf-8-Format.
Ich nutze UTF-16 aus einem einfachen Grund: wird eine Datei gelesen, die nicht UTF-16 ist, gibt es keine Fehlermeldung.

LTE5 18. Nov 2017 16:28

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Folgender Artikel hat mir die Augen geöffnet. Ist ein bisschen was zu lesen. Aber es ließt sich zum Glück einfacher als andere englische Texte

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

Ich verwende nun UTF-8.

mensch72 18. Nov 2017 17:45

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
..."Ich verwende nun UTF-8."...
keine schlechte Entscheidung, viele "alte" C/C++-Programmierer mit dem Dogma "ein String" ist und bleibt ein ByteArray sind diesen Weg gegangen!
->aber akzeptiere mit Delphi XE10.x das du nun überall ausser bei WebSeiten dich selbst stets beim Einlesen und Ausgeben und ALLEN WinApi Funktionen um die paassende Konvertierung kümmern musst, bzw immer prüfen ob die implizite Typkonvertierung von Delphi noch/schon richtig arbeitet.
Denn leider ist "draussen" und in allen SYSTEM-APIs wenn UTF, dann UTF-16 verbreitet. Aber Kopfhoch, Lazerus/FreePascal schafft das ja auch, denn die arbeiten intern mit UTF8 und da gibt es von PC,Linux,Mac,Android,IOS,Rasperry,... "alles".
Wenn du mal in Delphi mit UTF8 nicht weiter kommst, sieh eventuell mal in die Sourcen der LCL wie die das dort an der vergleichbaren Stelle machen.

Ich arbeite intern auch mit UTF8. Da ich eh durchgehend mit Getter&Setter Funktionen für "string" Propertys arbeite ist das relativ problemlos. Man muss nur bei UTF8 beachten, das man als ZeichenAnzahl nicht simpel die Byteanzahl nimmt, weil diese ja eben oft nicht mit der realen Zeichenanzahl übereinstimmt... daher ist eine "eigene" Funktion ala "CharCount(utf8String)" gleich von Anfang an eine der wichtigsten Funktionen sie man sicher "einmal richtig" selbst schreiben(und verstehen) sollte.

Wenn ich in meinen Programmen mit "CharCount" arbeite, geht es fast immer um Positionierung oder visuelle Begrenzungen.
Wenn ich in meinen Programmen mit "Length" oder "StrLen" arbeite geht es bei mir weiter um Speicherplatz und echtes Lesen&Schreiben von "Bytes".

;)Ich hoffe ich habe dir hiermit nicht die "Lust" auf eigenes interes UTF8 verdorben.... aber 98% der aktuellen Delphi&Windowsprogrammier nehmen ja nicht zufällig lieber UTF-16, bzw. wissen garnicht das sie intern mit UTF-16 arbeiten;)

LTE5 18. Nov 2017 17:53

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Zitat:

Ich arbeite intern auch mit UTF8. Da ich eh durchgehend mit Getter&Setter Funktionen für "string" Propertys arbeite ist das relativ problemlos. Man muss nur bei UTF8 beachten, das man als ZeichenAnzahl nicht simpel die Byteanzahl nimmt, weil diese ja eben oft nicht mit der realen Zeichenanzahl übereinstimmt... daher ist eine "eigene" Funktion ala "CharCount(utf8String)" gleich von Anfang an eine der wichtigsten Funktionen sie man sicher "einmal richtig" selbst schreiben(und verstehen) sollte.
Ich gucke einfach ob Fehler auftreten und erst dann ändere ich was. Richtig verstanden habe ich das eh nicht und dieses Byte-geschupse mit Streams... davon halte ich mich eh fern ;)

Ich arbeite mit strings, nicht mit utf8strings.

p80286 18. Nov 2017 21:12

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Wenn Du in Deiner Datei den Wert x90 findest, dann kann dieser für NOP stehen, ist sicher, daß an dieser Position Buchstaben zu finden sind, dann könnte es das Zeichen É sein (Extended ASCII) oder das zweite Byte in einer UTF8-Sequenz. Oder anders ausgedrückt es handelt sich nicht um eine ANSI-Kodierung. (Ich hoffe ich habe da recht).
Die Kodierung gibt an in welches Zeichen ein Wert bzw. eine Wertfolge übersetzt wird.

Unter diesen Bedingungen wirst Du nur dann eine Fehlermeldung bekommen, wenn eine Kodierung dekodiert werden soll, die nicht zulässig ist (im Rahmen der gewählten Kodierung).

Gruß
K-H

LTE5 18. Nov 2017 21:28

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Es gibt noch das hier aber wie man das anwendet ist mir ein Rätsel.
http://chsdet.sourceforge.net/

Sonst habe ich noch das hier gefunden, bin aber gerade unfähig eine ansi-Datei zu erstellen. Daher bekomme ich bei jeder Datei true zurück
Delphi-Quellcode:
function FileMayBeUTF8(FileName: WideString): Boolean;
var
 Stream: TMemoryStream;
 BytesRead: Integer;
 ArrayBuff: array [0 .. 127] of Byte;
 PreviousByte: Byte;
 i: Integer;
 YesSequences, NoSequences: Integer;

begin
 if not FileExists(FileName) then
  Exit;

 YesSequences := 0;
 NoSequences := 0;
 Stream := TMemoryStream.Create;
 try
  Stream.LoadFromFile(FileName);
  repeat

   {read from the TMemoryStream}

   BytesRead := Stream.Read(ArrayBuff, High(ArrayBuff) + 1);
   {Do the work on the bytes in the buffer}
   if BytesRead > 1 then
    begin
     for i := 1 to BytesRead - 1 do
      begin
       PreviousByte := ArrayBuff[i - 1];
       if ((ArrayBuff[i] and $C0) = $80) then
        begin
         if ((PreviousByte and $C0) = $C0) then
          begin
           inc(YesSequences)
          end
         else
          begin
           if ((PreviousByte and $80) = $0) then
            inc(NoSequences);
          end;
        end;
      end;
    end;
  until (BytesRead < (High(ArrayBuff) + 1));
  // Below, >= makes ASCII files = UTF-8, which is no problem.
  // Simple > would catch only UTF-8;
  Result := (YesSequences >= NoSequences);

 finally
  Stream.Free;
 end;
end;
Hier noch eine schöne Version. Gibt aber leider bei ANSI auch true zurück
Delphi-Quellcode:
function UTF8CharLength(const c: Byte): Integer;
begin
 // First Byte: 0xxxxxxx
 if ((c and $80) = $00) then
  begin
   Result := 1;
  end
  // First Byte: 110yyyyy
 else if ((c and $E0) = $C0) then
  begin
   Result := 2;
  end
  // First Byte: 1110zzzz
 else if ((c and $F0) = $E0) then
  begin
   Result := 3;
  end
  // First Byte: 11110uuu
 else if ((c and $F8) = $F0) then
  begin
   Result := 4;
  end
  // not valid, return the error value
 else
  begin
   Result := -1;
  end;
end;

function UTF8IsTrailChar(const c: Byte): Boolean;
begin
 // trail bytes have this form: 10xxxxxx
 Result := ((c and $C0) = $80);
end;

function IsUTF8Memory(AMem: PBYTE; ASize: Int64): Boolean;
var
 i: Int64;
 c: Integer;
begin
 Result := True;
 i := 0;
 while (i < ASize) do
  begin
   // get the length if the current UTF-8 character
   c := UTF8CharLength(AMem^);
   // check if it is valid and fits into ASize
   if ((c >= 1) and (c <= 4) and ((i + c - 1) < ASize)) then
    begin
     Inc(i, c);
     Inc(AMem);
     // if it is a multi-byte character, check the trail bytes
     while (c > 1) do
      begin
       if (not UTF8IsTrailChar(AMem^)) then
        begin
         Result := False;
         Break;
        end
       else
        begin
         Dec(c);
         Inc(AMem);
        end;
      end;
    end
   else
    begin
     Result := False;
    end;
   if (not Result) then
    Break;
  end;
end;

LTE5 18. Nov 2017 23:10

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Ich habe mich an der ganzen Sache nun mal versucht. Schöner bekomme ich es leider nicht hin

Delphi-Quellcode:
function ByteToHex(AByte: Byte): string;
const
 Digits: array [0 .. 15] of char = '0123456789ABCDEF';
begin
 Result := Digits[AByte shr 4] + Digits[AByte and $0F];
end;

function BytesToHex(ABytes: TBytes): string;
var
 i: Integer;
begin
 for i := 0 to High(ABytes) do
  Result := Result + ByteToHex(ABytes[i]);
end;

function GetFileEncoding(const FileName: string): string;
var
 Stream: TBytesStream;
 Bytes: TBytes;
begin
 Stream := TBytesStream.Create;

 try
  Stream.LoadFromFile(FileName);

  if Stream.Size >= 3 then
   begin
    SetLength(Bytes, 3);
    Stream.ReadData(Bytes, 3);
   end
  else if Stream.Size >= 2 then
   begin
    SetLength(Bytes, 2);
    Stream.ReadData(Bytes, 2);
   end;

  if BytesToHex(Bytes) = 'EFBBBF' then
   ShowMessage('UTF-8 BOM')
  else if BytesToHex(Bytes) = 'FEFF' then
   ShowMessage('UTF-16 BE');

 finally
  Stream.Free;
 end;
end;
Ich hätte auch noch das hier im Angebot:
Delphi-Quellcode:
function IsTextUnicode(const Text: string): Boolean;
var
 C: Char;
begin
 Result := False;

 for C in Text do
  begin
   if C > #127 then
    begin
     Result := True;
     Break;
    end;
  end;
end;

function IsFileUnicode(const AFile: string): Boolean;
var
 i: Int64;
 Stream: TBytesStream;
begin
 Result := False;

 Stream := TBytesStream.Create;
 try
  Stream.LoadFromFile(AFile);

  for i := 0 to Stream.Size - 1 do
   begin
    if Char(Stream.Bytes[i]) > #127 then
     begin
      Result := True;
      Break;
     end;
   end;
 finally
  Stream.Free;
 end;
end;

freimatz 20. Nov 2017 11:12

AW: Mal wieder Kodierungsprobleme. ANSI UTF8 UTF16
 
Zitat:

Zitat von mensch72 (Beitrag 1386578)
;)Ich hoffe ich habe dir hiermit nicht die "Lust" auf eigenes interes UTF8 verdorben.... aber 98% der aktuellen Delphi&Windowsprogrammier nehmen ja nicht zufällig lieber UTF-16, bzw. wissen garnicht das sie intern mit UTF-16 arbeiten;)

Warum sollte man intern mit UTF8 arbeiten? Der Vorteil hat sich mir noch nicht erschlossen. Ich selber nehme intern das Delphi übliche UTF-16. Nach außen wandele ich dann was gerade dran ist wei z.B. ANSI oder UTF-8. Intern muss ich noch teilweise noch Hand anlegen wgen altem Code und weil die Unterstützung von Delphi für Surrogaten recht bescheiden ist.


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:01 Uhr.

Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf