Delphi XE - RawByteString与AnsiString的区别

7
我有一个类似的问题,链接在这里:Delphi XE - should I use String or AnsiString?。在决定在我的(大型)库中使用ANSI字符串后,我意识到我实际上可以使用RawByteString而不是ANSI。因为我将UNICODE字符串与ANSI字符串混合使用,所以我的代码现在有很少的地方需要在它们之间进行转换。然而,如果我使用RawByteString,看起来我可以摆脱这些转换。

请让我知道您对此的看法。
谢谢。


更新:
这似乎令人失望。看起来编译器仍然会从RawByteString转换为字符串。

procedure TForm1.FormCreate(Sender: TObject);
var x1, x2: RawByteString;
    s: string;
begin
  x1:= 'a';
  x2:= 'b';
  x1:= x1+ x2;
  s:= x1;              {      <------- Implicit string cast from 'RawByteString' to 'string'     }
end;

我认为它会进行一些内部操作(例如复制数据),我的代码不会更快,我仍然需要在代码中添加许多类型转换才能使编译器静默。


2
任何转换都会调用Windows API(例如System.@UStrFromPCharLen将调用MultiByteToWideCharAnsiString转换为UnicodeString)。这会使您的代码变慢。甚至在RTL的某些部分中存在一些不必要的转换(请参见IntToStr()在Delphi 2010下至少是非常缓慢的)。 - Arnaud Bouchez
2个回答

13

RawByteString是一个默认没有设置代码页的AnsiString

当你将另一个string赋值给这个RawByteString变量时,你会复制源string的代码页。这将包括一次转换。抱歉。

但是还有另一种使用RawByteString的方法,就是存储纯字节内容(例如数据库BLOB字段内容,就像一个array of byte

总结一下:

  • RawByteString应该作为“代码页不可知”的方法或函数的参数;
  • RawByteString可以用作存储一些BLOB数据的变量类型。

如果你想减少转换,并且更愿意在你的应用程序中使用8位字符string,你应该:

  • 不要使用通用的AnsiString类型,它将依赖于当前系统代码页,并且你将失去数据;
  • 依赖于UTF-8编码,即一些8位代码页/字符集,当从或转换为UnicodeString时不会丢失任何数据;
  • 不要让编译器显示关于隐式转换的警告:所有转换都应该明确指定;
  • 使用你自己的专用函数集来处理你的UTF-8内容。

这正是我们为我们的框架所做的。我们想在其内核中使用UTF-8,因为:

  • 我们依赖于UTF-8编码的JSON进行数据传输;
  • 内存消耗将更小;
  • 使用的SQLite3引擎将文本作为UTF-8存储在其数据库文件中;
  • 我们希望有一种处理Unicode文本的方式,在所有版本的Delphi(从Delphi 6到XE)中不会丢失数据,并且WideString不是一个选项,因为它非常缓慢,并且你会遇到隐式转换的同样问题。

但是,为了实现最佳速度,我们编写了一些优化的函数来处理我们的自定义字符串类型:

  {{ RawUTF8 is an UTF-8 String stored in an AnsiString
    - use this type instead of System.UTF8String, which behavior changed
     between Delphi 2009 compiler and previous versions: our implementation
     is consistent and compatible with all versions of Delphi compiler
    - mimic Delphi 2009 UTF8String, without the charset conversion overhead
    - all conversion to/from AnsiString or RawUnicode must be explicit }
{$ifdef UNICODE} RawUTF8 = type AnsiString(CP_UTF8); // Codepage for an UTF8string
{$else}          RawUTF8 = type AnsiString; {$endif}

/// our fast RawUTF8 version of Trim(), for Unicode only compiler
// - this Trim() is seldom used, but this RawUTF8 specific version is needed
// by Delphi 2009/2010/XE, to avoid two unnecessary conversions into UnicodeString
function Trim(const S: RawUTF8): RawUTF8;

/// our fast RawUTF8 version of Pos(), for Unicode only compiler
// - this Pos() is seldom used, but this RawUTF8 specific version is needed
// by Delphi 2009/2010/XE, to avoid two unnecessary conversions into UnicodeString
function Pos(const substr, str: RawUTF8): Integer; overload; inline;

我们保留了 RawByteString 类型来处理 BLOB 数据:

{$ifndef UNICODE}
  /// define RawByteString, as it does exist in Delphi 2009/2010/XE
  // - to be used for byte storage into an AnsiString
  // - use this type if you don't want the Delphi compiler not to do any
  // code page conversions when you assign a typed AnsiString to a RawByteString,
  // i.e. a RawUTF8 or a WinAnsiString
  RawByteString = AnsiString;
  /// pointer to a RawByteString
  PRawByteString = ^RawByteString;
{$endif}

/// create a File from a string content
// - uses RawByteString for byte storage, thatever the codepage is
function FileFromString(const Content: RawByteString; const FileName: TFileName;
  FlushOnDisk: boolean=false): boolean;

我们的代码库提供源代码。在这个单元中,为了更快速度,UTF-8相关的函数得到了深度优化,同时提供了pascal和asm版本。我们有时重载默认函数(比如Pos)以避免转换。关于在框架中处理文本的更多信息,请点此查看

最后一句话:

如果你确定你的应用程序只包含7位内容(没有重音字符),可以在程序中使用默认的AnsiString类型。但在这种情况下,最好在uses子句中添加AnsiStrings单元,以获得重载的字符串函数,避免大部分不必要的转换。


谢谢A。我非常确定我只使用标准ASCII字符。实际上,我只使用了整个ASCII字母表的有限部分。使用ANSI字符串的主要/唯一原因是内存需求,因为我在RAM中加载大量“文本”。不幸的是,许多重要的Delphi函数没有ANSI等效或不接受混合字符串(ansi和unicode),因此我的代码中会出现很多隐式/显式转换。 - Gabriel
@Altar 请看一下我在帖子末尾写的 AnsiStrings 单元。你会在这里找到大多数重要的 Delphi 函数。为了确保调用正确的重载函数,您可以使用 AnsiString.FunctionName() 来避免调用 Unicode 版本。并且为了确保,请使用 Alt-F2 并查看生成的代码:即使您不知道汇编语言,请查看所调用的子例程。如果它类似于 call LStrPos(),那就没问题。但是,如果在主函数调用之前有一些 call UStrFromLStr() 或类似的内容,则会出现缓慢的转换。尝试编写自己的版本。 - Arnaud Bouchez

12

RawByteString仍然是一个"AnsiString"。它最好被描述为一个"通用接收器",这意味着它将在赋值时采用源字符串的代码页,而不会强制进行代码页转换。RawByteString旨在仅用作函数参数,以便在调用需要AnsiStrings的实用程序函数时,不会发生具有不同代码页亲和性的AnsiStrings之间的转换。

然而,在上述情况中,您正在将本质上是AnsiString的内容分配给UnicodeString,这将引起转换。必须进行转换,因为RawByteString具有基于8位字符的有效载荷,而字符串(UnicodeString)具有基于16位字符的有效载荷。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接