2013-05-13 57 views
9

私はDelphi 7のコードをXE4に移植していますので、ここではUnicodeが対象です。DelphiのUnicode文字列の長さ(バイト単位)

文字列がTMemoryStreamに書き込まれる方法があるので、this embarcadero articleによれば、文字列の長さにChar型のサイズを乗じて、必要なバイト数で長さを取得する必要がありますWriteBufferの長さ(バイト単位)のパラメーター。

ので、前に:

rawHtml : string; //AnsiString 
... 
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml); 

後:DelphiのUnicodeString型の

rawHtml : string; //UnicodeString 
... 
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml)* SizeOf(Char)); 

私の理解で、それは内部的にUTF-16であるということです。しかし、私の一般的なUnicodeの理解は、すべてのUnicode文字を2バイトで表現できるわけではなく、いくつかのコーナーケースの外部文字は4バイトを取るということです。 Another of embarcadero's articlesは私の疑惑を確認しているようです。「実際には、1つのCharが2バイトに等しいことは事実ではありません!

だから、Length(rawHtml)* SizeOf(Char)が本当に正確であるかどうか、またはより正確な文字列のサイズを判断するためのよりよい方法があるかどうかは疑問に思えます。

+5

なぜ、「TMemoryStream」の代わりに「TStringStream」を使用しないのですか? – teran

+0

最終的に、MemoryStreamはTWebBrowserコンポーネントに渡されて表示されます。私が見てきたすべての例は、MemoryStreamを使っています。その目的のためにStringStreamを使用するのが良いでしょうか? –

+0

@Jessica結局、彼らはどちらも 'TStream'をベースにしています。つまり、両方の内部構造が同じであることを意味します。とにかくTFileStreamやTResourceStreamもあなたの場合に使用することができます。つまり、あなたのブラウザにファイルやリソースを送信していた場合です。 –

答えて

7

DelphiのUnicodeString型の理解は、内部的にはUTF-16 です。

あなたは、DelphiのUnicodeStringのUTF-16エンコーディングについて正しいです。これは、ある1つの16ビット文字が、のすべてをChar要素のstringという配列として表現するのに十分な幅であることを意味します。

しかし、ユニコードの私の一般的な理解ではなく、すべて Unicode文字が一部 コーナーケース外国文字は4つのバイトを取ること、でも2バイトで表現することができるということです。

ただし、ここで少し誤解があります。 Length関数は文字の詳細な検査を実行せず、文字列内のサロゲートを考慮せずに、単に16ビットのWideChar要素の数を返します。文字列データのバイトサイズは常に固定されているとLength(S) * SizeOf(Char)、どんなに等しい:これは、あなたがUnicodeStringSupplementary Planesのいずれかから単一の文字を割り当てると、Lengthは2

program Egyptian; 

{$APPTYPE CONSOLE} 

var 
    S: UnicodeString; 

begin 
    S := #$1304E; // single char 
    Writeln(Length(S)); 
    Readln; 
end. 

結論を返します。どのような意味しますSに可変長文字が含まれている場合

9

DelphiのUnicodeStringは、UTF-16でエンコードされています。 UTF-16は、UTF-8のように、可変長のエンコーディングです。つまり、1つのUnicodeコードポイントで複数の文字要素をエンコードする必要があります。興味深い点として、唯一の固定長UnicodeエンコーディングはUTF-32です。 UTF-16エンコーディングは16ビットの文字要素を使用するため、名前が使用されます。

Unicode Delphiでは、Charは、WideCharのエイリアスで、UTF-16文字要素です。 stringUnicodeStringの別名で、WideChar要素の配列です。 Length()関数は配列内の要素の数を返します。

したがっての場合、SizeOf(Char)は常に2です。一部のUnicodeコードポイントは、複数の文字要素、またはCharでエンコードされます。しかし、Length()は、文字要素の数とではなく、のコードポイント数を返します。文字要素はすべて同じサイズです。したがって

memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml)* SizeOf(Char)); 

が正しいです。

3

あなたがやっていることは正しいです(sizeof(Char)付き)。

あなたが参照するのは、1つの文字が1つのコードポイントを参照しているわけではありません(たとえば、サロゲートペアのためです)。しかし、文字列のUSC2でエンコードされた(UTF-16ではない)文字は、正確にはLength(Str) * sizeof(Char)のバイト数を占めます。

Delphiで使用されるUnicodeエンコーディングは、Wバリアントで期待されるすべてのWindows APIコールと同じです。

+0

あなたはどういうことを言っていますか?質問はUTF16についてであり、UCS2についてではありません。 –

+0

UnicodeStringではUTF-16が使用され、古いUCS-2では使用されません。そのためコードポイントは1つまたは2つのCharsで構成できます。しかしDavidが説明したように、サロゲートペアは2つのCharsであり、Lengthはコードポイントの数ではなくChar要素の数をカウントします。 –

+0

Windowsの文字列はWindows 2000以降UTF-16になっています – afrazier

3

他は、UnicodeStringがどのようにエンコードされているか、バイト長を計算する方法について説明しています。 RTLにはすでにこのような機能があります。SysUtils.ByteLength()

memorystream1.WriteBuffer(PChar(rawHtml)^, ByteLength(rawHtml)); 
+0

これは本当にひどく設計された機能です。 UnicodeString以外の文字列を受け入れますが、むしろ無駄な値を返します。 UTF8Stringを渡すとどうなるか考えてみてください。私はこれを無駄にQCしました。 –

+0

あなたのQCレポートを読みました。UnicodeStringをRawByteStringに渡すと、今度はUTF-16からAnsiにデータ変換が行われるので、あなたの提案した「解決法」はあまり良くありません。 'RawByteString'は' UnicodeString'データを保存せず、 'AnsiString(N')データだけを保存します。正しい解決策は、他のRTL関数と同様に、** UnicodeStringとRawByteStringの両方でByteLength()をオーバーロードすることです。 –

+0

あなたは大丈夫です。 QCレポートを修正します。報告書が公開されていないこともあるので、おそらく時間の無駄です。 –

関連する問題