如何将 PChar 的一部分提取到字符串中?
How can I extract part of a PChar into a string?
在分析过程中,我遇到了一个花费相当多时间的函数,但基本上可以归结为这段非常简单的代码:
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
Result := Copy(AInput, AStart, ASubstringLength);
end;
此函数 returns 预期的子字符串,但它不能很好地扩展较长的输入。我在 CPU 视图中查看了汇编程序代码,据我所知(我通常不在汇编程序级别工作),似乎 AInput
在调用之前隐式转换为字符串 Copy
.
但由于此时 string/character 数组的长度未知,转换代码必须遍历 PChar
的长度,直到找到空终止符。这可以解释较长输入的可怕缩放。
但是,由于调用者传入了 PChar
的长度,我最初认为可以将方法转换为使用 SetString
。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
SetString(Result, AInput + AStart - 1, ASubstringLength);
end;
除了 SetString
从零开始工作(而不是像复制那样从一开始),Copy
在验证其输入方面似乎还有一些其他的小事情,而不是所有这些都记录在案(例如,任何小于 1 的起始值都更改为 1)。所以上面的简单实现并不总是像原来的那样工作。
我的目标是尽可能多地复制 Copy
例程,因为此函数是库的一部分并且已被我的同事广泛使用。
我想知道以下实现是否实现了这一点,或者我是否需要了解 Copy
的任何其他注意事项。注意:FLength
是 AInput
的实际长度,它来自此函数所属模块的另一部分。我删除了这个例子的其他部分。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
if (AInput = nil) then begin
Result := '';
end else begin
if (AStart < 1) then begin
AStart := 0;
end else begin
AStart := AStart - 1;
end;
if (ASubstringLength + AStart > FLength) then begin
ASubstringLength := FLength - AStart;
end;
SetString(Result, AInput + AStart, ASubstringLength);
end;
end;
我使用的是 Delphi 2006,但我认为这与该产品的其他版本(至少是非 Unicode 版本)没有太大区别。
让我们考虑极端情况。我认为他们是:
AInput
无效。
AStart < 1
.
AStart > FLength
.
ASubstringLength < 0
.
ASubstringLength + (AStart-1) > FLength
.
我认为我们可以忽略案例1。调用者有责任提供有效的 PChar
。事实上,你检查 AInput <> nil
在我看来已经太过分了,因为 nil
不是有效的 PChar
.
其余的你已经涵盖了 2 和 5,但没有涵盖 3 和 4。因此,如果用户提供的 AStart
值太大,那么你将读取字符串的末尾。同样,用户可以很容易地提供否定 ASubstringLength
。我认为您不需要任何人编写代码来检查这些情况,因为您显然非常能干。
现在,如果您真的关心每一滴性能,您不应该检查任何这些情况。要求用户传递有效参数。在调试模式下,您可以使用 {$IFOPF D+}
或 Assert
检查输入。当然,如果这些论据来自外部来源,那么它们应该被验证。
另一方面,原始代码遭受的最大性能损失是对整个字符串的不必要扫描,以及复制到中间堆分配的字符串。一旦你删除了那些,那么进一步提高性能的机会就会大大减少。
而不是将 PChar
转换为 string
,您应该尝试将长度为 ASubstringLength * SizeOf(PChar)
的内存从地址 AInput + (AStart * SizeOf(PChar))
复制到 @Result
作为指针处理 Result
更容易。
Move
程序可以做到这一点。
在分析过程中,我遇到了一个花费相当多时间的函数,但基本上可以归结为这段非常简单的代码:
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
Result := Copy(AInput, AStart, ASubstringLength);
end;
此函数 returns 预期的子字符串,但它不能很好地扩展较长的输入。我在 CPU 视图中查看了汇编程序代码,据我所知(我通常不在汇编程序级别工作),似乎 AInput
在调用之前隐式转换为字符串 Copy
.
但由于此时 string/character 数组的长度未知,转换代码必须遍历 PChar
的长度,直到找到空终止符。这可以解释较长输入的可怕缩放。
但是,由于调用者传入了 PChar
的长度,我最初认为可以将方法转换为使用 SetString
。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
SetString(Result, AInput + AStart - 1, ASubstringLength);
end;
除了 SetString
从零开始工作(而不是像复制那样从一开始),Copy
在验证其输入方面似乎还有一些其他的小事情,而不是所有这些都记录在案(例如,任何小于 1 的起始值都更改为 1)。所以上面的简单实现并不总是像原来的那样工作。
我的目标是尽可能多地复制 Copy
例程,因为此函数是库的一部分并且已被我的同事广泛使用。
我想知道以下实现是否实现了这一点,或者我是否需要了解 Copy
的任何其他注意事项。注意:FLength
是 AInput
的实际长度,它来自此函数所属模块的另一部分。我删除了这个例子的其他部分。
function GetSubstring(AInput: PChar; AStart, ASubstringLength: Integer): string;
begin
if (AInput = nil) then begin
Result := '';
end else begin
if (AStart < 1) then begin
AStart := 0;
end else begin
AStart := AStart - 1;
end;
if (ASubstringLength + AStart > FLength) then begin
ASubstringLength := FLength - AStart;
end;
SetString(Result, AInput + AStart, ASubstringLength);
end;
end;
我使用的是 Delphi 2006,但我认为这与该产品的其他版本(至少是非 Unicode 版本)没有太大区别。
让我们考虑极端情况。我认为他们是:
AInput
无效。AStart < 1
.AStart > FLength
.ASubstringLength < 0
.ASubstringLength + (AStart-1) > FLength
.
我认为我们可以忽略案例1。调用者有责任提供有效的 PChar
。事实上,你检查 AInput <> nil
在我看来已经太过分了,因为 nil
不是有效的 PChar
.
其余的你已经涵盖了 2 和 5,但没有涵盖 3 和 4。因此,如果用户提供的 AStart
值太大,那么你将读取字符串的末尾。同样,用户可以很容易地提供否定 ASubstringLength
。我认为您不需要任何人编写代码来检查这些情况,因为您显然非常能干。
现在,如果您真的关心每一滴性能,您不应该检查任何这些情况。要求用户传递有效参数。在调试模式下,您可以使用 {$IFOPF D+}
或 Assert
检查输入。当然,如果这些论据来自外部来源,那么它们应该被验证。
另一方面,原始代码遭受的最大性能损失是对整个字符串的不必要扫描,以及复制到中间堆分配的字符串。一旦你删除了那些,那么进一步提高性能的机会就会大大减少。
而不是将 PChar
转换为 string
,您应该尝试将长度为 ASubstringLength * SizeOf(PChar)
的内存从地址 AInput + (AStart * SizeOf(PChar))
复制到 @Result
作为指针处理 Result
更容易。
Move
程序可以做到这一点。