我希望能够访问短字符串的部分作为记录的一部分
像
这样的东西TMyRecord = record
case Boolean of
True:
(
EntireString: String[20];
);
False
(
StringStart: String[8];
StringMiddle: String[4];
StringEnd: String[8];
);
end;
这是可能的还是我必须单独声明每个字符
TMyRecord = record
private
Chars: Array[1..20] of Char;
Function GetStringStart:String;
Procedure SetStringStart(Value: String);
public
Property StringStart: String read GetStringStart write SetStringStart; // Can I have properties on a record?
end;
Function GetStringStart: String;
begin
Result := Chars[1] + Char[2]....;
end;
Procedure SetStringStart(Value: String);
begin
for i := 1 to 8 do
begin
Chars[i] := Value[i];
end;
end;
这可能/值得努力吗?
答案 0 :(得分:4)
Delphi短字符串不仅包含字符串内容。数据结构中的初始字节包含字符串的长度。这就是短字符串限制为255个字符的原因。
因此,您不能按照提议的方式在变量数组中使用短字符串。
你可以做的是根据getter和setter方法调整你的第二种方法,使其更具可读性。
例如:
function TMyRecord.GetStringStart: string;
begin
SetString(Result, @Chars[1], 8);
end;
您可能会考虑使用字符串而不是char数组,但如果不确切知道您的基本问题是什么,就很难100%确定该建议。
最后想一想,为什么不解决这个问题呢?存储3个字符串:StartString
,MiddleString
和EndString
。然后使用名为EntireString
的getter和setter支持一个属性。当您阅读EntireString
时,它会将它从3个单独的部分拼凑在一起,当您写入时,它会拉出各个部分。我怀疑这样会更容易。
答案 1 :(得分:4)
您的第一个示例不考虑长度字节。内存布局如下所示:
case True:
L12345678901234567890
^....................
case False:
L12345678L1234L12345678
^........^....^........
(L =长度字节)。
根据您的要求(例如:部分字符串总是 8,4和8个字符吗?)我会尝试使用系统存储部分字符串并使EntireString
成为属性.Copy,StrUtils.LeftStr等。
答案 2 :(得分:3)
ShortString
有一个隐含的长度,所以你的第一个例子会将子串的长度部分映射到主字符串的顶部。
您的第二个样本是开始的方式,请注意以下几点:
修改的
这完全取决于你想要的原因,混合字符数组和字符串会让你遇到麻烦,因为字符串可能比数组长度短。
小例子:
program VariantRecordsWithCharactersAndStrings;
{$APPTYPE CONSOLE}
uses
SysUtils,
Math;
const
Size20 = 20;
Size8 = 8;
Size4 = 4;
type
TChar20 = array[0..Size20-1] of Char;
TChar8 = array[0..Size8-1] of Char;
TChar4 = array[0..Size4-1] of Char;
TMyRecord = record
class var FillCharValue: Byte;
function GetEntireString: string;
function GetStringStart: string;
function GetStringMiddle: string;
function GetStringEnd: string;
procedure SetEntireString(const Value: string);
procedure SetStringStart(const Value: string);
procedure SetStringMiddle(const Value: string);
procedure SetStringEnd(const Value: string);
property EntireString: string read GetEntireString write SetEntireString;
property StringStart: string read GetStringStart write SetStringStart;
property StringMiddle: string read GetStringMiddle write SetStringMiddle;
property StringEnd: string read GetStringEnd write SetStringEnd;
procedure SetCharArray(const CharArrayPointer: PChar; const CharArraySize: Integer; const Value: string);
case Boolean of
True:
(
CharFull: TChar20;
);
False:
(
CharStart: TChar8;
CharMiddle: TChar4;
CharEnd: TChar8;
);
end;
function TMyRecord.GetEntireString: string;
begin
Result := CharFull;
end;
function TMyRecord.GetStringStart: string;
begin
Result := CharStart;
end;
function TMyRecord.GetStringMiddle: string;
begin
Result := CharMiddle;
end;
function TMyRecord.GetStringEnd: string;
begin
Result := CharEnd;
end;
procedure TMyRecord.SetEntireString(const Value: string);
begin
SetCharArray(CharFull, SizeOf(CharFull), Value);
end;
procedure TMyRecord.SetCharArray(const CharArrayPointer: PChar; const CharArraySize: Integer; const Value: string);
begin
FillChar(CharArrayPointer^, CharArraySize, FillCharValue);
Move(Value[1], CharArrayPointer^, Min(CharArraySize, SizeOf(Char)*Length(Value)));
end;
procedure TMyRecord.SetStringStart(const Value: string);
begin
SetCharArray(CharStart, SizeOf(CharStart), Value);
end;
procedure TMyRecord.SetStringMiddle(const Value: string);
begin
SetCharArray(CharMiddle, SizeOf(CharMiddle), Value);
end;
procedure TMyRecord.SetStringEnd(const Value: string);
begin
SetCharArray(CharEnd, SizeOf(CharEnd), Value);
end;
var
MyRecord: TMyRecord;
procedure Dump();
begin
Writeln(MyRecord.EntireString);
Writeln(MyRecord.StringStart);
Writeln(MyRecord.StringMiddle);
Writeln(MyRecord.StringEnd);
end;
procedure TestWithFillCharValue(const FillCharValue: Byte);
begin
Writeln('Testing with FillCharValue ', FillCharValue);
TMyRecord.FillCharValue := FillCharValue;
MyRecord.EntireString := '123456789001234567890';
Dump();
MyRecord.StringStart := 'AAA';
MyRecord.StringMiddle := 'BBB';
MyRecord.StringEnd := 'CCC';
Dump();
end;
begin
try
TestWithFillCharValue(0); // this will truncated all the sub arrays when you pass strings that are too short
TestWithFillCharValue(20); // when using Unicode, this fails even more horribly
Write('Press <Enter>');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
这个课程或多或少地符合你的要求:
答案 3 :(得分:1)
正如其他所述,它不起作用,因为变体大小的记录会在StringStart/StringMiddle/StringEnd
类型的中间为EntireString
添加一些长度。
您将*char
类型的C与pascal shortstring
类型混淆。位置[0]
有一个隐藏的字符,即shortstring
长度。
您可以使用常规字符串类型,然后故意拆分:
procedure StringSplit(const EntireString: string; out StringStart, StringMiddle, StringEnd: string);
begin
if length(EntireString)<>20 then
exit;
StringStart := copy(EntireString,1,8);
StringMiddle := copy(EntireString,9,4);
StringEnd := copy(EntireString,13,8);
end;
请注意,out
参数类型会在调用函数之前将所有输出String *变量设置为''。
此版本预计会输入20个字符长的整个字符串。
你可以使用短串,但是如果要避免隐藏副本来自/ string[255]
(当你使用shortstring
类型并使用{{}时),可以使用精确长度的自定义类型1}} n&lt; 255):
string[n]