2

我希望能够访问短字符串的部分作为记录的一部分

就像是

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; 

这可能/值得努力吗?

4

4 回答 4

4

Delphi 短字符串不仅仅包含字符串内容。数据结构中的初始字节包含字符串的长度。这就是为什么短字符串限制为 255 个字符的原因。

因此,您不能按照您建议的方式在变体数组中使用短字符串。

您可以做的是根据 getter 和 setter 方法调整您的第二种方法,使其更具可读性。

例如:

function TMyRecord.GetStringStart: string;
begin
  SetString(Result, @Chars[1], 8);
end;

您可能会考虑使用字符串而不是 char 数组,但是在不确切知道您的潜在问题是什么的情况下,很难 100% 确定该建议。

作为最后的想法,为什么不扭转这个问题呢?存储 3 个字符串StartStringMiddleStringEndString。然后有一个由 getter 和 setter 支持的属性,称为EntireString. 当您阅读EntireString它时,它将 3 个单独的部分拼凑在一起,而当您对其进行写入时,它会将各个部分拉出。我怀疑这样会更容易。

于 2011-03-31T11:33:31.997 回答
4

您的第一个示例不考虑长度字节。内存布局如下所示:

case True:
L12345678901234567890
^....................

case False:
L12345678L1234L12345678
^........^....^........

(L = 长度字节)。

根据您的要求(例如:部分字符串是否总是8、4和 8 个字符?)我会尝试EntireString使用 System.Copy、StrUtils.LeftStr 等存储部分字符串并创建属性。

于 2011-03-31T11:34:39.483 回答
3

ShortString具有隐含长度,因此您的第一个示例将在主字符串顶部映射子字符串的长度部分。

您的第二个示例是开始的方式,带有以下注释:

  • 记录上的属性是可能的
  • 您应该考虑每个子字符串的长度(或者它是否始终是 20 个字符的固定数组?)

编辑

这完全取决于你想要这个的原因,混合字符数组和字符串会给你带来麻烦,因为字符串可能比数组长度短。

小例子:

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.

这个类或多或少地做了你想要的:

  • 它具有重叠的数据结构
  • 分配数组时:没问题
  • 分配字符串时:注意字符串何时变短
于 2011-03-31T11:34:10.077 回答
1

如前所述,它不起作用,因为变体大小的记录会StringStart/StringMiddle/StringEnd在类型的中间添加一些长度EntireString

您将*charC 的类型与 pascalshortstring类型混淆了。位置有一个隐藏字符,[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类型并string[n]使用 n<255 时会发生这种情况),您可以使用短字符串,但使用精确长度的自定义类型:

type 
  String20 = string[20];
  String4 = string[4];
  String8 = string[8];

procedure StringSplit(const EntireString: String20; out StringStart: String8;
            out StringMiddle: String4; out StringEnd: String8);
begin
  if length(EntireString)<>20 then
    exit;
  StringStart := copy(EntireString,1,8);
  StringMiddle := copy(EntireString,9,4);
  StringEnd := copy(EntireString,13,8);
end;
于 2011-03-31T12:15:47.860 回答