拥有这样的记录并不罕见:
TAddress = record
Address: string[50];
City : string[20];
State : string[2];
ZIP : string[5];
end;
最好有硬编码的字符串大小,以确保字符串的大小不会超过为数据分配的数据库字段大小。
但是,鉴于ShortString
类型已被弃用,Delphi开发人员正在做什么来“解决”这个问题?将记录字段声明为string
可以完成工作,但不能保护数据不会超出正确的长度。
这里最好的解决方案是什么?
答案 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就个人而言,我建议用对象替换记录,然后你可以做更多的技巧。