简介:一份面向VFP初学者的数据库系统开发实例文档,以公共计算机成绩管理系统为主线,完整展示从需求分析、系统设计、项目创建、数据库物理设计到界面实现的全过程。文档基于Visual FoxPro环境,先梳理学生信息、管理员信息、成绩与课程等数据需求,再明确录入、查询、维护、审核、打印等功能需求,继而给出功能模块图、关系模型表结构以及启动表单等关键界面的事件代码,适合高校学生、课程设计及自学VFP的开发者参考。文档压缩包内共有1个doc文件,整包大小约902KB,图文并茂,便于在Word中按章节对照阅读。该文档已有七百余人学习浏览,可作为毕业设计或期末项目的起步范例。读者可借此理解VFP项目管理器的官方推荐用法、数据库规范化建模思路,以及将多个数据表与界面模块集成为可运行应用系统的完整方法,实操参考价值较强。
1. 一份VFP数据库系统开发实例,为什么到今天还值得打开
拿到《VFP数据库系统开发实例(附图)》这类文档时,很多人的第一反应是“Visual FoxPro都退出主流多少年了,还有必要看吗”。真实情况是,数据库课程设计、中小型MIS系统、老单位内部管理工具,到现在仍然存在大量 VFP 存量项目,每年也还有一批新人被要求用 VFP 快速交出一个能跑的表单系统。这份实例的价值不在语言多新,而在于它把“建库 → 建表 → 做界面 → 写增删改查 → 截图验收”的完整链路压缩成了一套可复现的步骤。你不需要先啃完整本手册,只要按着实例把表和表单搭起来,就能在一个晚上交付一个能演示、能打分、能维护的小系统。这篇文章就按同样的路径拆给你看:先讲清楚 VFP 的数据模型和建表逻辑,再落到表单和增删改查代码,最后把最容易翻车的五处细节单独拎出来。
2. VFP做数据库开发的底层逻辑:库、表、索引的关系
2.1 从DBC到DBF:数据库容器与自由表不是一回事
VFP 里最容易被新手混淆的概念是“数据库”和“表”。这里说的数据库不是 MySQL 里那个逻辑库,而是一个叫数据库容器的东西,扩展名是 .DBC。真正的数据存放在 .DBF 表文件里,索引文件是 .CDX。一个 .DBC 可以管理多个 .DBF,它本身不存业务数据,只存数据字典信息:表之间的永久关系、字段默认值、字段级规则、存储过程、本地视图、索引标识。换句话说,DBC 是表的管理层,DBF 是数据存储层。
这个区分会直接影响你的实例文档怎么写。常见的数据库系统开发实例里,第一件事一定是创建数据库容器,再把表加进去。如果只建了几个自由表(不挂在任何 DBC 下的 DBF),也能完成增删改查,但你会丢掉不少重要能力:字段级规则在自由表上不生效,参照完整性做不了,日期和空值校验都得靠程序代码硬写。我在实际项目里一般建议:只要你的系统超过两张表,就用 CREATE DATABASE 建库,别贪图省事用自由表。
* 创建数据库容器:testdata.dbc CREATE DATABASE testdata * 打开数据库容器 OPEN DATABASE testdata * 在打开的数据库容器里创建学生表 student.dbf CREATE TABLE student ; (sno C(10) PRIMARY KEY, ; sname C(8) NOT NULL, ; sex C(2) DEFAULT '男', ; birth D, ; score N(5,1), ; memo M)这段代码可以单独保存成一个 .PRG 程序文件运行,也可以直接在命令窗口逐行执行。CREATE DATABASE 后面不能带分号,数据库名不要用中文和空格;CREATE TABLE 最后的括号里定义字段,PRIMARY KEY 会同时创建主索引,并且这个字段自动变成非空。这里我用了三组参数来说明建表时的常见选型:C(10) 表示宽度为 10 的字符型字段,学号用字符型而不是数值型,因为学号不需要参与加减运算,而且很多学号带前置零;N(5,1) 表示数值型,总长度 5 位,其中小数占 1 位,能存 999.9 以内的成绩;D 是日期型,内部按年月日排序。这些长度在实例文档的设计阶段就要定好,因为后面 ALTER TABLE 改字段长度,会重建表结构,耗时且容易触发文件损坏。
2.2 字段类型与默认值:让建表语句一次写对
VFP 的字段类型比主流关系数据库少,但每个类型都有脾气。字符型 C、数值型 N、浮点型 F、日期型 D、日期时间型 T、逻辑型 L、备注型 M、通用型 G、货币型 Y。课程设计里最常见的组合就是 C、N、D、L、M。很多新手分不清 N 和 F,其实 N 是 VFP 内部用整数保存的十进制数,F 是浮点数;对成绩、数量这种精度要求不高的数据,用 N 就够了。M 备注字段在表里的实际值是一个指针,内容存在同名的 .FPT 文件里,所以当你复制 DBF 文件时,必须连 .FPT 一起复制,否则备注字段就会读取失败。
字段默认值和字段级规则应该在建表时一并设置,而不是等表单写完之后再用代码硬校验。DEFAULT 的作用是插入记录时自动填值,比如性别默认填“男”,出生地默认填“北京”。字段级规则用 CHECK 和 ERROR 定义,CHECK 写条件,ERROR 写违反条件时的提示信息。下面是一个带规则的建表示例:
CREATE TABLE student ; (sno C(10) PRIMARY KEY, ; sname C(8) NOT NULL, ; sex C(2) CHECK (sex $ "男女") ERROR "性别只能填男或女" DEFAULT '男', ; birth D CHECK (EMPTY(birth) OR birth <= DATE()) ERROR "出生日期不能晚于今天", ; score N(5,1) CHECK (score >= 0 AND score <= 100) ERROR "成绩必须在0到100之间")这里用了 $ 运算符判断字符串是否属于某个集合,比写成 sex='男' OR sex='女' 简洁得多。birth 字段的 CHECK 里先用 EMPTY(birth) 放行空日期,因为空日期既不是 .NULL. 也不是具体的日期,如果不放行,用户不输入生日也过不了规则。这是 VFP 字段级规则里最容易被忽视的细节:日期字段的空值必须单独处理。一个实际的坑在于,当你通过表单输入数据时,字段级规则默认在字段失去焦点时触发,而不是保存按钮单击时触发。如果你把校验逻辑全压在 SQL UPDATE 上,界面层反而会因为规则弹出一堆原生提示框,界面风格和你自己写的提示完全不一致。所以我在做这类系统时,建表规则只保留字典级底线,比如性别取值、成绩范围,页面友好提示由表单控件完成。
2.3 索引类型怎么选:主索引、候选索引、普通索引的边界
VFP 的索引分四种:主索引、候选索引、普通索引、唯一索引。主索引只允许在数据库容器内的表上创建,每个表最多一个,并且字段值不能重复;候选索引和主索引约束一样,但一个表可以建多个;普通索引不限制重复值,主要用来加速查找和排序;唯一索引这个名字有误导性,它不是约束重复,而是同一索引键值只保留第一条记录参与显示。实例文档里如果出现“重复学号也能插入成功”的现象,你首先要查的就不是代码,而是索引类型。
* 在 student 表的学号字段上建立一个候选索引 ALTER TABLE student ADD UNIQUE TAG xh ON sno * 在姓名字段上建立一个普通索引,允许重名 INDEX ON sname TAG xmINDEX ON 命令创建的默认是普通索引,不需要 ALTER TABLE。TAG 参数是索引标识名,也就是这个索引在 .CDX 结构索引文件里的称呼。查询时用 SET ORDER TO xh 就能让表按学号顺序移动指针。注意,ADD UNIQUE 这里的 UNIQUE 创建的是候选索引,不是唯一索引,这是 VFP 一个非常容易踩的术语坑:SQL 标准里 UNIQUE 就是唯一约束,VFP 的命令里 UNIQUE 却表示压缩重复值。所以实例文档里如果出现“UNIQUE 索引”这个说法,你要先确认作者的意图。
关于索引还有一个隐藏规则:普通索引在打开表后是默认不生效的,除非上一次会话里用 SET ORDER 设置过。而主索引和候选索引如果是通过 ALTER TABLE 建的,表打开后会按主索引排序。这个默认排序的差异会让新手困惑:为什么同样是带条件查询,有的表返回顺序稳定,有的表顺序乱跳。原因就是主索引被自动设置,普通索引没有。解决方法是每次执行查询前,有意识地写上一句 SET ORDER TO TAG,不要指望默认状态。
3. 搭出第一个可运行的维护表单:从数据环境到按钮事件
3.1 表单与数据环境:先绑定再写代码
实例文档的附图里,最常出现的两张图一张是“数据环境设计器”,一张是“表单运行效果图”。数据环境是 VFP 表单特有的概念:它不是一个数据库,也不是一个表,而是表单运行时需要打开的表、视图和表之间关系的容器。你把表拖进数据环境后,表单运行时会自动按设计时的顺序打开表,关闭表单时自动关闭表。这样做的最大好处是,你不必在每个按钮的 Click 事件里写 USE 一堆表,也不会因为忘记关闭表导致文件被占用。
* 表单数据环境的 Init 事件:手动指定要打开的表和索引 OPEN DATABASE testdata USE student IN 0 EXCLUSIVE ORDER TAG xhEXCLUSIVE 表示独占打开,只有当前用户访问,适合课程设计这类单机系统。ORDER TAG xh 让表的数据环境打开后直接按学号索引排序,这样后续执行 GO TOP 或 SEEK 时,表本身就是有序的,避免了因为索引没生效而查不到数据。如果这一步不写,表默认按物理记录顺序访问,SEEK 会直接在未索引状态下报“表未排序”。注意:USE 命令里的 ORDER TAG 跟在表名和打开方式之后,顺序不能反。
3.2 按钮事件:Click、Valid、InteractiveChange 分别该放什么
表单界面最核心的交互是“新增”“保存”“删除”“退出”。按钮命令放哪个事件里,是 VFP 实例文档里经常讲不透的地方。命令按钮一般只处理 Click,也就是单击触发。但文本框的校验不能放在 Click 里,因为 Click 是鼠标事件,用户敲完字直接点按钮时,文本框的 Valid 会先触发。Valid 被设计成在控件失去焦点时执行,返回 .T. 表示允许离开,返回 .F. 表示光标不能移走。这是 VFP 表单最实用的输入校验点。
* 保存按钮的 Click 事件:把表单控件的值写回当前记录 SELECT student IF EMPTY(ALLTRIM(ThisForm.txtName.Value)) MESSAGEBOX("姓名不能为空", 48, "提示") ThisForm.txtName.SetFocus() RETURN ENDIF REPLACE sname WITH ALLTRIM(ThisForm.txtName.Value), ; sex WITH ThisForm.cboSex.Value, ; birth WITH ThisForm.txtBirth.Value, ; score WITH VAL(ThisForm.txtScore.Value) ThisForm.Refresh()这段代码里,ALLTRIM 去掉字符两侧空格,避免用户误输入空格导致姓名看似有值;VAL() 把文本框里的字符串转成数值,因为文本框的 Value 默认是字符型;MESSAGEBOX 的第二个参数 48 表示警告图标加确定按钮。保存时最忌讳的就是无条件写库,至少要做一次空值兜底。这属于“有界面层的校验,但仍然在保存事件里二次把关”的做法,和数据库字段级规则形成两层防护。有一个细节值得注意:REPLACE 只能修改当前指针指向的记录,如果用户先点“新增”,再点“保存”,插入逻辑必须自己处理。常见做法是保存按钮只负责 REPLACE,新增按钮负责 APPEND BLANK 并清空界面。两类职责拆开,代码不容易互相干扰。
3.3 用代码创建表单:实例附图的对照依据
你说你在做一个 VFP 数据库系统开发实例,肯定绕不开可视化表单设计器。但可视化设计器生成的界面文件是 .SCX,它是一个表文件,没法用文本 diff 查看改动。对提交课程设计和审计代码的人来说,更友好的方式是用代码创建表单,再引用到 .SCX 里。这里我给出一个最小可用的代码建表示例,它在命令窗口执行后就能看到一个带输入框和按钮的窗口:
frmMyForm = CREATEOBJECT("Form") WITH frmMyForm .Caption = "学生信息维护" .Width = 420 .Height = 260 .AutoCenter = .T. ENDWITH txtScore = CREATEOBJECT("TextBox") txtScore.Parent = frmMyForm txtScore.Left = 120 txtScore.Top = 40 txtScore.Width = 80 cmdSave = CREATEOBJECT("CommandButton") cmdSave.Parent = frmMyForm cmdSave.Caption = "保存" cmdSave.Left = 220 cmdSave.Top = 40 cmdSave.Click = [REPLACE student.score WITH VAL(ThisForm.txtScore.Value)] frmMyForm.Show(1)CREATEOBJECT("Form") 在内存里建立一个表单对象,不使用 .SCX 文件。控件创建后必须设置 Parent 属性,否则它只是孤儿对象,不会显示在表单上。Show(1) 表示模态显示,用户关掉窗口前不能操作其他窗口。cmdSave.Click 后面直接跟一个字符串表达式,这是 VFP 里给对象方法赋代码块的简便写法。这种方式在实例文档里很难用截图表达,所以绝大多数附图展示的还是设计器界面;但你要复现一个实例时,反而应该先看图中表单的 Caption、按钮 Caption 和输入框位置,再用代码把这些属性还原出来。这个过程能帮你厘清每个控件挂在哪一层容器上,比照图拖鼠标更接近“开发”而不是“拼图”。
4. 把增删改查做扎实:SQL命令与表操作的对照
4.1 SQL增删改查的VFP写法
VFP 支持一套兼容 SQL-92 的语法,核心命令是 SELECT、INSERT、UPDATE、DELETE。和其他数据库最大的区别是,VFP 的 DELETE 默认只做逻辑删除,在记录上打删除标记,不真正移除数据;要做物理删除必须再执行 PACK。这个设计对实例开发有利也有弊:利在于误删可以后悔药式恢复,弊在于新手不知道 PACK 时,表文件越来越大,统计时用 COUNT 还会把已删除记录算进去。
* 插入一条记录 INSERT INTO student (sno, sname, sex, birth, score) ; VALUES ('2025001', '张明', '男', {^2025-03-01}, 88.5) * 按学号更新成绩 UPDATE student SET score = score + 1 WHERE sno = '2025001' * 逻辑删除性别为女的记录 DELETE FROM student WHERE sex = '女' * 物理删除已经被逻辑删除的记录,需要独占打开表 USE student EXCLUSIVE PACKINSERT INTO 的 VALUES 部分,字符型用单引号,日期型用花括号加脱字符号 {^2025-03-01},这是 VFP 严格的日期字面量写法。如果你写 {2025-03-01},它会被当成日期表达式而不是日期字面量,在部分语言设置下会解析错误。UPDATE 的 SET 支持表达式赋值,score = score + 1 是合法写法。DELETE 命令在事务里可以回滚,但 PACK 属于物理文件操作,不能在事务里回滚,所以 PACK 一定要在事务结束后单独执行。如果实例文档里要求保留原始数据以便复查,就不要做 PACK,而是用 SET DELETED ON 把所有读取命令自动屏蔽删除记录。
* 查询女生成绩大于等于60的记录 SELECT s.sno, s.sname, s.score ; FROM student s ; WHERE s.sex = '女' AND s.score >= 60 ; ORDER BY s.score DESC INTO CURSOR myresult这段查询用了表别名 s,FROM 后面紧跟别名,这是多表联查的标准写法。INTO CURSOR myresult 把查询结果放到一个临时表里,这个临时表可以直接赋给表单的 Grid 控件作为数据源。注意,ORDER BY DESC 表示降序。另一个参数是 INTO TABLE,它会生成一个真实的 DBF 文件,大多数实例里不需要持久化查询结果,所以用 CURSOR 就够了。VFP 的 SELECT 和 SQL Server、MySQL 还有个明显差别:字符串比较默认不区分大小写,除非用 SET EXACT ON。这点在做登录名去重时要格外小心。
4.2 不用SQL的老写法:APPEND、REPLACE、SEEK
虽然 SQL 已经够用,但存量 VFP 系统里仍然大量存在老式表操作代码。原因很简单:老项目的作者从 FoxBase 过来,习惯用指针定位加 REPLACE 处理业务,性能上在小数据量下表现也不差。如果你拿到一份实例文档,里面的代码全是 GO TOP、SKIP、LOCATE、REPLACE,没必要强行改成 SQL,但要能看懂。
USE student EXCLUSIVE GO TOP LOCATE FOR sno = '2025001' IF FOUND() REPLACE score WITH 90 ENDIFLOCATE FOR 是顺序扫描,从当前指针位置一直扫到文件尾。FOUND() 判断是否查到了记录。这套写法适合单表单文件、记录数几千条以内的场景。一旦表超过几万条,LOCATE 的耗时就会线性增加,换成 SEEK 才是正解。SEEK 要求表有激活索引,并且 SET ORDER 指定的索引键必须与查找值类型一致。
USE student IN 0 SET ORDER TO TAG xh SEEK '2025001' IF FOUND() REPLACE score WITH 91 ENDIFSET ORDER TO TAG xh 把学号索引设为当前有效索引,SEEK 按索引键值查找。字符型学号用单引号,数值型关键字段就不带引号。这里最常见的错误是:建了索引但没 SET ORDER,直接 SEEK 只能得到错误结果;或者索引建在 sno 上,却用 sname 去找,同样查不到。VFP 里还有一个“玄学”问题,就是 SET EXACT 状态会影响字符串等值比较。默认 OFF 时,'2025' 能匹配上 '2025001' 的前四位;如果实例里的学号字段宽度不固定,你会莫名查出多条记录。排查时先执行 SET EXACT ON,强制要求完全匹配。
4.3 事务:给错误操作留一剂后悔药
VFP 支持本地事务,命令是 BEGIN TRANSACTION 和 END TRANSACTION,回滚用 ROLLBACK。事务内的表更新,包括 INSERT、UPDATE、DELETE 和 REPLACE,都可以在 ROLLBACK 时撤销。这在你做一个多步操作时尤其重要,比如批量给学生加分时,第 10 条记录触发字段级规则错误,如果不用事务,前 9 条已经改掉了,后面想整体取消做不到。
USE student EXCLUSIVE BEGIN TRANSACTION UPDATE student SET score = score + 5 WHERE score >= 60 IF RECCOUNT() > 100 ROLLBACK MESSAGEBOX("记录数异常,操作已回滚", 48, "事务提示") ELSE END TRANSACTION ENDIF这段代码模拟了一个批量操作的后置校验。因为 UPDATE 不会逐条报错,所以只能在事务结束后用 RECCOUNT() 判断影响的行数是否符合预期,不符合就 ROLLBACK。事务对 DBC 结构操作的约束要特别清楚:BEGIN TRANSACTION 之后,不要执行 ALTER TABLE、MODIFY STRUCTURE、PACK、ZAP 这类命令,它们不受事务保护,而且可能直接报错。实例文档里如果出现“先 PACK 再事务回滚”的写法,基本可以断定是无效演示。
5. 避坑:VFP实例开发中最常见的五处翻车点
5.1 关窗口后数据丢了一半
现象:在表单里录入了好几条记录,点退出按钮时没有提示;重新打开表单后,最后几条记录不见了,表里留下的是空白记录。原因:表单数据环境里把 BufferModeOverride 设成了缓冲模式,但没有在保存时调用 TABLEUPDATE()。缓冲模式下,所有表操作的记录不是立即写入原表,而是缓存在内存里,必须显式提交才能落盘。解决:在“保存”按钮的 Click 事件里调用表格对应游标的 TABLEUPDATE(),并用返回值判断是否提交成功;如果提交失败,用 TABLEREVERT() 放弃当前修改。比较稳妥的做法是统一在表单的 Unload 事件里做一次兜底提交:
* 表单 CheckOut 按钮:提交并关闭 IF TABLEUPDATE(.T.) THISFORM.Release() ELSE MESSAGEBOX("数据没有保存成功,请检查后重试", 48, "提示") ENDIFTABLEUPDATE(.T.) 的 .T. 表示强制提交所有修改过的记录。使用缓冲模式时,我的血泪经验是:宁可多做一次提交,也不要只依赖控件失去焦点时的隐式保存。
5.2 日期字段显示成空串,判断却总是不对
现象:birth 字段没填值时,表单里显示 00/00/00 或者空白,代码里用 ISNULL(birth) 判断却永远返回 .F.。原因:VFP 的日期字段空值有两种:NULL 和空日期 { / / }。绝大多数表单输入不写 NULL,而是保留空日期。ISNULL 只能判断前者。解决:所有针对日期字段的空值判断都用 EMPTY(birth)。写入空日期时用 {},不建议用 CTOD(''),因为不同系统日期格式下 CTOD 解析可能翻车。
IF EMPTY(student.birth) REPLACE student.birth WITH {} ENDIF实例文档附带的源码里,凡是出现“日期不显示”“日期对不上”的反馈,十有八九都是空日期类型混用造成的。统一用 EMPTY 判断后,这类问题基本绝迹。
5.3 备注字段在表格里只显示“Memo”
现象:用表单的 Grid 显示表时,备注字段那一列全部显示为固定的“Memo”字样,业务文本根本看不到。原因:Grid 列默认绑定字段名时,备注型字段被当成类型标志展示,要显示内容必须把列里的文本框控件绑定到具体的字段属性。解决:在 Grid 的列对象里找到 Text 控件,把它的 ControlSource 设置为表名.备注字段名,例如 student.memo。如果不想让用户直接编辑备注,再把列的 ReadOnly 设为 .T.。这个操作不在设计器里做时,只改列标题,内容永远是“Memo”。
5.4 SEEK 查到了完全不相干的记录
现象:用 SEEK 按学号查找,返回的第一条记录学号完全不一样;或者同一个 SEEK 语句在两次运行里结果不同。原因:表有多个索引,当前活动索引不是你想用的那个。VFP 会用最近一次 SET ORDER 指定的索引,如果没指定,则可能沿用上次会话的残留状态。解决:每条使用 SEEK 的程序段前,先显式 SET ORDER TO 目标索引标识。对不信任的现场,还可以加一句 SET ORDER TO 0 取消索引,强制回到物理顺序,再用 LOCATE 做慢速查找排障。
5.5 换台电脑打开库,提示找不到表所属的数据库
现象:把项目文件夹整个复制到另一台电脑,双击表单或执行 DO FORM 时报错,提示表 xxxx 所属的数据库不存在或无法打开。原因:表文件 DBF 头里记录了它所属数据库 DBC 的相对路径或文件名。直接复制 DBF 而没复制对应的 DBC 时,表被检测到“所属库丢失”。解决:连同 .DBC、.DCT、.DCX 三个数据库容器相关文件一起复制;如果只要表数据,可以先执行 FREE TABLE,把表从数据库中移除,让它变成自由表,这样就不再依赖 DBC。但要注意,FREE TABLE 后字段规则和永久关系全部失效,只适合导出数据做临时分析。
FREE TABLE student这是一个不可逆操作,执行前备份原库。实例文档里的“附图”如果是在你本机正常,换台机器就报错,第一条检查路径就是这句话。
6. 提交前的一个自检技巧:写一段短程序验证增删改查
6.1 用 RECCOUNT 和 SQL 做冒烟验证
表单能打开、能点按钮,不代表增删改查全部正常。最常见的坑是“保存按钮能点,但数据没进表”。为了不靠肉眼逐条核对,我习惯在交付前写一段自检程序,用记录数的变化和查询结果来断言功能是否正常。这段程序不是正式功能,只是开发期验证用的脚手架,但它能在两分钟内覆盖增删改查四条路径。
* check.prg:交付前冒烟验证 CLEAR OPEN DATABASE testdata USE student EXCLUSIVE oldCount = RECCOUNT() * 增:插入一条测试记录 INSERT INTO student (sno, sname, sex, score) ; VALUES ('test001', '自检', '男', 60) IF RECCOUNT() <> oldCount + 1 ? "插入失败" ELSE ? "插入通过" ENDIF * 改:把测试记录成绩改为80 UPDATE student SET score = 80 WHERE sno = 'test001' * 查:验证修改结果 SELECT score FROM student WHERE sno = 'test001' INTO CURSOR v IF v.score = 80 ? "修改与查询通过" ENDIF * 删:逻辑删除并物理清理 DELETE FROM student WHERE sno = 'test001' PACK ? "自检完成" CLOSE DATABASERECCOUNT() 返回表里的物理记录数,注意在 SET DELETED ON 状态下逻辑删除的记录仍被计算,所以插入验证要用物理记录数。INTO CURSOR v 后用 v.score 读取查询结果,前提是查询结果只有一条记录;如果查到多条,应该用 SELECT v.score 或 v.score[1] 取第一条。这段自检程序每次都在正式功能开发完后跑一遍,能在 10 秒内告诉我是哪一步出了问题。
6.2 养成提交前看文件依赖的习惯
除了跑自检程序,我还会检查一遍项目文件清单:是否有 .DBC、.DCT、.DCX、.DBF、.CDX、.FPT 全部就位,数据库容器路径是否被写死,表单是否引用了临时表。很多 VFP 实例“交到老师手里打不开”的问题,根源不是代码逻辑,而是漏交了 .DCT 或 .FPT 文件。你可以在自检程序里加一个 FILE() 判断来兜底:
IF FILE("testdata.dbc") AND FILE("student.dbf") AND FILE("student.cdx") ? "主文件完整" ENDIF这套习惯不是我一开始就有的,是吃过几次“明明本机能跑,换个目录就崩”的亏之后长出来的。做 VFP 数据库系统开发实例,最容易被忽略的不是怎么写代码,而是怎么保证代码所依赖的表、索引、库容器和备注文件始终在一起。先把自检程序跑绿,再打包目录,交活时能省掉大半解释工作。希望这个判断清单和自检程序能帮到你,让你第一次在别人机器上运行实例时,也能一次通过。
本文还有配套的精品资源,点击获取