13

我正在将一些 Delphi 7 代码移植到 XE4,因此,unicode 是这里的主题。

我有一个将字符串写入 TMemoryStream 的方法,因此根据这篇 embarcadero 文章,我应该将字符串的长度(以字符为单位)乘以 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 的一般理解是,并非所有 Unicode 字符都可以用 2 个字节表示,一些极端情况的外来字符将占用 4 个字节。embarcadero 的另一篇文章似乎证实了我的怀疑,“事实上,一个 Char 等于两个字节甚至并不总是正确的!”

所以......这让我想知道是否Length(rawHtml)* SizeOf(Char)真的足够强大以保持一致准确,或者是否有更好的方法来确定更准确的字符串大小?

4

4 回答 4

11

DelphiUnicodeString是用 UTF-16 编码的。UTF-16 是一种可变长度编码,就像 UTF-8 一样。换句话说,单个 Unicode 代码点可能需要多个字符元素对其进行编码。有趣的是,唯一固定长度的 Unicode 编码是 UTF-32。UTF-16 编码使用 16 位字符元素,因此得名。

在 Unicode Delphi 中,是UTF-16 字符元素Char的别名。WideCharAndstring是 的别名UnicodeString,它是一个WideChar元素数组。该Length()函数返回数组中元素的数量。

因此,SizeOf(Char)始终为 2 UnicodeString。一些 Unicode 代码点使用多个字符元素或Chars 进行编码。但Length()返回字符元素的数量而不是代码点的数量。字符元素都具有相同的大小。所以

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

是正确的。

于 2013-05-13T20:09:06.623 回答
8

我对 Delphi 的 UnicodeString 类型的理解是它内部是 UTF-16。

您对 Delphi 的 UTF-16 编码是正确的UnicodeString。这意味着一个 16 位字符的宽度足以将基本多语言平面中的所有代码点表示为数组的一个元素。Charstring

但我对 Unicode 的一般理解是,并非所有 Unicode 字符都可以用 2 个字节表示,一些极端情况的外来字符将占用 4 个字节。

但是,您在这里有一点误解。Length函数不对字符进行任何深入检查,只返回 16 位WideChar元素的数量,而不考虑字符串中的任何代理项。这意味着如果您将任何补充平面中的单个字符分配给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包含任何变长字符。

于 2013-05-13T21:41:29.543 回答
6

其他人已经解释了 UnicodeString 是如何编码的,以及如何计算它的字节长度。我只想提一下,RTL 已经有了这样的功能—— SysUtils.ByteLength()

memorystream1.WriteBuffer(PChar(rawHtml)^, ByteLength(rawHtml));
于 2013-05-14T01:25:10.547 回答
3

您所做的是正确的(使用 sizeof(Char))。

您指的是没有一个字符指代一个代码点(例如,由于代理对)。但是字符串中的 USC2 编码(不是 UTF-16)字符完全占用了Length( Str ) * sizeof( Char ).

请注意,Delphi 中使用的 Unicode 编码与 ....W 变体中的所有 Windows API 调用相同。

于 2013-05-13T20:44:16.343 回答