Delphi中大字符串的安全连接

kwa*_*ens 4 delphi string concat substring concatenation

我在相当大的字符串上执行操作 - 我搜索给定短语的出现,并做各种工作,我们称之为“数据库”(我准备一个包含数据的文件以在 R 中进一步处理)正确使用两个过程/函数:Pos 和字符串替换。它们中的大多数大小约为 20-30 mb,有时更大。

此处的文档中- 我知道所有字符串都声明为“字符串”,例如:

my_string : String;
Run Code Online (Sandbox Code Playgroud)

是“注意:在 RAD Studio 中,字符串是 UnicodeString 的别名”。这意味着我不应该担心它们的大小或内存分配,因为 RAD 会自动完成。当然,在这个阶段我可以问一个问题 -你认为声明的选择对编译器很重要并影响 strings 的行为,因为它们在技术上是相同的吗?

my_string1 : String;
my_string2 : AnsiString;
my_string3 : UnicodeString;
Run Code Online (Sandbox Code Playgroud)

它对大小和分配、长度等有一定意义(我们谈论的是超过 20 MB 的丁字裤)?

现在最重要的问题 -如何安全地将两个大字符串相互组合?内存泄漏和字符串内容安全,程序速度安全等。这里有两个选项:

> var string1, string2: String;
> ...
> string1 := string1 + string2;
Run Code Online (Sandbox Code Playgroud)

此处此处的文档表明这是在 Delphi 中连接字符串的方法。但是还有另一种方法 - 我可以提前设置一个非常大的字符串大小,并使用移动过程移动第二个内容。

const string_size: Integer = 1024*1024;
var string1, string2: String;
    concat_place: Integer = 1;
...
SetLength(string1, string_size);
Move(string2[1],string1[concat_place],Length(string2));
Inc(concat_place,Length(string2));
Run Code Online (Sandbox Code Playgroud)

这似乎更安全,因为这个字符串在内存中的区域(大小)不会动态变化,我只是将适当的值移动到它。这是一个更好的主意吗?或者他们甚至更好?也许我不明白什么?

和奖励问题 - 我使用 Pos 和 AnsiPos 测试了 String 和 AnsiString 搜索。它们似乎在所有组合中都一样。这是否意味着它们现在在 Delphi 中是相同的?

预先感谢您提供的所有提示。

And*_*and 14

在 Delphi 中,字符串一直由编译器管理。

在实践中,这意味着程序员根本不需要担心他们的内存分配或生命周期,并且不会有(意外的)内存泄漏。字符串与普通整数一样容易和安全(除非你开始做非常奇怪的事情)。

在幕后,字符串变量是指向字符串数据结构的指针,并且字符串是引用计数并使用写时复制语义。尽管您很可能不需要详细信息,但它们已记录在案

在 Delphi 2009 之前,字符串不是 Unicode:它们每个字符使用一个字节,因此只有 255 个非空字符可用,由当前代码页决定。这些都是艰难的时期。

在 Delphi 2009 及更高版本中,字符串是 Unicode 字符串,每个字符有两个字节。因此,现在可以毫不费力地对诸如“?? + ?sin²x dx”之类的字符串进行编码,而且您无需担心代码页。

您暗示您相信以下声明是相同的:

MyString1: string;
MyString2: AnsiString;
MyString3: UnicodeString;
Run Code Online (Sandbox Code Playgroud)

那么,在2009年德尔福,UnicodeString并且string是相同的:他们是Unicode字符串每个字符两个字节。但是,AnsiString是旧的(旧的,2009 年之前的)字符串类型,它每个字符使用一个字节(最多 255 个非空字符)并取决于代码页。尝试将 "?? + ?sin²x dx" 存储在AnsiString!

现在最重要的问题 - 如何安全地将两个大字符串相互组合?内存泄漏和字符串内容安全,程序速度安全等。

要在 Delphi 中组合两个字符串,您几乎总是使用+运算符:MyString1 + MyString2。这在正确性、内存管理等方面是 100% 安全的。不会有任何内存泄漏。在 Delphi 中连接字符串就是这么简单。

但是,就速度而言,在某些情况下您可能会对此有所改进。该+操作将导致编译器用于制作新的内部字符串数据结构和复制内容创建代码MyString1,并MyString2以新的领域。

因此,例如,如果您想通过连接许多较小的字符串(甚至单个字符)来构建一个大字符串,您可能会通过不使用连续+操作而获得(很多)性能,而是在开始时分配足够大的结果字符串(使用SetLength和字符计数)并手动将字符/字符串复制到其中(例如,使用Move字节计数)。

请注意,我强调了byte一词:您的示例,

Move(string2[1], string1[concat_place], Length(string2));
Run Code Online (Sandbox Code Playgroud)

可能不会做你所期望的。由于字符串被声明为string,在 Delphi 2009 及更高版本中,它们是 Unicode 字符串,因此每个字符有两个字节。所以你需要复制2*Length(string2)字节。为了安全起见,我会写

Move(string2[1], string1[concat_place], sizeof(char) * Length(string2));
Run Code Online (Sandbox Code Playgroud)

假设字符串被声明为string. 在 Delphi 2009 之前,sizeof(char)1; 在 Delphi 2009 及更高版本中,sizeof(char)2.

作为一个简单的基准,我试过

function GetChar: char;
begin
  Result := Char(1 + Random(1000));
end;

const
  N = 100000000;

function MakeString1: string;
var
  i: Integer;
begin
  Result := '';
  for i := 1 to N do
    Result := Result + GetChar;
end;

function MakeString2: string;
var
  i: Integer;
begin
  SetLength(Result, N);
  for i := 1 to N do
    Result[i] := GetChar;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  f, c1, c2: Int64;
  dur1, dur2: Double;
  s1, s2: string;
begin

  QueryPerformanceFrequency(f);

  QueryPerformanceCounter(c1);
  s1 := MakeString1;
  QueryPerformanceCounter(c2);
  dur1 := (c2 - c1) / f;

  QueryPerformanceCounter(c1);
  s2 := MakeString2;
  QueryPerformanceCounter(c2);
  dur2 := (c2 - c1) / f;

  ShowMessage(dur1.ToString + sLineBreak + dur2.ToString);

end;
Run Code Online (Sandbox Code Playgroud)

在我的系统上,MakeString1在 5 秒和MakeString21秒内完成。