【问题标题】:Is possible to declare string type with restricted length that doesn't start from 0/1?是否可以声明不从 0/1 开始的长度受限的字符串类型?
【发布时间】:2011-09-28 19:16:34
【问题描述】:

在 Delphi 中,可以为整数值声明子范围。例如:

type
  myInt = 2..150

这将 myInt 类型的值限制为 2 到 150 之间的值。但是如果我想限制字符串的长度呢?

如果我写:

type 
  myString = string [150]

我声明 mystring 的长度为 150 字节,并将长度限制在 0、1、2 等到 150 之间。但是,例如,如何将长度限制在 2 到 150 之间? 当然,我可以检查字符串的长度并引发异常,但是 Delphi 是否包含一些特定于这种情况的语法,在样式上类似于子范围?

这显然行不通,但我想要类似的东西:

type
  myString = string[2..150] 

如果不可能,那么我可以只检查长度,引发异常等。


尝试此代码:

var
 str1, str2, str3: TRestrictedString;
begin
  str1.Create(2, 5, 'pp');
  str2.Create(2, 5, 'aaaa');
  str3.Create(2, 10, str1 + str2);
  writeln (str3.getstring)
end

或:

var
 str1, str2, str3: TRestrictedString;
begin
  str1.Create(2, 5, 'pp');
  str2.Create(2, 5, 'aaaa');
  str3.Create(2, 10);
  str3.SetString(str1 + str2);
  writeln (str3.getstring)
end

或:

var
 str1, str2, str3: TRestrictedString;
begin
  str1.Create(2, 5, 'pp');
  str2.Create(2, 5, 'aaaa');
  str3.Create(2, 10);
  str3 := str1 + str2;
  writeln(str3.GetString);
end

所有这些都会引发异常。有可能解决这个问题吗?对于字符串上的多个操作,是否需要将函数拆分为更多部分? 在构造函数中,添加minlength < maxlength的检查会更好吗?如果我设置 minlength > maxlength 它会引发异常。

【问题讨论】:

  • 你不能用内置的字符串类型来做到这一点。作为一个疯狂的思想实验,您可以使用记录类型和运算符重载来实现这一点。将这些数据包装在一个属性中并控制该属性的设置会更有意义。
  • 不,这对于 String 类型是不可能的。您不能为字符串设置最小长度,尽管使用ShortString 可以设置最大值。
  • @Ken:但是你会失去一切 Unicode...
  • @Andreas,当然。 :) 这就是为什么我的回答是“不”。 OP 在他的问题中没有提到 Unicode;问题是关于字符串类型的最小和最大长度。
  • @Marcello - 只是为了指出一点 - delphi 字符串的第一个字符是 MyString[1]

标签: delphi delphi-xe2


【解决方案1】:

我愿意

type
  TRestrictedString = record
  strict private type
    TBounds = record
      MinLength,
      MaxLength: integer;
    end;
  strict private
    FStr: string;
  public
    Bounds: TBounds;
    procedure SetString(const AString: string);
    function GetString: string;
    constructor Create(AMinLength, AMaxLength: integer); overload;
    constructor Create(AMinLength, AMaxLength: integer; const AString: string); overload;
    constructor Create(const AString: string); overload;
    class operator Implicit(S: string): TRestrictedString;
    class operator Implicit(S: TRestrictedString): string;
    class operator Equal(const A, B: TRestrictedString): boolean;
    class operator NotEqual(const A, B: TRestrictedString): boolean;
    class operator Add(const A, B: TRestrictedString): TRestrictedString;
  end;

{ TRestrictedString }


constructor TRestrictedString.Create(AMinLength, AMaxLength: integer);
begin
  Bounds.MinLength := AMinLength;
  Bounds.MaxLength := AMaxLength;
  FStr := '';
end;

constructor TRestrictedString.Create(AMinLength, AMaxLength: integer;
  const AString: string);
begin
  Bounds.MinLength := AMinLength;
  Bounds.MaxLength := AMaxLength;
  SetString(AString);
end;

class operator TRestrictedString.Add(const A,
  B: TRestrictedString): TRestrictedString;
begin
  result.Bounds := A.Bounds;
  result.SetString(A.GetString + B.GetString);
end;

constructor TRestrictedString.Create(const AString: string);
begin
  Bounds.MinLength := 0;
  Bounds.MaxLength := MaxInt;
  FStr := AString;
end;

class operator TRestrictedString.Equal(const A, B: TRestrictedString): boolean;
begin
  result := A.GetString = B.GetString;
end;

function TRestrictedString.GetString: string;
begin
  result := FStr;
end;

class operator TRestrictedString.Implicit(S: TRestrictedString): string;
begin
  result := S.GetString;
end;

class operator TRestrictedString.NotEqual(const A,
  B: TRestrictedString): boolean;
begin
  result := A.GetString <> B.GetString;
end;

class operator TRestrictedString.Implicit(S: string): TRestrictedString;
begin
  result.Create(S);
end;

procedure TRestrictedString.SetString(const AString: string);
begin
  with Bounds do
    if (length(AString) < MinLength) or (length(AString) > MaxLength) then
      raise Exception.Create('Invalid length of string.');
  FStr := AString;
end;

现在你可以做很自然的事情了,比如

procedure TForm1.Button1Click(Sender: TObject);
var
  str: TRestrictedString;
begin
  str.Create(5, 10);         // Create a string w/ length 5 to 10 chrs
  str.SetString('Testing!'); // Assign a compatible string
  ShowMessage(str);          // Display the string
end;

你也可以这样做

str.Create(5, 10, 'Testing!');
ShowMessage(str);

你可以像往常一样添加字符串:

var
  s1, s2, s3: TRestrictedString;
begin
  s1.Create(2, 10, 'Hi ');
  s2.Create(2, 10, 'there!');
  s3 := s1 + s2;
  ShowMessage(s3);
end;

甚至

var
  s1, s3: TRestrictedString;
begin
  s1.Create(2, 10, 'Hi ');
  s3 := s1 + 'there!';
  ShowMessage(s3);

当您添加两个TRestrictedStrings,或一个TRestrictedString 和一个string 时,结果将具有与第一个操作数相同的限制。你可以试试

var
  str: TRestrictedString;
begin
  str.Create(5, 10);
  str.SetString('Testing!');
  str := str + '!!';
  ShowMessage(str);

这会起作用,但不是

var
  str: TRestrictedString;
begin
  str.Create(5, 10);
  str.SetString('Testing!');
  str := str + '!!!';
  ShowMessage(str);

请注意分配 stringTRestrictedString 也会分配字符串的“边界”,即 TRestrictedString 将边界设置为 0MaxInt。因此,无论s: TRestrictedString 如何受到限制,分配s := 'some string' 将始终有效。

更新:Chris Rolliston 将此答案作为灵感,创作了一个非常有趣的article

【讨论】:

  • FStr := '' 在构造函数中不满足最小长度约束。注意:已经提出了这个想法(我相信你是独立提出的)我现在要尝试在你的实现中找到漏洞! ;-) 也 +1。
  • InRange 会很好。另外,当我看到 with ..... ;-)
  • @David:至于你的第一句话,你建议我怎么做?用“Lorem ipsum dolor...”填充字符串?
  • +1 表示努力。但我认为它实际上证明了实际的答案是“是的,但不值得这么麻烦。”
  • +1 这个很好的例子展示了 Delphi 记录和重载创建真正灵活的值类型的强大功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-09
  • 2011-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-27
  • 1970-01-01
相关资源
最近更新 更多