变体记录中的短字符串?

发布于 2024-10-27 19:38:05 字数 903 浏览 3 评论 0原文

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

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; 

这可能/值得吗?

I'd like to be able to access sections of a short string as part of a record

Something like

TMyRecord = record
    case Boolean of
        True: 
        (
            EntireString: String[20];
        );
        False
        (
            StringStart: String[8];
            StringMiddle: String[4];
            StringEnd: String[8];
        );
end;

Is this possible or would I have to declare each char individually

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; 

Is this possible / worth the effort?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

伴梦长久 2024-11-03 19:38:05

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

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

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

例如:

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

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

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

A Delphi short string contains more than just the string contents. The initial byte in the data structure contains the length of the string. This is why short strings are limited to 255 characters.

So, you can't use short strings in your variant array the way you propose.

What you could do is adapt your second approach based on getter and setter methods to be a bit more readable.

For example:

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

You might consider using a string rather than a char array, but it's a little hard to be 100% sure of that advice without knowing exactly what your underlying problem is.

As a final thought, why not turn the problem around? Store 3 strings: StartString, MiddleString and EndString. Then have a property backed with a getter and setter called EntireString. When you read EntireString it pieces it together from the 3 individual parts, and when you write to it it pulls the individual parts out. I suspect it would be easier that way around.

装纯掩盖桑 2024-11-03 19:38:05

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

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

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

(L = 长度字节)。

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

Your first sample doesn't consider the length byte. The memory layout looks like this:

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

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

(L = length byte).

Depending on your requirements (e.g.: Are the partial strings always 8, 4 and 8 Chars?) I'd try storing the partial strings and make EntireString the property, using System.Copy, StrUtils.LeftStr etc.

锦欢 2024-11-03 19:38:05

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.

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

  • 它有重叠的数据结构
  • 当你分配数组时
  • :当你分配字符串时没有问题:当字符串变得很短时要注意

ShortString has an implied length, so your first example will map the length parts of the substrings on top of the main string.

Your second sample is the way to start, with these notes:

  • properties on records are possible
  • you should think of the length of each sub-string (or is it always a fixed array of 20 characters?)

Edit

It totally depend on the reason you want this, and mixing character arrays and strings will get you into trouble because strings can be shorter than the array length.

Small example:

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.

This class does more or less what you want:

  • it has overlapping data structures
  • when you assign the arrays: no problem
  • when you assign the strings: be aware when strings get to short
烟燃烟灭 2024-11-03 19:38:05

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

您将 C 的 *char 类型与 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 时会发生这种情况),您可以使用短字符串,但使用精确长度的自定义类型输入并使用 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;

As other stated, it won't work, because the variant-sized record will add some lengths for StringStart/StringMiddle/StringEnd in the middle of the EntireString type.

You are confusing the *char type of C with the pascal shortstring type. There is an hidden character at position [0] which is the shortstring length.

You could use regular string type, then split in on purpose:

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;

Note that the out parameter type will set all output String* variables into '' before calling the function.

This version will expect entering entire string of 20 chars long.

You could use shortstrings, but with custom types of the exact length, if you want to avoid hidden copies from/to string[255] (which occur when you use a shortstring type and work with string[n] with 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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文