资讯动态

Delphi习题答案工程化:.pas编译、OCR与docx生成

发布时间:2026/9/17 17:05:33 来源:尧图企业网站定制
简介这份文档是《完整的Delphi程序设计教程》课后习题配套答案面向正在学习Object Pascal与Delphi集成开发环境的高校学生、自学者及需要进行Windows桌面应用入门的开发者。压缩包内仅含1个docx文件大小约15KB体量轻便便于随时查阅与打印。内容围绕教材中的典型习题展开涵盖小时钟绘制、两点间距离计算以及学生成绩记录与文件筛选等练习。其中小时钟题目借助Timer组件定时刷新画布并通过decodetime、sin、cos与PaintBox的Canvas方法绘制时、分、秒指针距离计算题演示FormPaint重绘事件、坐标轴绘制与sqrt求欧氏距离成绩处理题则定义studentrecord记录类型用assignfile、reset、rewrite、read、write与eof完成stu.dat到stu1.dat的记录读写与奖学金筛选。这些代码样例可帮助读者理解Delphi的事件驱动模型、图形绘制、几何运算及文件操作等核心概念对照课本复盘习题、调试运行与查漏补缺适合作为课程作业参考与上机练习对照材料。目前已有164人学习。1. 从一份「完整的delphi程序设计教程课后习题答案.docx」反推习题到底该怎么跑通很多人搜这份文档是因为手上只有教材目录没有可运行的代码。真实的卡点从来不是答案的措辞而是 Delphi 习题从第 3 章开始就有窗体、事件、数据库抄下来的代码在 Delphi 7 里能编译在 XE2 里可能因为 string 默认变成 UnicodeString 而给出不一样的输出。所以一份可信的习题答案文档本质上是「每道题都编译过、运行过、贴上真实输出」的工程产物。这篇讲的就是把教材习题逐题跑通、把过程沉淀成 docx 的完整路径环境怎么选.pas 文件怎么拆控制台题与 VCL 窗体题分别怎么组织答案扫面件怎么用 OCR 转成可检索文本最后怎么自动校验整份文档的题目覆盖率与编译通过率。适合自学者、带课老师以及隔了几年重新捡起 Object Pascal 的老手。2. Delphi 习题答案的工程化准备版本、.pas 拆分与目录规范习题答案最怕的一件事是「在我机器上能跑」。Delphi 的版本差异直接决定答案对不对得上教材所以准备工作要先解决版本问题再解决代码组织问题。2.1 Delphi 7 与 Delphi XE2 的编译差异怎么影响答案国内教材绝大多数以 Delphi 7 为基准而新机器上装的往往是 XE2 或更高版本。两者在习题层面最容易踩的差异集中在字符串和字符宽度上做题前先确认清楚否则答案里的输出长度、字符下标全都会偏。对比项Delphi 7Delphi XE2对习题的影响string 默认类型AnsiStringUnicodeString汉字 Length 由 2 变 1字符串题答案输出不同Char 宽度1 字节2 字节PChar 转换、内存题结果不一致TStringList 读写文件无编码参数需传 TEncoding读中文 txt 出现乱码默认窗体单位像素近似需设 Scaled窗体布局题截图与教材不符单元文件扩展名.pas .dfm.pas .fmx/.dfm建题时选错模板导致控件缺失处理办法很直接凡是字符串长度相关的题目在答案里显式声明{$H}并注明「本答案按 Delphi 7 的 AnsiString 语义给出XE2 下 Length(中) 会返回 1需要在答案旁标注差异」。这样一份文档既能对上教材也能给用新版本的人一条退路。2.2 用 .pas 文件把每道习题拆成可编译单元不要把十几道题塞进一个工程。更稳的做法是每题一个 .pas 文件控制台题用{$APPTYPE CONSOLE}窗体题单独建工程。控制台题可以完全脱离 IDE用命令行编译器批量验证这对后面做自动校验非常关键。{ Exercise3_2.pas —— 教材第 3 章第 2 题输入三个整数并降序输出 } {$APPTYPE CONSOLE} // 声明为控制台程序编译出可直接运行的 exe {$H} // 长字符串模式与 Delphi 7 默认行为对齐 program Exercise3_2; var A, B, C, T: Integer; begin Write(请输入三个整数以空格分隔); ReadLn(A, B, C); if A B then begin T : A; A : B; B : T; end; // 两两比较冒泡思路 if A C then begin T : A; A : C; C : T; end; if B C then begin T : B; B : C; C : T; end; WriteLn(降序结果, A, , B, , C); ReadLn; end.逻辑说明三个 if 完成三次交换等价于三元素选择排序保证 A≥B≥C。{$APPTYPE CONSOLE}决定生成的是控制台子系统程序缺了它双击运行会一闪而过。{$H}让 string 走长字符串路径避免早期版本对超过 255 字符的字符串截断。参数说明A、B、C 为输入T 是交换用的临时变量用 Integer 而非 Real因为题目限定整数。末尾的ReadLn用来暂停窗口是控制台习题答案的标准收尾正式提交时可以去掉并在答案里注明。命令行编译与批量验证# -B 全部重建-Q 安静模式-U 单元搜索路径-E 输出 exe 目录 dcc32 -B -Q -Usrc\common -Ebuild src\Exercise3_2.pas echo 退出码: $? # 0 表示编译通过非 0 时去 build 目录看错误行号参数说明-B强制重编译所有依赖单元避免旧 dcu 干扰-Q抑制编译横幅方便脚本抓取错误信息-U指定公共单元目录把复用代码放这里-E把可执行文件统一输出便于后续批量运行取输出。2.3 答案目录与命名规范一份能长期维护的答案文档背后一定有一套目录规则。建议按「章-题号-题型」三级命名题型用 c控制台、vVCL 窗体、d数据库、f文件操作四类这样一眼就能看出这题该怎么验证。delphi-answers/ ├─ src/ │ ├─ ch03/ │ │ ├─ Exercise3_2_c.pas # 控制台题可命令行编译 │ │ └─ Exercise3_7_v/ # 窗体题独立工程目录 │ ├─ ch05/ │ └─ common/ # 复用单元如排序、字符串工具 ├─ build/ # 编译产物 ├─ shots/ # 每题的运行结果截图 └─ answer.docx # 最终答案文档命名规范带来两个直接好处一是dcc32可以直接用通配符批量编译 ch03 下所有_c.pas二是生成 docx 时按文件名排序就是教材顺序不需要手工调整章节。窗体题的运行结果截图统一放 shots 目录文件名与题号一致例如Exercise3_7_v.png后面配对进文档时不会错位。3. 按题型拆解从 Object Pascal 语法题到 VCL 窗体题的答案落地Delphi 习题大致分四类每类的答案结构差别很大用同一种写法套所有题文档会变得又长又难查。按题型分别给出模板比逐题写更省力。3.1 顺序、分支、循环三类题的答案模板这三类是所有教材开篇必有的题特点是输入输出明确、可自动化验证。答案结构固定为「题目 思路一句话 完整代码 输入样例 输出样例」不要写大段解释读者要的是能对照的输出。{ 输入一个正整数 n判断是否为素数 } function IsPrime(N: Integer): Boolean; var I: Integer; begin Result : N 1; // 1 和负数直接判否 I : 2; while (I * I N) and Result do // 只需试到平方根 begin if N mod I 0 then Result : False; Inc(I); end; end;逻辑说明循环条件写成I * I N而不是I Sqrt(N)避免浮点比较误差这是教材答案里常见的坑。and Result短路一旦判定合数立即退出复杂度降到 O(√n)。参数说明入参 N 为待判正整数返回值为 Boolean。判题时用 1、2、9、97、100 五组样例覆盖边界这五组样例要原样写进答案文档的输出块方便读者自测。3.2 数组与字符串处理题的通用写法这类题在版本差异上最容易翻车。凡涉及Length、Copy、Pos的题目答案里必须写明基准版本。一个常见题是统计子串出现次数用Pos循环配Delete比逐字符比较短得多。function CountSub(S, Sub: string): Integer; var P: Integer; begin Result : 0; P : Pos(Sub, S); // 找到第一次出现的位置1 基 while P 0 do begin Inc(Result); Delete(S, 1, P Length(Sub) - 1); // 连同子串一起删掉避免重叠计数 P : Pos(Sub, S); end; end;逻辑说明Delphi 的 Pos 返回 1 基下标找不到返回 0所以循环条件写P 0。Delete的长度参数是P Length(Sub) - 1表示从开头删到子串结束这样不会重复匹配同一处。若题目要求允许重叠计数就把Delete(S, 1, P)只删掉一个字符答案里要把这两种情况并列说明。参数说明S 为源串Sub 为待统计子串。注意 Sub 为空串时 Pos 恒返回 1 会死循环答案中应加一行if Sub then Exit;作为防御。3.3 VCL 窗体题的答案结构Timer 与 ListBox 自绘窗体题的答案光贴代码没用读者看不到控件属性就复原不出来。标准做法是「控件清单表 事件代码 运行截图」三件套。以做一个每秒刷新一次的计时器为例先用表格把属性列清楚。控件属性/事件取值说明Timer1Interval1000单位毫秒1000 即 1 秒Timer1EnabledTrue为 True 时窗体加载即开始计时Timer1OnTimerTimer1Timer事件处理过程名Label1Caption0初始计数Button1Caption / OnClick暂停 / Button1Click控制启动与停止procedure TForm1.Timer1Timer(Sender: TObject); begin FCount : FCount 1; // FCount 为窗体私有字段 Label1.Caption : IntToStr(FCount); end; procedure TForm1.Button1Click(Sender: TObject); begin Timer1.Enabled : not Timer1.Enabled; // 取反实现启停切换 Button1.Caption : IfThen(Timer1.Enabled, 暂停, 继续); end;逻辑说明Interval 是间隔而非精度1000 毫秒只是「不早于 1 秒触发」实际累加会有毫秒级漂移。若题目要求精确计时答案里应改用GetTickCount或Now计算差值这一点要在文档中明确标注否则读者会以为计数等于秒数。参数说明FCount 必须在窗体类的 private 区声明不能用全局变量否则多窗体时互相污染。IfThen需要引入System.StrUtilsDelphi 7 下写完整 if 语句替代。ListBox 自绘题的答案要点是三个属性配合一个事件Style设为lbOwnerDrawFixedItemHeight决定行高然后在OnDrawItem里自己画。procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); begin if Odd(Index) then ListBox1.Canvas.Brush.Color : $00F0F0F0 // 隔行浅灰 else ListBox1.Canvas.Brush.Color : clWhite; ListBox1.Canvas.FillRect(Rect); ListBox1.Canvas.TextRect(Rect, Rect.Left 4, Rect.Top 2, ListBox1.Items[Index]); end;逻辑说明自绘模式下系统不再负责绘制条目必须自己 FillRect 打底否则文字会叠在上一条内容上。Index 从 0 开始Odd(Index)实现斑马纹。TextRect而非TextOut因为前者会按 Rect 裁剪长文本不会溢出到相邻行。参数说明Rect 是当前条目的绘制矩形由 ItemHeight 和滚动位置决定。颜色常量$00F0F0F0是 BGR 顺序写clSilver也可以但色值偏深。State 参数包含odSelected如果题目要求选中行高亮需要额外判断并换前景色。3.4 文件与数据库题的答案组织文件题的答案必须附上被读写文件的完整样例内容否则读者无法复现。用 TStringList 处理文本文件是最短路径注意 XE2 起LoadFromFile需要显式传编码。var L: TStringList; begin L : TStringList.Create; try L.LoadFromFile(data.txt, TEncoding.UTF8); // Delphi 7 下删除第二个参数 L.Sort; // 按字典序排序 L.SaveToFile(sorted.txt, TEncoding.UTF8); finally L.Free; end; end;逻辑说明try/finally 保证异常时对象仍被释放这是教材后几章反复强调的点答案里不能省。文件不存在时LoadFromFile抛 EFOpenError稳妥写法是先判FileExists。参数说明TEncoding.UTF8是 XE2 及以上才有的重载Delphi 7 下直接LoadFromFile(data.txt)。数据库题则建议统一用 ADO 组件连接串写进答案时把敏感路径换成占位符避免读者直接复制后连不上。4. 把散落的习题变成一份可检索的 answer.docx代码跑通了只是前半程剩下的工作是把题目、代码、输出、截图整理进一份文档并且让它能被搜索到具体某道题。手工复制粘贴几十道题很容易漏用脚本走流水线更稳。4.1 用 Tesseract 把习题扫描件转成可检索文本教材习题页大多是拍照或扫描件直接放进 docx 没法搜索。常见做法是用 Tesseract 命令行先做一遍 OCR把题干转成文本再人工校对公式和代码片段。# chi_simeng 同时加载中英文模型代码里的关键字靠 eng 模型识别 tesseract shots/ch03_page12.png stdout -l chi_simeng --psm 6 txt/ch03_page12.txt参数说明-l chi_simeng指定语言包必须把中文包下载到 tessdata 目录--psm 6表示按统一文本块切分适合排版规整的教材页输出到 stdout 再用重定向落盘方便批量脚本循环。识别结果里0与O、1与l混淆很常见尤其出现在代码块中所以 OCR 结果只用于题干代码一律以实际编译的 .pas 文件为准不要用 OCR 文本回填代码。import pytesseract from PIL import Image from pathlib import Path for img in sorted(Path(shots).glob(ch*_page*.png)): text pytesseract.image_to_string( Image.open(img), langchi_simeng, config--psm 6) (Path(txt) / f{img.stem}.txt).write_text(text, encodingutf-8)逻辑说明按文件名排序保证章序不乱逐张识别后以同名 txt 落盘后续合并时用 stem 就能和截图一一对应。config--psm 6与命令行参数等价写在代码里便于不同页码用不同参数。参数说明lang参数顺序影响置信度中文在前时中文识别率略高如果教材含大量英文术语可改成engchi_sim再比较一次结果。4.2 代码、输出与截图的配对规则配对错位是答案文档最常见的问题题目 3.4 的截图挂到 3.5 下面读者照着做必然报错。用一张清单表强制约束每个条目的四个字段缺任意一个就不允许进入文档。字段来源命名规约缺失后果题号教材目录ch03-04无法检索定位源代码src 目录下 .pas与题号同后缀读者无法复现运行输出编译后 exe 重定向文本与题号同名 .out无法核对结果运行截图shots 目录与题号同名 .png窗体题无法还原界面控制台题的输出建议直接由程序重定向生成而不是手打这样文档里的输出与代码严格一致。cd build ./Exercise3_2.exe ../src/ch03/Exercise3_2.in ../out/Exercise3_2.out逻辑说明把输入样例单独存成 .in 文件运行时重定向进程序输出直接落盘为 .out生成文档时按题号读取即可全程不经过剪贴板杜绝手误。参数说明是标准输入重定向程序里的ReadLn会从文件读而不是等键盘覆盖写入批量时注意别把上题输出冲掉必要时加题号前缀。4.3 用 python-docx 生成规范排版的答案文档最后一步是把上面四类素材拼成 docx。标题层级、代码字体、题号索引这三件事做对文档就具备了可用的检索体验。from docx import Document from docx.shared import Pt from pathlib import Path doc Document() doc.styles[Normal].font.name 宋体 doc.styles[Normal].font.size Pt(10.5) for src in sorted(Path(src).rglob(Exercise*_c.pas)): qid src.stem # 例如 Exercise3_2_c doc.add_heading(f习题 {qid}, level2) code src.read_text(encodingutf-8) p doc.add_paragraph(code) p.runs[0].font.name Consolas # 代码用等宽字体避免对齐错乱 out Path(out) / f{qid}.out if out.exists(): doc.add_heading(运行结果, level3) doc.add_paragraph(out.read_text(encodingutf-8)) doc.save(answer.docx)逻辑说明rglob递归收集所有控制台题文件名即题号天然有序。代码段落单独设等宽字体中文题干的宋体不受影响。运行结果存在时才写小节避免生成空的「运行结果」标题。参数说明level2对应文档里的二级标题方便用导航窗格按题号跳转字号 10.5 磅是 A4 正文的常用值代码段可再降到 9 磅以控制页数。窗体题的截图用doc.add_picture追加宽度建议固定为 14 厘米与正文版心对齐。4.4 编译通过率与题目覆盖率怎么自动核对文档生成完不代表题目做全了。写一个脚本把「教材目录里的题号」和「src 目录里的文件」做差集再统计编译退出码两个数字都对得上才算完整。total0; ok0 for f in src/ch*/*_c.pas; do total$((total1)) dcc32 -B -Q -Ebuild $f /dev/null 21 ok$((ok1)) \ || echo 编译失败$f done echo 通过 $ok / $total逻辑说明循环遍历所有控制台题编译成功计数加一失败立即打印文件名方便定位。窗体题无法这样批量编译单独在 IDE 里逐个人工确认并记录。参数说明/dev/null 21把编译输出丢弃只保留退出码判断如果想把错误信息留档改成2 build/errors.log。差集核对可以用comm -23 (sort qids.txt) (ls src/ch*/*.pas | xargs -n1 basename | cut -d. -f1 | sort)完成输出的就是漏做的题号。5. 进阶版本比对、片段库与答案文档的长期可维护性答案文档写完就发出去过两年自己都看不懂这是最亏的。留一点工程余量后面复用成本会低很多。5.1 用编译开关维护双版本答案与其维护两份代码不如用条件编译把版本差异收在一个文件里答案文档只贴一次读者按自己的版本看对应分支。{$IFDEF VER150} // Delphi 7 的版本常量 L.LoadFromFile(data.txt); {$ELSE} L.LoadFromFile(data.txt, TEncoding.UTF8); {$ENDIF}逻辑说明VER150 对应 Delphi 7VER230 起为 XE2 系条件编译让同一份 .pas 在两种环境下都能编译。文档中这段代码不需要拆成两段只在下方加一句版本说明即可。参数说明版本常量写在System.pas中用错常量会导致分支永不生效可以用{$IF Declared(CompilerVersion)}的方式先验证。批量编译时给 dcc32 传-DVER150可以模拟 Delphi 7 环境这是校验双版本答案最省事的办法。5.2 把高频代码抽成可复用片段库排序、字符串处理、输入校验这三类代码在习题里反复出现抽到 common 目录后每道题的答案能缩短三分之一读者看到的也是干净的题目逻辑而不是一屏工具函数。片段单元文件典型调用出现在哪些题素数判定common/MathUtil.pasIsPrime(n)循环题、数组题子串统计common/StrUtil.pasCountSub(s, sub)字符串题输入重试common/ConsoleUtil.pasReadIntDef(0)所有控制台题文件读写封装common/FileUtil.pasLoadLines(path)文件操作题抽片段的判断标准是「被三道以上题目用到」低于这个数就留在原题里避免 common 目录变成杂物间。片段的接口注释要写清前置条件和异常行为例如ReadIntDef在输入非法时返回默认值而不抛异常这条约定直接决定了上层题目的答案写法。5.3 一个提升检索命中率的小技巧docx 的搜索能力取决于文本本身是否带关键词而教材题号往往只是「第 3 章 第 4 题」这种形式。生成文档时把题号、题型、涉及的知识点拼成一行隐藏不了的索引行放在每道题的开头用方括号包起来例如[ch03-04][循环][素数][控制台]。这样读者在 Word 里搜「素数」能直接跳到题搜「控制台」能筛出所有可命令行验证的题。索引行本身不影响阅读但它把一份按章节排列的静态文档变成了一份可按知识点检索的资料对复习阶段的价值远大于多写几行解释。索引行建议由脚本从 .pas 文件头部的一行注释自动读取注释里写{tags: 循环,素数,控制台}避免手工维护时漏标。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价