Delphi 7迁移至RAD Studio 10.3时遇“Read beyond end of file”错误
问题背景
我有一个用Delphi 7开发的旧程序,需要迁移到RAD Studio 10.3。旧程序通过定义带固定长度字符串的记录直接读写文件,核心代码如下:
TYPE TPanel = RECORD Name : String[40]; Width : Integer; Height : Integer; TypeID : String[20]; TypeName : String[40]; Price : Extended; END; TWorktop = RECORD Width : Integer; Height : Integer; TypeID : String[20]; TypeName : String[40]; Price : Extended; End; TData = RECORD ID: String[20]; IDName : String[30]; OrderName: String[30]; OrderAdr : String[30]; Quantity : Integer; CselcsicsAzon : String[30]; dateCreate : TDate; ProjectChanged: Boolean; PanelCount : Integer; Panels : Array[1..orderMaxPanel+1] Of TPanel; PanelCount : Integer; Worktops : Array[1..orderMaxWorktop+1] Of TWorktop; WorktopCount : Integer; Price : Extended; END; Var Data: TData; F: File Of TData; Begin AssignFile(F,Filename); Reset(F); Read(F,Data); CloseFile(F); End;
在Delphi 7中读写完全正常,但迁移到RAD Studio 10.3后,读取文件时出现Read beyond end of file错误。一开始怀疑是String类型的差异(Delphi 7默认是AnsiString,RAD Studio 10.3默认是UnicodeString),但尝试用AnsiString[40]时发现语法不支持,陷入困境。
排查过程
测试中发现记录的内存布局存在版本差异:
比如下面的记录在Delphi 7和RAD Studio 10.3中占用的内存大小不一致:
TTest = RECORD rhLeft, rhTop, rhRight, rhBottom : Extended; end;
将字段拆分为单独一行定义后,记录大小一致了,但Delphi 7却无法读取新生成的文件。
最终解决方案
使用packed record(压缩记录)!
原理说明
Delphi默认会对记录的字段进行内存对齐优化,不同版本的编译器对齐规则可能存在差异,这会导致同一记录类型在不同版本中占用的内存大小、字段偏移量不一致,直接按记录读写文件时就会出现位置错位,最终触发“读取超出文件末尾”的错误。
添加packed关键字可以强制编译器取消内存对齐,让记录的内存布局在所有Delphi版本中保持完全一致,从而实现旧文件的跨版本兼容读写。
修改后的代码示例
只需在每个记录定义前加上PACKED关键字即可:
TYPE TPanel = PACKED RECORD Name : String[40]; Width : Integer; Height : Integer; TypeID : String[20]; TypeName : String[40]; Price : Extended; END; TWorktop = PACKED RECORD Width : Integer; Height : Integer; TypeID : String[20]; TypeName : String[40]; Price : Extended; End; TData = PACKED RECORD ID: String[20]; IDName : String[30]; OrderName: String[30]; OrderAdr : String[30]; Quantity : Integer; CselcsicsAzon : String[30]; dateCreate : TDate; ProjectChanged: Boolean; PanelCount : Integer; Panels : Array[1..orderMaxPanel+1] Of TPanel; PanelCount : Integer; Worktops : Array[1..orderMaxWorktop+1] Of TWorktop; WorktopCount : Integer; Price : Extended; END;
补充说明
关于字符串类型:Delphi 7中的String[N]是固定长度的Ansi字符串,在RAD Studio 10.3中这个语法是向下兼容的,编译器会自动将其处理为固定长度的AnsiString,不需要强行修改为AnsiString[N](该语法本身也不被支持),只要保证记录是packed的即可兼容旧文件。
内容的提问来源于stack exchange,提问作者KissGe

