![]() |
UTF8 nach Unicode
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 |
AW: UTF8 nach Unicode
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.
|
AW: UTF8 nach Unicode
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; |
AW: UTF8 nach Unicode
Zitat:
|
AW: UTF8 nach Unicode
Zitat:
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 |
AW: UTF8 nach Unicode
Dafür nimmt man eigentlich
Delphi-Quellcode:
und
TStreamReader
Delphi-Quellcode:
.
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; |
AW: UTF8 nach Unicode
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 |
AW: UTF8 nach Unicode
Zitat:
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. Zitat:
Zitat:
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. |
AW: UTF8 nach Unicode
Also ich nutze jetzt den TStreamWriter und damit klappt alles super :-)
Vielen Dank für den Tipp. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:19 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