AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Vorstellung Unit: File encoding detector
Thema durchsuchen
Ansicht
Themen-Optionen

Vorstellung Unit: File encoding detector

Ein Thema von LTE5 · begonnen am 19. Nov 2017 · letzter Beitrag vom 15. Jan 2018
Antwort Antwort
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.787 Beiträge
 
Delphi 12 Athens
 
#1

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 13:53
Zitat:
Findet sich ein Charakter > 127 (also außerhalb des ASCII-Bereichs), wird unterbrochen denn es ist vermutlich Unicode.
Erfahrungsgemäß ist das gerade im deutschsprachigen Umfeld eher selten der Fall: Ein Zeichen > #127 ist dort in der Regel ein Umlaut.

Nur so als Hinweis: Die Unit System.Character enthält ein paar Methoden für UTF-32 Zeichen und in System ist auch ein Typ UCS4String definiert, der allerdings nichts mit einem üblichen Delphi-String gemein hat.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
LTE5

Registriert seit: 13. Nov 2017
355 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#2

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 14:03
Irgendwie habe ich den Eindruck meine Unit ist mehr als nutzlos in diesem Encoding-Labyrinth.
Aber mehr als "erraten" kann man ja eh nicht. Also versuche ich jedenfalls das.

Wenn jemand eine bessere Unit bauen kann, würde sich jemand dazu bereit erklären?


Zitat:
Findet sich ein Charakter > 127 (also außerhalb des ASCII-Bereichs), wird unterbrochen denn es ist vermutlich Unicode.
Was ich damit meine ist, wenn ein Zeichen > 127 vorkommt, ist es etwas anderes als ANSI.

# Ich habe die Unit in #1 nochmal angepasst.

# Ich habe die Unit in #1 noch einmal angepasst. Rückgabewert ist jetzt TEncoding.
Ich habe lange getestet. Wenn ich nicht gerade in den Kopfdaten einer Datei rumwühle und komische Werte eintrage, wird die Datei immer korrekt gelesen.

# Einen Fall habe ich doch gefunden. Wenn man eine utf-8-Datei ohne BOM prüft und kein Default-Encoding angibt, kommt natürlich ANSI zurück und é kann nicht dargestellt werden.
Deswegen gibt es nun eine überladene Version von TEncodingDetect.GetFileEncoding().

Geändert von LTE5 (19. Nov 2017 um 15:37 Uhr)
  Mit Zitat antworten Zitat
HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
989 Beiträge
 
Delphi 6 Professional
 
#3

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 15:47
Hmm..

Zitat:
Findet sich ein Charakter > 127 (also außerhalb des ASCII-Bereichs), wird unterbrochen denn es ist vermutlich Unicode.
Was ich damit meine ist, wenn ein Zeichen > 127 vorkommt, ist es etwas anderes als ANSI.
Nicht ganz:

ASCII entspricht 7 Bit = 0-127
ANSI entspricht 8 Bit = 0-255

https://de.wikipedia.org/wiki/ANSI-Zeichencode

Somit kann ein gültiges ANSI-Zeichen auch als Zahl 240 sein..
  Mit Zitat antworten Zitat
LTE5

Registriert seit: 13. Nov 2017
355 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#4

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 15:48
Wie genau müsste man denn dann nachprüfen?
Einfach bis 255 klingt ja zu einfach.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.787 Beiträge
 
Delphi 12 Athens
 
#5

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 16:26
Die Problematik liegt eigentlich in der Unterscheidung zwischen ANSI und UTF-8 ohne BOM. Welche CodePage bei ANSI verwendet werden soll kannst du eh kaum raus finden, wenn du keine Informationen über die Art des Inhaltes hast (manche Zeichen kommen in einer bestimmten Art Text halt nicht vor und sind ein Trigger für eine falsche Codierung). Allerdings beginnen in UTF-8 alle Zeichen > #127 mit einer bestimmten Sequenz. Sind also Zeichen > #127 vorhanden, die nicht mit einer dieser UTF-8 Sequenzen beginnen, handelt es sich offenbar nicht um ein UTF-8 Encoding.

Ein sehr einfacher Ansatz um ANSI und UTF-8 zu unterscheiden wäre z.B. einfach erst mit TEncoding.UTF8 (BOM oder nicht erkennt er automatisch) einzulesen und falls das eine Exception wirft eben mit TEncoding.ANSI zu lesen. Bei dieser Vorgehensweise braucht man auch nicht erst das Encoding ermitteln, sondern bekommt das beim Lesen gleich mit. Das spart ein erneutes Durchlaufen zur eigentlichen Verarbeitung der Daten, denn wozu brauche ich das Encoding der Daten, wenn ich sie danach nicht damit lesen will. Das könnte dann in etwa so aussehen:
Delphi-Quellcode:
function StreamToString(Stream: TStream): string;
var
  enc: TEncoding;
  reader: TStreamReader;
  savePosition: Int64;
begin
  savePosition := Stream.Position;
  try
    { ANSI als letztes, denn das klappt immer } 
    for enc in TArray<TEncoding>.Create(TEncoding.UTF8, TEncoding.ANSI) do begin
      Stream.Position := savePosition;
      reader := TStreamReader.Create(Stream, enc, false);
      try
        try
          result := reader.ReadToEnd;
          { Wenn es geklappt hat, Schleife verlassen }
          Break;
        except
          on EEncodingError do ;
        end;
      finally
        reader.Free;
      end;
    end;
  finally
    Stream.Position := savePosition;
  end;
end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Wosi

Registriert seit: 29. Aug 2007
59 Beiträge
 
#6

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 16:27
Für den Fall, dass es keinen BOM gibt:
Gibt es nur Zeichen bis #127, dann ist es sicher den Stream als ASCII-String zu interpretieren.
Liegen einzelne Byte jedoch im Wertebereich zwischen #128 und #255 dann wird es komplizierter. Dann könnte es sich um UTF8 oder ANSI oder ein anderes lokales Format handeln.
Es gibt dabei leider kein 100% sicheres Vorgehen um das korrekte Encoding zu ermitteln. Es existieren lediglich Vorgehensweisen um ein möglichst gutes Ergebnis zu erzielen.

Ich musste mal ein Problem lösen, bei dem eine Software muss regelmäßig entscheiden musste, ob eine Datei UTF8 oder ANSI beinhaltet. So habe ich es damals gelöst:

- Ist ein BOM-Header vorhanden, dann nimm das passende Encoding für den BOM-Header
- Ansonsten untersuche den Bytestream auf gültige UTF8-Multi-Byte-Zeichen. Gibt es eine Byte-Sequenz, die nicht UTF8-Konform ist, dann nimm ANSI. Entspricht der Byte-Stream hingegen bis zum Ende gültigem UTF8, dann nimm UTF8.

Die Definition von Multi-Byte-Zeichen gibt es bei Wikipedia.
Das beschriebene Vorgehen funktioniert generell ganz gut. Allerdings scheitert es, wenn
- wenn sich mindestens ein ungültiges Multi-Byte-Zeichen in einer ansonsten gültigen UTF8-Datei befindet (von Text-Editoren werden diese Zeichen in der Regel als "�" dargestellt)
- wenn der Dateiinhalt weder ANSI noch UTF8 ist
  Mit Zitat antworten Zitat
LTE5

Registriert seit: 13. Nov 2017
355 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#7

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 16:40
Habe wieder viel zu lesen! Melde mich gleich.
Ich habe in der Zwischenzeit Support für Streams eingebaut. Schreibe ich gleich in Beitrag #1.

Zitat:
Das spart ein erneutes Durchlaufen zur eigentlichen Verarbeitung der Daten, denn wozu brauche ich das Encoding der Daten, wenn ich sie danach nicht damit lesen will.
Genau das will ich ja aber. Ich brauche das Encoding, eben damit ich die Daten lesen kann. Sonst würde ich mir die ganze Mühe nicht machen.

Zitat:
Allerdings beginnen in UTF-8 alle Zeichen > #127 mit einer bestimmten Sequenz. Sind also Zeichen > #127 vorhanden, die nicht mit einer dieser UTF-8 Sequenzen beginnen, handelt es sich offenbar nicht um ein UTF-8 Encoding.
Welche Sequenz ist das denn genau und wie prüfe ich danach?

Sollte man demnach hier unten also besser bis 255 prüfen und ab 127 zusätzlich diese Sequenz prüfen?
Delphi-Quellcode:
class function TEncodingDetect.IsStreamUnicode(const Stream: TStream): Boolean;
var
 i: Integer;
 B: Byte;
begin
 Result := False;

 if Stream.Size = 0 then
  Exit;

 for i := 0 to Stream.Size - 1 do
  begin
   Stream.ReadData(B, Sizeof(B));
   Result := Ord(B) > 127;
   if Result then
    Break;
  end;
end;
Zitat:
- Ansonsten untersuche den Bytestream auf gültige UTF8-Multi-Byte-Zeichen. Gibt es eine Byte-Sequenz, die nicht UTF8-Konform ist, dann nimm ANSI. Entspricht der Byte-Stream hingegen bis zum Ende gültigem UTF8, dann nimm UTF8.
Ich werde mich mal dran versuchen und TEncodingDetect.IsStreamUnicode anpassen. Könnte jedenfalls schwer werden, denn von dem Wikipedia-Artikel verstehe ich nichts.

Geändert von LTE5 (19. Nov 2017 um 16:55 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.787 Beiträge
 
Delphi 12 Athens
 
#8

AW: Vorstellung Unit: File encoding detector

  Alt 19. Nov 2017, 16:56
Genau das will ich ja aber. Ich brauche das Encoding, eben damit ich die Daten lesen kann. Sonst würde ich mir die ganze Mühe nicht machen.
Die gezeigte Funktion liefert dir ja auch den String korrekt decodiert zurück. Den kannst du dann ja weiter verarbeiten ohne dich noch um das Encoding der Datei kümmern zu müssen.

Welche Sequenz ist das denn genau und wie prüfe ich danach?
Kannst du bei Wikipedia nachsehen: Zulässige Bytes und ihre Bedeutung
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:49 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz