鉴于不推荐使用ShortString,限制字符串长度的最佳方法是什么

时间:2012-12-17 16:49:57

标签: string delphi field

拥有这样的记录并不罕见:

TAddress = record
  Address: string[50];
  City   : string[20];
  State  : string[2];
  ZIP    : string[5];
end;

最好有硬编码的字符串大小,以确保字符串的大小不会超过为数据分配的数据库字段大小。

但是,鉴于ShortString类型已被弃用,Delphi开发人员正在做什么来“解决”这个问题?将记录字段声明为string可以完成工作,但不能保护数据不会超出正确的长度。

这里最好的解决方案是什么?

6 个答案:

答案 0 :(得分:6)

问题中的短字符串类型并不能真正保护字符串不超过正确的长度。为这些短字符串分配较长的值时,会以静默方式截断该值。

我不确定您使用的是什么数据库访问方法,但我更想象它会做同样的事情。即将任何超长字符串截断为最大长度。在这种情况下,无所事事。

如果数据库访问方法在为其提供超长字符串时抛出错误,则需要在将值传递给数据库之前进行截断。

如果必须明确截断,那么有很多地方可以选择这样做。我的理念是在最后可能的时刻截断。这就是你受限制的点。截断任何其他地方似乎是错误的。这意味着数据库限制正在扩散到与数据库无明显关系的部分代码中。

当然,所有这些都是基于您希望继续进行静默截断的假设。如果您想在截断的情况下提供用户反馈,那么您需要确定反馈的正确行动点。

答案 1 :(得分:5)

如果我不得不保持数据超出正确的长度,我会让数据库代码尽可能地处理它。对字段设置大小限制,并在数据绑定控件中向用户显示数据。绑定到字符串字段的TDBEdit将正确强制执行长度限制。设置它以便直接从数据集中填充记录,并且它将始终具有正确的长度。

然后,您需要担心的是,数据是从某些不属于您的UI的外部源进入记录的。为此,使用相同的过程。让导入代码将数据插入到数据集中,并让其长度限制为您进行验证。如果它引发异常,则拒绝导入。如果没有,那么您就拥有了一个有效的数据集行,您可以使用该行来填充记录。

答案 2 :(得分:4)

如果您只是从Delphi 7移植到XE3,请保留它。另外,虽然“ShortString”可能会被弃用,但如果它们完全删除它,我会吃掉我的帽子,因为有很多代码在没有它的情况下永远无法重建。 ShortString + Records仍然是指定面向字节的文件记录数据存储的唯一实用方法。 Delphi永远不会删除ShortString,也不会改变它的行为,这对现有的delphi代码来说是毁灭性的。因此,如果你真的必须定义记录并限制它们的长度,并且你真的不希望这些记录支持Unicode,那么就没有理由停止使用或停止编写ShortString代码。话虽这么说,我讨厌短串,记录文件,希望他们会离开,很高兴他们被标记为弃用。

话虽如此,我完全赞同梅森和大卫;我会说,长度检查和验证都是表示/验证问题,Delphi的强类型不是正确的地方或正确的处理方式。如果需要在类上放置验证约束,请编写实现约束存储的辅助类(EmployeeName是字符串字段,EmployeeName具有以下长度限制)。例如,在Edit控件中,这已经是一个属性。在我看来,使用新的Binding系统将DB Fields映射到可视字段比尝试在代码中静态表达约束要好得多。

用户输入验证和存储不同,应在GUI控件中设置长度限制,而不是在数据结构中。

例如,如果您想要一个Unicode宽但长度有限的字符串,可以使用Array of UnicodeChar。您甚至可以使用Delphi中的新类助手方法编写自己的LimitedString类。但这种方法并不是一种可维护和稳定的设计。

如果您的SQL数据库有一个用VARCHAR(100)类型声明的字段,并且您希望将用户的输入限制为100个字符,那么您应该在GUI层执行此操作并忘记强制截断(数据损坏,实际上默默地在幕后。

答案 3 :(得分:3)

根据我的理解,我的回答应该是“不要混合图层”。

我怀疑字符串长度是在数据库层级别(列宽)指定的,或者是在业务应用程序层指定的(例如,用于验证卡号)。

从“纯Delphi代码”的角度来看,您不应该知道您的string变量具有最大长度,除非您到达持久层甚至业务层。

使用属性可能是一个想法。但它可能“污染”源代码的原因与混合层的原因完全相同。

所以我建议使用专用的数据建模,在其中指定数据期望。然后,在Delphi var级别,您只需定义一个普通的string。这正是我们的 mORMot 框架implements data filtering and validation:在模型级别,有一些专用类 - 方便,可扩展和干净。

答案 4 :(得分:1)

我遇到了这个问题 - 严重的是 - 从Delphi 6升级到2009年,因为一个程序正在/正在做什么,因此必须能够将旧的ASCII字符串视为单独的ASCII字符。

程序输出ASCII文件(甚至不是ANSI)并且具有诸如在最后一个数字上过冲以表示否定的概念。所以文件格式可以说有点可以说!

在2009年第一次构建之后(10年的代码,好吧,你不是吗!)在对单位名称等进行排序后,确实有数百个报告错误/非法指派和数据丢失/转换警告... < / p>

无论Delphi的后台操作/魔法与弦乐和角色有多好,我都不太信任它。最后要确保所有内容都恢复原状,我将它们全部重新声明为byte数组,然后相应地更改代码。

答案 5 :(得分:1)

你还没有指定delphi版本,这是在delphi 2010中对我有用的东西:

<强>版本1

TTestRecordProp = record
private
  FField20: string;
  ...
  FFieldN: string
  procedure SetField20(const Value: string);
public
  property Field20: string read FField20 write SetField20;
  ...
  property FieldN: string ...
end;
...
procedure TTestRecordProp.SetField20(const Value: string);
begin
  if Length(Value) > 20 then
    /// maybe raise an exception?
    FField20 := Copy(FField20, 1, 20)
  else
    FField20 := Value;
end;

<强>版本2

TTestRecordEnsureLengths = record
  Field20: string;
  procedure EnsureLengths;
end;
...
procedure TTestRecordEnsureLengths.EnsureLengths;
begin
  // for each string field, test it's length and truncate or raise exception
  if Length(Field20) > 20 then
    Field20 := Copy(Field20, 1, 20); // or raise exception...
end;

//在将数据推送到db ...

之前,你必须调用.EnsureLength

就个人而言,我建议用对象替换记录,然后你可以做更多的技巧。