Stimmen aus RawByteString string nicht automatisch aufrufen UTF8Decode?
Ich soll zum speichern von beliebigen binären Daten als BLOB in einer SQlite-Datenbank.
Den Daten Hinzugefügt werden, wie value
mit dieser Funktion:
procedure TSQLiteDatabase.AddParamText(name: string; value: string);
Ich jetzt konvertieren wollen WideString
in seine UTF-8-Darstellung, so kann es auch in der Datenbank gespeichert. Nach dem Aufruf UTF8Encode
und speichern das Ergebnis in der Datenbank habe ich festgestellt, dass die Daten innerhalb der Datenbank ist nicht UTF8 decodiert werden. Vielmehr ist es codiert, wie AnsiString in meinem computer das Gebietsschema.
Lief ich folgenden test, um zu überprüfen, was passiert ist:
type
{$IFDEF Unicode}
TBinary = RawByteString;
{$ELSE}
TBinary = AnsiString;
{$ENDIF}
procedure TForm1.Button1Click(Sender: TObject);
var
original: WideString;
blob: TBinary;
begin
original := 'ä';
blob := UTF8Encode(original);
// Delphi 6: ä (as expected)
// Delphi XE4: ä (unexpected! How did it do an automatic UTF8Decode???)
ShowMessage(blob);
end;
Nachdem das Zeichen "ä" hat, wurde in UTF8, dass die Daten korrekt in den Speicher ("ä"), jedoch, sobald ich den pass der TBinary
Wert an eine Funktion (als string
oder AnsiString
), Delphi XE4 hat eine "Magische festgelegten" Aufruf von UTF8Decode aus irgendeinem Grund ich weiß es nicht.
Ich habe bereits einen workaround gefunden, dies zu vermeiden:
function RealUTF8Encode(AInput: WideString): TBinary;
var
tmp: TBinary;
begin
tmp := UTF8Encode(AInput);
SetLength(result, Length(tmp));
CopyMemory(@result[1], @tmp[1], Length(tmp));
end;
procedure TForm1.Button2Click(Sender: TObject);
var
original: WideString;
blob: TBinary;
begin
original := 'ä';
blob := RealUTF8Encode(original);
// Delphi 6: ä (as expected)
// Delphi XE4: ä (as expected)
ShowMessage(blob);
end;
Jedoch, diese Problemumgehung mit RealUTF8Encode
schmutzig aussieht auf mich und ich würde gerne verstehen, warum ein einfacher Aufruf von UTF8Encode
hat nicht funktioniert, und wenn es eine bessere Lösung.
Du musst angemeldet sein, um einen Kommentar abzugeben.
In Ansi-Versionen von Delphi (vor D2009),
UTF8Encode()
gibt einen UTF-8-kodiertAnsiString
. In Unicode-Versionen (D2009 und später), gibt es ein UTF-8-kodiertRawByteString
mit einer code-Seite vonCP_UTF8
(65001) zugeordnet.In Ansi-Versionen,
ShowMessage()
nimmt eineAnsiString
als Eingabe, und die UTF-8-string ist eineAnsiString
, so wird es dargestellt-ist. In Unicode-VersionenShowMessage()
nimmt eine UTF-16-kodiertUnicodeString
als Eingabe, so ist die UTF-8-kodiertRawByteString
wird in UTF-16 konvertiert die Verwendung der Ihr zugewiesenenCP-UTF8
code Seite.Wenn Sie tatsächlich schrieb die
blob
Daten direkt an die Datenbank, die Sie finden würde, dass er möglicherweise oder möglicherweise nicht UTF-8 codiert sein, je nachdem, wie Sie es schreiben. Aber dein Ansatz ist falsch; die Verwendung vonRawByteString
ist falsch in dieser situation.RawByteString
verwendet werden soll als parameter für eine Prozedur nur. Verwenden Sie es nicht als eine lokale variable. Das ist die Quelle Ihres Problems. Aus der Dokumentation:Für Unicode-Versionen von Delphi, statt
RawByteString
würde ich vorschlagen, dass Sie verwendenTBytes
zu halten Ihre UTF-8-Daten und Kodieren Sie es mitTEncoding
:Du suchst einen Datentyp, der keine implizite text-Codierungen, wenn herum, und
TBytes
ist der Typ.Für Ansi-Versionen von Delphi können Sie
AnsiString
,WideString
undUTF8Encode
genau so, wie Sie tun.Persönlich würde ich jedoch empfehlen, mit
TBytes
konsequent für Ihre UTF-8-Daten. Also, wenn Sie brauchen eine einheitliche code-Basis, unterstützt Ansi und Unicode-Compiler (ugh!) dann sollten Sie einige Helfer:Die Ansi-version verursacht mehr heap-Zuweisungen als notwendig. Sie könnten auch entscheiden, zu schreiben, ein effizienter Helfer, die Anrufe
WideCharToMultiByte()
direkt.In den Unicode-Versionen von Delphi, wenn Sie aus irgendeinem Grund nicht verwenden wollen
TBytes
für UTF-8-Daten, die Sie verwenden könnenUTF8String
statt. Dies ist eine BesondereAnsiString
dass immer dieCP_UTF8
code Seite. Sie können dann schreiben:und der compiler konvertiert von UTF-16 zu UTF-8 hinter den kulissen für Sie. Ich würde nicht empfehlen dies aber nicht, weil es nicht unterstützt, auf mobilen Plattformen oder in Ansi-Versionen von Delphi (
UTF8String
existiert seit Delphi 6, aber es war nicht eine echte UTF-8-string bis Delphi 2009). Das ist, unter anderen Gründen, warum ich schlage vor, daß duTBytes
. Meine Philosophie ist, zumindest in der Unicode-Zeitalter, daß es die nativestring
geben, und jeder anderen Codierung gespeichert werden sollTBytes
.WideCharToMultiByte
nun, das ist eine performante Lösung. Ich habe auch gelernt, dass die neuen Elemente inStrRec
- auch die Zeichentabelle. Dies erklärt, warum meinCopyMemory
hack in der original-post hat funktioniert als workaround.WideCharToMultiByte
(einer für die entscheidenden der Ausgabe-Größe und eine für die eigentliche Konvertierung) sind schneller als eine einzelneUTF8Encode
mit ManipulationStrRec.codePage
im Speicher (anstelle der BerufungSetCodePage
was tun würde, eine unnötigeUniqueString
) - ich weiß, dass dies ein dirty hack, aber ich wollte testen, ob ich noch schneller als die WinAPI. Vielleicht habe ich auch etwas falsch gemacht haben bei dem experiment.TUTF8Encoding.GetMaxByteCount
zurück(CharCount + 1) * 3;
. Ich bin nicht sicher, ob dies sicher ist, da RFC3629 gibt die maximale Anzahl von bytes pro Zeichen auf 4. Ich denke, Sie haben die 3 gewählt, weil Sie davon ausgehen, dass keine real-world-string muss 4 gezogen durch den ganzen string. Also, es könnte möglich sein, erstellen Sie eine Zeichenfolge, die konvertiert werden kann mithilfeTEncoding.UTF8.GetBytes
. Ich sehe den Konflikt: die Multiplikation alles mit 4 würde bedeuten, große Speicherauslastung, wenn der input-string ist sehr groß. Dies ist der Grund, warum mein Erster Versuch war der Einsatz von 2 Anrufe von der API, die ist langsamer.SetLength
um die tatsächliche Länge der Daten nicht verschoben werden.