AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

UTF8 nach Unicode

Ein Thema von backdraft · begonnen am 7. Sep 2019 · letzter Beitrag vom 18. Sep 2019
Antwort Antwort
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
333 Beiträge
 
Delphi 11 Alexandria
 
#1

UTF8 nach Unicode

  Alt 7. Sep 2019, 13:43
Hallo zusammen,

ich hab ein Problem beim Konvertieren von UTF8 nach Unicode bezüglich des Speicherverbrauchs.

Ich habe meine UTF8 Daten in einem TFilestream.
Diese müssen nach Unicode auch wieder in einen TFilestream.

Ich kenne allerdings keine Möglichkeit die Daten direkt im Stream zu konvertieren, außer diese vorher in den Arbeitsspeicher zu laden und dann z.B. über TEncoding.Convert oder via TStringStream mit Encoding zu konvertieren.
Das klappt auch soweit so gut, so lange die Daten halt nicht zusammen 2GB Ram verbrauchen, was aber bei mir der Fall ist.
Ich bräuchte eine Komponente die halt blockweise die Daten von Stream A nach B transferiert.

Kennt jemand noch einen anderen Weg oder eine 3rdParty Komponente die dieses kann?
Nutze für das Projekt Delphi Rio.

Vielen Dank
Oliver
Oliver
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#2

AW: UTF8 nach Unicode

  Alt 7. Sep 2019, 15:39
Blockweise ist die Konvertierung möglich, indem nur die Zeichen z.B. bis zum nächsten Zeilenende (CR/LF oder LF) aus der UTF-8 kodierten Datei eingelesen, diese dann konvertiert und in den Ausgabestream geschrieben werden. Dazu ist allerdings Voraussetzung, dass die Datei entsprechende Zeilentrenungen enthält. Eine Komponente benötigt man dafür nicht, es ist einfach mit zwei Streams lösbar.
Michael Justin
habarisoft.com

Geändert von mjustin ( 7. Sep 2019 um 15:46 Uhr)
  Mit Zitat antworten Zitat
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
333 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: UTF8 nach Unicode

  Alt 7. Sep 2019, 17:06
Hallo,

Zeilenweise hab ich nicht wirklich.

da ich nix gefunden habe, habe ich mal einen Konverter in beide Richtungen versucht.
Bis jetzt klappt es. Kann man bestimmt noch optimieren, aber löst erstmal mein Problem.


Code:
class procedure TUTF8Helper.UnicodeToUTF8(aInput, aOutput: TStream);
const
  cMaxBufferIn = 8192;
  cMaxBufferOut = 8192;

var
  lInBuff: array[0..cMaxBufferIn] of WideChar;
  lReadLen: Integer;
  lReadChars: Integer;
  lCharPos: Integer;
  lChar: WideChar;
  lWord: Word absolute lChar;
  lOutBuff: array[0..cMaxBufferOut] of Byte;
  lOutPos: Integer;
  lOutByte: Byte;
begin
  aInput.Position := 0;
  aOutput.Size := 0;

  lOutPos := 0;
  while aInput.Position < aInput.Size do
    begin

      lReadLen := aInput.Read(lInBuff[0], cMaxBufferIn * SizeOf(WideChar));
      lReadChars := lReadLen div SizeOf(WideChar);
      lCharPos := 0;

      while lCharPos < lReadChars do
        begin
          lChar := lInBuff[lCharPos];

          if (lWord >= $0001) and (lWord <= $007F) then
            begin
              // 1-byte code
              lOutByte := lWord and $7F;
              lOutBuff[lOutPos] := lOutByte;
              inc(lOutPos);
            end
          else
            begin
              if (lWord >= $0080) and (lWord <= $07FF) then
                begin
                  // 2-byte code
                  lOutByte := ($C0 or ((lWord shr 6) and $1F));
                  lOutBuff[lOutPos] := lOutByte;
                  lOutByte := ($80 or (lWord and $3F));
                  lOutBuff[lOutPos + 1] := lOutByte;
                  inc(lOutPos, 2);
                end
              else
                begin
                  // 3-byte code
                  lOutByte := ($E0 or ((lWord shr 12) and $0F));
                  lOutBuff[lOutPos] := lOutByte;
                  lOutByte := ($80 or ((lWord shr 6) and $3F));
                  lOutBuff[lOutPos + 1] := lOutByte;
                  lOutByte := ($80 or (lWord and $3F));
                  lOutBuff[lOutPos + 2] := lOutByte;
                  inc(lOutPos, 3);
                end;
            end;

          inc(lCharPos);

          if (lOutPos > cMaxBufferOut - 4) then
            begin
              aOutput.Write(lOutBuff[0], lOutPos * SizeOf(Byte));
              lOutPos := 0;
            end;

        end;

    end;

  aOutput.Write(lOutBuff[0], lOutPos * SizeOf(Byte));
  lOutPos := 0;

end;
Code:
class procedure TUTF8Helper.UTF8ToUnicode(aInput, aOutput: TStream);

const
  cMaxBufferIn = 8192;
  cMaxBufferOut = 8192;

  function _BytesLeft(aPos: Int64): Int64;
  begin
    Result := cMaxBufferIn - aPos - 1;
  end;

var
  lInBuff: array[0..cMaxBufferIn] of Byte;
  lReadLen: Integer;
  lPos: Integer;
  lPart1: Byte;
  lPart2: Byte;
  lPart3: Byte;
  lOutWord: Word;
  lOutBuff: array[0..cMaxBufferOut] of Word;
  lOutPos: Integer;
begin
  aInput.Position := 0;
  aOutput.Size := 0;

  lPart1 := 0;
  lPart2 := 0;
  lPart3 := 0;
  lOutPos := 0;

  while aInput.Position < aInput.Size do
    begin
      lReadLen := aInput.Read(lInBuff[0], cMaxBufferIn);
      lPos := 0;
      while lPos < lReadLen do
        begin
        lPart1 := lInBuff[lPos];
        if ((lPart1 and $80) = 0) then
          begin
            // 1-byte code
            inc(lPos);
            lOutWord := lPart1;
            lOutBuff[lOutPos] := lOutWord;
            inc(lOutPos);
          end
        else
          begin
            if ((lPart1 and $E0) = $C0) then
              begin
                if (_BytesLeft(lPos) < 1) then
                  begin
                    aInput.Seek(-1, soCurrent);
                    Break;
                  end;

                // 2-byte code
                lPart2 := lInBuff[lPos + 1];
                inc(lPos, 2);
                lOutWord := ((lPart1 and $1F) shl 6) or (lPart2 and $3F);
                lOutBuff[lOutPos] := lOutWord;
                inc(lOutPos);
              end
            else
              begin
                if (_BytesLeft(lPos) < 2) then
                  begin
                    aInput.Seek(-2, soCurrent);
                    Break;
                  end;

                // 3-byte code
                lPart2 := lInBuff[lPos + 1];
                lPart3 := lInBuff[lPos + 2];
                inc(lPos, 3);
                lOutWord := ((lPart1 and $0F) shl 12) or ((lPart2 and $3F) shl 6) or (lPart3 and $3F);
                lOutBuff[lOutPos] := lOutWord;
                inc(lOutPos);
              end;
          end;

        if (lOutPos = cMaxBufferOut) then
          begin
            aOutput.Write(lOutBuff[0], lOutPos * SizeOf(Word));
            lOutPos := 0;
          end;

      end;

    end;

  if (lOutPos > 0) then
    begin
      aOutput.Write(lOutBuff[0], lOutPos * SizeOf(Word));
      lOutPos := 0;
    end;

end;
Oliver
  Mit Zitat antworten Zitat
Redeemer

Registriert seit: 19. Jan 2009
Ort: Kirchlinteln (LK Verden)
1.017 Beiträge
 
Delphi 2009 Professional
 
#4

AW: UTF8 nach Unicode

  Alt 7. Sep 2019, 17:54
Hallo,

Zeilenweise hab ich nicht wirklich.

da ich nix gefunden habe, habe ich mal einen Konverter in beide Richtungen versucht.
Bis jetzt klappt es. Kann man bestimmt noch optimieren, aber löst erstmal mein Problem.
Kleiner Hinweis: Der Algorithmus konvertiert zwischen CESU-8 und UTF-16 („WideString“). Außerdem kann er zwischen UTF-8 und UCS-2 konvertieren. Für die Umwandlung zwischen UTF-8 und Unicode ist die Unterstützung von 4-Byte-Codes nötig, für die Umwandlung zwischen UTF-8 und UTF-16 fehlt zusätzlich die Unterstützung von Surrogates (die sind in CESU-8 schon drin, daher funktioniert dein Code problemlos mit CESU-8).
Janni
2005 PE, 2009 PA, XE2 PA
  Mit Zitat antworten Zitat
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
333 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: UTF8 nach Unicode

  Alt 8. Sep 2019, 11:49
Kleiner Hinweis: Der Algorithmus konvertiert zwischen CESU-8 und UTF-16 („WideString“). Außerdem kann er zwischen UTF-8 und UCS-2 konvertieren. Für die Umwandlung zwischen UTF-8 und Unicode ist die Unterstützung von 4-Byte-Codes nötig, für die Umwandlung zwischen UTF-8 und UTF-16 fehlt zusätzlich die Unterstützung von Surrogates (die sind in CESU-8 schon drin, daher funktioniert dein Code problemlos mit CESU-8).
Delphi Rio nutzt doch als Typ für string einen WideString oder auch UnicodeString. Du schreibst, das ist UTF-16.
UTF-16 hat aber doch ggf. mehr als 2 Byte pro Zeichen.
Und WideString sind doch immer fest 2 Byte pro Zeichen, oder irre ich?
Bin jetzt eher verwirrt.

Bezüglich CESU-8. Was erzeugt TEncoding.UTF8 in Delphi denn?

Ich brauche auf jeden Fall einen Konverter von einem String aus Delphi 10.3 und einem XML UTF-8 Datenstrom.
Komm ich das mit dem CESU-8 zurecht?

Vielen Dank schonmal für die Hilfe
Oliver
Oliver
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#6

AW: UTF8 nach Unicode

  Alt 8. Sep 2019, 12:07
Dafür nimmt man eigentlich TStreamReader und TStreamWriter .

Stream und Encoding angeben, und dann per ReadBlock/Write die Daten von links nach rechts schieben.

Und das ganze mit einer Handvoll Code:
Delphi-Quellcode:
procedure Transfer(ASource: TStream; ASourceEncoding: TEncoding; ADest: TStream; ADestEncoding: TEncoding);
const
  bufferSize = 2048;
var
  reader: TStreamReader;
  writer: TStreamWriter;
  buffer: TCharArray;
  readCount: Integer;
begin
  reader := TStreamReader.Create(ASource, ASourceEncoding);
  try

    writer := TStreamWriter.Create(ADest, ADestEncoding);
    try
      SetLength(buffer, bufferSize);

      repeat
        readCount := reader.ReadBlock(buffer, 0, bufferSize);
        if readCount > 0 then
          writer.Write(buffer, 0, readCount);
      until (readCount < bufferSize);

    finally
      FreeAndNil(writer);
    end;

  finally
    FreeAndNil(reader);
  end;
end;

Geändert von Schokohase ( 8. Sep 2019 um 13:33 Uhr)
  Mit Zitat antworten Zitat
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
333 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: UTF8 nach Unicode

  Alt 8. Sep 2019, 18:05
Ahh, cool, das klingt easy.
Und durch das SourceEncoding ließt er auch immer volle Zeichen ein?
Ich meine, ich hab ja das Problem bei meinem Code, dass das Buffer Ende auf ein Zeichen fällt, was 3 Byte benötigt in UTF8 und nur 2 noch zu Verfügung stehen. Da muss ich ja wieder mit einem Seek zurückfahren im SourceFile.
Wäre ja cool, wenn das hier nicht ist.
Danke
Oliver
Oliver
  Mit Zitat antworten Zitat
Redeemer

Registriert seit: 19. Jan 2009
Ort: Kirchlinteln (LK Verden)
1.017 Beiträge
 
Delphi 2009 Professional
 
#8

AW: UTF8 nach Unicode

  Alt 8. Sep 2019, 19:52
Delphi Rio nutzt doch als Typ für string einen WideString oder auch UnicodeString. Du schreibst, das ist UTF-16.
UTF-16 hat aber doch ggf. mehr als 2 Byte pro Zeichen.
Und WideString sind doch immer fest 2 Byte pro Zeichen, oder irre ich?
Bin jetzt eher verwirrt.
WideString ist ein Windows-Typ und Windows ab 2000 definiert WideStrings als UTF-16LE (Vorgänger-NTs: UCS-2LE). Sie kodieren jedes Zeichen mit 2 oder 4 Bytes, da man die 1.112.064 Codepunkte nicht mit 2 Bytes darstellen kann.

Bei den 2-Byte-Zeichen ist UTF-16 dasselbe wie UCS-2 (und UTF-8 dasselbe wie CESU-8), Zeichen darüber werden durch Surrogates kodiert, die sich an der Stelle 0xD800 bis 0xDFFF in der Ebene 0 (BMP) befinden. Die Surrogates sind keine Codepunkte und haben deshalb in UTF-8 nichts zu suchen. Der Versuch, Surrogates wie Codepunkte zu behandeln führt zu CESU-8, das Zeichen mit 1, 2, 3 oder 6 Byte kodiert.

Bezüglich CESU-8. Was erzeugt TEncoding.UTF8 in Delphi denn?
UTF-8.

Ich brauche auf jeden Fall einen Konverter von einem String aus Delphi 10.3 und einem XML UTF-8 Datenstrom.
Komm ich das mit dem CESU-8 zurecht?
Legt eine Software eine UTF-8-Eingabe zunächst als UTF-16 im Speicher ab und führt sie keine gesonderte Prüfung durch, unterstützt sie auch CESU-8.
Es gibt Software, die behauptet, UTF-8 zu unterstützen, meint aber CESU-8. Jeweils andere Programme unterstützen nur die BMP, sodass die Frage egal ist. Teilweise stürzen Programme auch ab, wenn man das jeweils andere eingibt.
Janni
2005 PE, 2009 PA, XE2 PA
  Mit Zitat antworten Zitat
backdraft

Registriert seit: 19. Apr 2005
Ort: Hückeswagen
333 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: UTF8 nach Unicode

  Alt 18. Sep 2019, 08:28
Also ich nutze jetzt den TStreamWriter und damit klappt alles super
Vielen Dank für den Tipp.
Oliver
  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 02:00 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz