我正在努力将一些Delphi 7代码移植到XE4,因此,unicode就是这里的主题。
我有一个方法,其中字符串被写入TMemoryStream,因此根据this embarcadero article,我应该将字符串的长度(以字符为单位)乘以Char类型的大小以获得字节长度这是WriteBuffer的长度(以字节为单位)参数所需的。
所以之前:
rawHtml : string; //AnsiString
...
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml);
后:
rawHtml : string; //UnicodeString
...
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml)* SizeOf(Char));
我对Delphi的UnicodeString类型的理解是它内部是UTF-16。但我对Unicode的一般理解是,即使是2个字节也不能表示所有unicode字符,有些角落外壳字符将占用4个字节。 Another of embarcadero's articles似乎证实了我的怀疑,“事实上,一个Char等于两个字节并不总是正确的!”
所以...让我想知道Length(rawHtml)* SizeOf(Char)
是否真的足够强大以保持一致,或者是否有更好的方法来确定更准确的字符串大小?
答案 0 :(得分:10)
Delphi的UnicodeString
使用UTF-16编码。 UTF-16是一种可变长度编码,就像UTF-8一样。换句话说,单个Unicode代码点可能需要多个字符元素来对其进行编码。作为一个兴趣点,唯一的固定长度Unicode编码是UTF-32。 UTF-16编码使用16位字符元素,因此名称为。
在Unicode Delphi中,Char
是WideChar
的别名,它是UTF-16字符元素。 string
是UnicodeString
的别名,是WideChar
元素的数组。 Length()
函数返回数组中元素的数量。
因此,SizeOf(Char)
始终为UnicodeString
。某些Unicode代码点使用多个字符元素或Char
进行编码。但是Length()
会返回字符元素的数量,不会返回代码点的数量。字符元素都具有相同的大小。所以
memorystream1.WriteBuffer(Pointer(rawHtml)^, Length(rawHtml)* SizeOf(Char));
是对的。
答案 1 :(得分:7)
我对Delphi的UnicodeString类型的理解是它是UTF-16 内部。
你对Delphi的UnicodeString
的UTF-16编码是正确的。这意味着一个16位字符的宽度足以表示code points中的所有Basic Multilingual Plane与Char
数组的string
个元素完全相同。
但我对Unicode的一般理解并非全部 unicode字符甚至可以表示为2个字节,即一些 角落外壳字符将占用4个字节。
但是,你在这里有一点误解。 Length
函数不会对字符执行任何深入检查,只返回16位WideChar
元素的数量,而不考虑字符串中的任何代理项。这意味着如果您将Supplementary Planes中的任何一个字符分配给UnicodeString
,Length
将返回2.
program Egyptian;
{$APPTYPE CONSOLE}
var
S: UnicodeString;
begin
S := #$1304E; // single char
Writeln(Length(S));
Readln;
end.
结论:字符串数据的字节大小始终是固定的,等于Length(S) * SizeOf(Char)
,无论S
是否包含任何可变长度的字符。
答案 2 :(得分:4)
其他人已经解释了如何编码UnicodeString以及如何计算其字节长度。我只想提一下RTL已经有了这样的功能 - SysUtils.ByteLength()
:
memorystream1.WriteBuffer(PChar(rawHtml)^, ByteLength(rawHtml));
答案 3 :(得分:3)
你正在做的是正确的(使用sizeof(Char))。
您所指的是,不是一个字符指的是一个代码点(例如,由于代理对)。但是字符串中的USC2编码(非UTF-16)字符恰好占用了Length( Str ) * sizeof( Char )
的字节数。
请注意,Delphi中使用的Unicode编码与.... W变体中的所有Windows API调用相同。