• VisualForxPro程序设计教程 > 程序设计与算法基础教程
  • 程序设计与算法基础教程

    免费下载 下载该文档 文档格式:PDF   更新时间:2014-04-02   下载次数:0   点击次数:1
    程序设计与算法基础教程
    第1章
    1.1 Delphi 概述
    Delphi 简介
    Delphi是一个新时代的 Windows 加速程序开发工具 Rapid Application Development 它 由美国 Imprise 公司研制 在 1993 年问世后受到广泛欢迎 在短短几年内版本即从 1.0 迅速 发展到 5.0 特别是 1999 年 7 月推出的 Borland Delphi 5.0 版本 被 InfoWold 杂志评为最 佳 Web 及 Windows 应用程序开发工具 和早期版本相比 Delphi 5.0 的主要特色是简化了 Windows 应用程序和网络浏览器 Web 服务器 中间件以及后端大型数据库的集成 提供了 对 XML(XML eXtensible Markup Language 可扩展的标记语言)和 HTML4.0 HTML Hypertext Markup Language 超文本链接标示语言 的支持 使用户能够方便地进行 Internet 和分布式 机应用系统的开发 把数据迅速发布到 Internet 上 在 Delphi 5.0 的可视化控件 库 VCL 即 Visual Component Library 中有 200 多个控件 它们不但奇迹般地支持了 Internet 应用程序和数据库应用系统的开发 也强有力地支持了多媒体应用系统的开发 Delphi5.0 主要有 3 种版本 即标准版 专业版和企业版 其中标准版是单机版 包含有 集成开发环境 编译器 dBASE和 Paradox 数据库引擎 可视化组件库 桌面数据库 Database Desktop 和 SQL 数据库查询软件 专业版在标准版的基础上增加了本地 InterBase 服务器 ODBC 连接和其他一些 ActiveX 组件等 企业版则在专业版的基础上又增加了 Oracle Sybase InterBase 等数据库的 SQL 连接驱动程序等
    1.2 Delphi 5.0 的安装 启动与退出
    1.2.1 Delphi 5.0 对系统资源配置的基本要求
    Delphi 5.0 对计算机系统资源配置的基本要求如下 硬件环境为 Intel Pentium 133 以上的 CPU 32MB以上的内存 大于 64MB的硬盘空 间 CD-ROM 驱动器 支持 256 色 具有 640*480 分辨率的显示驱动和显示适配卡 以及其 他常规输入输出设备 如键盘 鼠标 打印机等 软件环境为 Windows NT4.0 Windows 95 或 Windows 98 操作系统
    1.2.2 安装过程
    首先将载有 Delphi5.0 安装系统的光盘插入光盘驱动器 若系统设置了光盘插入自动通 告功能 则稍候片刻 系统就自动进入安装画面 否则可以选择执行光盘 Install 目录下的 Setup.exe 进入安装画面 然后即可在安装向导的提示下逐步进行各项选择或设置 一般情 况下 均采用了安装程序的默认设置 安装过程比较简单 在软件安装成功后 机将 提示重新启动或自动启动计算机 使 Delphi 和 Windows 系统结合起来 以便于后来的启动 1
    运行
    1.2.3 Delphi 5.0 的启动与退出
    单击 Windows 开始 按钮 在 程序 菜单中选择 Borland Delphi 5 选项 然后在 如图 1-1 所示的 Delphi 程序组中选择 Delphi 5 选项 即可进入 Delphi 开发环境
    图 1-1 Delphi 程序组
    如果在 Windows 桌面上设置了 Delphi 5.0 的快捷方式 直接选择点击它进入 Delphi 5.0 将会更加方便 选择 Delphi 5.0 窗口的关闭窗 钮 或选择窗口控制菜单的 退出 选项均可退出 Delphi 5.0 开发环境
    1.3 Delphi 开发环境简介
    在启动 Delphi 5.0 并进入其开发环境后 即可看到外观如图 1-2 所示的操作界面 Delphi 5.0 的基本开发环境主要包括主窗口 Main Window 对象监视器 Object Inspector 项目 器 程序 编辑窗口 Code Editor 和窗体编辑窗口 Form 等多个 部分 每个部分含有不同的功能区块 如主窗口包含有标题栏 主菜单条 加速按钮 Speed Bar 和组件板 Component Palette 等
    1.3.1 常用主菜单命令和加速按钮
    一个 Delphi 5.0 应用程序被称为一项工程 Project 它要完成其各项功能 就要通过用 户操作界面 一个个的窗体 上的按钮选择 在输入框编辑输入必要的数据或命令等项操作 来实现 由于这些窗体 按钮 输入框等都具有自己特定的属性和对数据处理的方法 着特定的操作 Delphi 5.0 将它们统称为组件 或构件 为了记录这些窗体和组件的属性 存放实现其特定操作功能的程序和数据等 Delphi 将会用到数十种类型的文件 除了可包含 的文件外 和程序设 关的文件主要有 3 种 控制和记录该程序中所有文件的工程文件 或 称项目文件 完成各窗体指定操作的程序单元文件和记录各窗体属性的窗体文件 针对上述 各类文件的编制和处理要求 Delphi 5.0 在其主菜单条中共提供了 10 个主菜单项 和 Windows 的菜单构成十分类似 共提供菜单命令 100 多条 下面结合本书内容介绍这些主菜单项中常 用的菜单命令 2
    图 1-2 Delphi 5.0 开发环境的主要组件
    1. File 文件 菜单 File 菜单的功能包括新建工程 打开 保存和打印文件等 1 New 选项用来创建一个新的项目 通过其 New Items 对话框 如图 1-3 所示 可以 选择建立新应用项目 Application 新窗体 Form 等
    图 1-3 New Items 对话框
    2 New Application 选项用于建立一个新项目 并默认为该项目配置一个空白窗体 Form1 和一个新的单元文件 Unit1 如图 1-4 和图 1-9 所示 若当前有 运行且还没 有保存的项目 将会提示用户保存现有的项目 3 New Form 选项用于建立一个新窗体 并默认配置一个空白窗体 Form1 和一个新 的单元文件 Unit1 若当前没有项目 还会创建一个新项目 3
    4 Open 选项打开一个对象 而 Reopen 选项打开最近关闭的项目和模板 5 Save Save As Save All 选项分别对当前对象进行覆盖保存 重新命名保存和保存 全部已打开的文件
    图 1-4 空白窗体
    6 Close 选项用于关闭当前对象 而 Close All 选项用于关闭当前所有的对象 包括模 板和项目等 7 Add To Project 和 Remove From Project 选项分别将一对象加入于一个工程或从一个 工程中删除 8 Print 选项用于打印当前项目 Exit 选项则用于退出 Delphi 5.0 程序开发环境 2 Edit 编辑 菜单 Edit 菜单的功能是对各个工程进行编辑操作 常用操作选项有 1 Redo 选项分别取消或重复上一步操作 但对组件属性修改等操作无效 2 Cut 选项将当前所选内容剪取到剪贴板中选项 所选内容被删除 Copy 选项将当 前所选内容复制到剪贴板中 所选内容不变 Paste 选项将剪贴板中的内容拷贝到当前的插 入点 Delete 选项将当前所选内容删除 Select All 选项可选定当前窗口内的所有组件或程序 3 Lock Controls 选项用于锁定屏幕上各组件的位置 避免对其误操作 但通过对象监 视器 仍然可以修改已被锁定的各个组件 4 其他的选项还包括用以设置当前选定组件尺寸的 Size 选项 将所选组件按栅格对 齐的 Align to Grid 选项 安排组件缩放比例的 Scale 选项等 3 Search 查找 菜单 Search 菜单的功能是实现在代码编辑窗 的定位 查找和替换操作 常用选项有 1 Find 选项查找给定的文本 在弹出的对话框中输入 Search Again 选项用以重复 最近的一次查找操作 但对组件属性修改等操作无效 Find Error 选项定位编辑后出现的第 一个错误的位置 2 Replace 选项用一个给定的新内容取 查找的内容 将当前所选内容剪取到剪贴 板中 所选内容被删除 3 Lock Controls 选项用于锁定屏幕上各组件的位置 避免对其误操作 但通过对象监 视器 仍然可以修改已被锁定的各个组件 4 其他的选项还包括用以设置当前选定组件尺寸的 Size 选项 将所选组件按栅格对 齐的 Align to Grid 选项 安排组件缩放比例的 Scale 选项等 4
    4 View 查看 菜单 View 菜单用来隐藏和显示 Delphi 开发环境的一些窗 项目 常用选项有 1 Project Manager 选项可打开工程 器 2 Project Source 选项用于在代码编辑窗 示工程原 3 Object Inspector 激活对 视器 4 Units 和 Form 选项分别用于激活正在使用的组件单元或窗体 View 菜单中与程序调试有关的选项以及上面提到的工程管理器 编辑窗口等的具体 操作请参看后面相关章节 5 Project 项目 菜单 Project 菜单用来建立和编译应用程序 常用选项有 1 Add to Project 选项和 Remove from Project 选项分别向工程添加现有窗体或从工程中 删除选定的窗体 2 Add to RePository 选项向对 件添加窗体 3 Syntax Check 选项用于在编译前进行语法检查 而不进行链接 4 Compile 选项用于编译当前工程 并链接形成可执行文件或动态链接库 DLL 5 Build All 选项编译当前工程中所有的组件 5 Run 运行 菜单 Run 菜单使用户可以调控应用程序的执行 进行程序调试 常用选项有 1 Run 选项运行用户应用程序 2 Step Over 选项启动单步执行程序 调用子程序或函数操作作为一步执行 3 Trace Into 选项启动单步执行程序 在子程序或函数中也单步执行 4 Run to Cursor 选项使程序运行到光标所在处停止 5 Program Pause 选项暂停执行程序 在观察窗 察变量的值后可以在断点处接着 继续向下执行 7 Component 组件 菜单 该菜单用于在应用程序中添加或配置 ActiveX 组件 请阅读相关专著 8 Data Base 数据库 菜单 该菜单用于对数据库操作 基本用法参看第 8 章第 8.1 节 9 Tools 环境工具 菜单 可帮助用户查看 修改 Delphi 5.0 的环境设置 其中 Environment Options 可进行显示器 调色板 浏览器 编辑器的设置 Configure Tools 可配置 Delphi 5.0 菜单中的工具等 10 Help 帮助 菜单 Help 菜单为随时查询和了解 Delphi 5.0 的语法和结构等提供了良好的支持 其风格与 Windows 等软件的帮助系统的风格相似 掌握求助的方法 养成及时利用在线帮助系统的习 惯 是学好 Delphi 5.0 的重要保证之一
    1.3.2 加速按钮 Speed Bar
    为了使程序开发操作更加简洁 Delphi 5.0 在主窗口中提供了如图 1-5 所示的加速按钮区 其中每个按钮对应一个常用的菜单命令 当鼠标移动到某个加速按钮后 稍稍停留就会显示
    5
    该按钮所对应的菜单命令名称和利用键盘操作时所需要的组合按键符号 帮助用户提高实际 操作能力 实际上 Delphi 5.0 的每一个菜单命令均对应着一个命令图标 在加速按钮区单击右键 并在弹出的快捷菜单中选择 Customize…选项 就可在该选项弹出的窗 进行加速按钮区 显示对象的设置 并通过鼠标拖动 向加速按钮区增加或从加速按钮区删除用户指定的菜单 命令
    New View Unit Open Save Save All Open Project Add File to Project Remove File from Project Help Step Over View Form New Form Toggle Form/Unit Run Pause Trace Into
    图 1-5 加速按钮对应功能示意图
    1.3.3 组件板 Component palette
    组件是建立一个窗体 即用户操作窗 面 如图 1-7 所示 界面的基本元素 例如按 钮 列表 编辑输入框等 对一个组件要用一些特征数据 如标题 宽窄 色彩等 来描述 这些特征数据称为组件的 属性 一个组件在程序运行时会遇到各种激励 如鼠标移动到了 该组件区域 编辑输入 字符 光标移动到了某编辑组件 鼠标在某组件上进行了单击或按 下操作等 这些激励称为组件的 事件 如果某事件发生时要进行对应的处理 就要为该事 件编写事件处理程序 Delphi把从简单的按钮 编辑框 到高度复杂的数据库报表 Windows API Application Programming Interface 等都封装成为一个个的组件 使得具有美观操作界面和强大功能的 Windows 应用程序的设计工作变得相当简单有趣 往往只要依据需要挑选组件添加到窗体上 即可 当然 Delphi 所提供的组件不可能包罗万象 为满足特定的实际要求 可通过给组件 编写程序 来完善和加强其功能 1 组件分类 按照运行期间是否可见 Delphi 5.0 将组件分为可见组件和不可见组件两类 可见组件在 程序运行期间会显示在用户操作界面上 为用户提供程序运行状况报告或接受用户输入信息 是实现计算机与用户进行交互操作的主要工具 一般包括按钮 文本框 下拉列表框等 不 可见组件在程序设计阶段出现在窗体中 但在运行时不可见 不可见组件主要为其他组件或 程序提供服务 比如定时器组件可以使程序周期性地执行某种操作 按照组件操作功能 Delphi 5.0 将组件分成十多类 分别是标准组件 Standard 附加组 件 Additional Windows 组件 Win32 数据库访问组件 Data Access 数据库控制组件 Data Controls 系统组件 System 对话框组件 Dialogs 快速报表组件 Qreport OCX 及范例组件 Samples 等 2 组件板 将组件依据功能类别分别安排到各自的组件页 再将它们排列起来就形成为如图 1-6 所 6
    示的组件板 但图中 a 仅为选中标准组件时的显示形式 若选中了附加组件则显示如图 b 所示 即组件板的下面一行显示了被选中组中的组件按钮
    a
    b 图 1-6 组件板示意图
    在组件板的分类页标行 上面行 的右端设有左右滚动按钮 可滚动查找想要选中的组 件页 用户也能重新分配组件板或给组件板添加自己定义的组件 请阅读参考文献[3] 3 几个基本组件 标准组件页中的按钮组件 Button 标签框组件 Label 编辑框组件 Edit 备注框 组件 Memo 检查框组件 Check Box 和单选钮组件 Radio Button 等将是我们本节编 程举例中要用到的几个最基本组件 其中按钮组件常用来控制程序启动或停止 标签组件 编辑组件和备注组件可以用来输入和输出数据 检查框组件和单选钮组件则常用于控制程序 执行流程的控制
    1.3.4 窗体设计器 Form Designer
    空白的窗体设计器外观如前面所讲的图 1-4 所示 设计应用程序时可以在上面放置按钮 编辑框 单选钮等各种可视化组件 运行应用程序时用户将在该窗体界面上工作 一个应用 程序可能需要多个窗体 设计或运行中的窗体称为当前窗体
    图 1-7 设计中的窗体
    图 1-7 是一个正在设计中的窗体 上面放置了 3 个标签 其中两个分别显示了 欢迎学 习使用 Delphi 5.0 和 和等于 另外一个用于显示求和运算结果 2 个编辑组件 分别 7
    用于输入两个操作数 和 2 个按钮组件 用于控制运算和结束程序运行 在当前窗体上的所 有组件中 只有一个是当前 设计中的组件 简称当前组件 当前组件的边界周围有 8 个 黑色小块 称为操作块 如图 1-7 中的 Button1 运算 按钮组件 拖动操作块可改变组 件的尺寸大小 当鼠标移动到当前组件时按下左键 其边框变为粗黑线 即可拖动鼠标改变 组件位置
    1.3.5 对象监视器 Object Inspector
    对象监视器如图 1-8 所示 它由标题行 对象选单 属性 Properties 页标和事件 Events 页标等部分组成 用于显示当前对象的各个属性值和事件处理情况 单击其对象选单按钮 可弹出当前应用程序中的对象名表列 可在其中选定要显示的对象名称 一旦选中某个对象 名称 它将成为当前对象 窗体或组件 同时 若通过其他命令选中了某个窗体 或选中了 窗体中的某个组件 选中的窗体或组件即成为对 视器中要显示的当前对象 选属性页标时 当前对象的属性将显示在对 视器的属性表列中 如图 1-8 a 在 对象监视器中改写某属性的值也可以改变该对 外观和动作 同时 在窗体中移动组件的 位置或拖动改变其大小时 对象监视器中的对应属性值也会随之改变 选事件页标时 显示当前对象在程序运行时可能发生的事件名称表列和对各事件的处理 情况 如图 1-8 b 所示 该图说明尚未给该组件编写任何事件处理程序 若双击某一事件 即可激活代码编辑器 如图 1-9 所示 进行该组件默认事件处理程序代码的编写 例如对于 按钮 标签等组件 其默认事件时被鼠标点击 即 OnClick
    a 图 1-8 对象监视器
    b
    若在对象监视器上单击右键即弹出其快捷菜单 如图 1-8 a 中所示 可选择其中的选 项对对象监视器的显示内容和效果等进行设置 比如 单击 Stay on Top 选项 则该选项 前就将显示符号 表示对象监视器将保持在屏幕的最前面 当移动其他窗 它不会被覆 8

    1.3.6 编辑器 Code Editor 和代码浏览器 Code Explorer
    编辑器用于程序代码的编写 浏览器则把所有的类型 类 属性 方法 全局 变量 全局例程等组织成为一个树形结构 当打开 浏览器时 它将出现在代码编辑器窗 左半部分 如图 1-9 所示 每当建立一个新工程时 Delphi 5.0 将自动产生一个工程文件 Project* (其中*号 Delphi定义的工程序号) 同时自动建立一个如图 1-4 所示的空白窗体 Form1 并在 编辑 器中为 Form1 安排了如图 1-9 中所示的单元文件结构 该单元文件的默认名为 Unit1 在窗 体中连击某一组件 或连击如图 1-8(a)所示的对象监视器事件名列表中的某一事件 也可以 打开 编辑器 在代码编辑器中编写代码时 可以使用 File Edit 等主菜单项中的相关菜 单命令进行单元文件的存盘 换名存盘 全部或局部内容的拷贝和移动操作等 若工程中包 含多个窗体 就对应有多个单元文件 并将它们在代码编辑器的标题行下面以按钮形式列示 出来 通过对它们的选择可以实现单元文件间的迅速转换
    图 1-9 编辑器和 浏览器
    为了扩大 编辑区的范围 可以单击 浏览器右上角的关闭按钮将其关闭 需要时 可选择主菜单 View 的 Code Explorer 选项 或按下组合键++ 或在 编 辑器单击右键后在快捷菜单中选择 View Explorer 选项激活代码浏览器 在代码浏览器中不但 可以观看当前单元文件的结构 还可以实现文件代码的快速定位 例如双击其中的某个变量 光标就会定位在代码编辑器中关于该变量的 行上
    1.3.7 工程 器 Project Manager
    选择主菜单 View 的 Project Manager 选项可以打开如图 1-10 所示的工程 器 在工程管理器窗口中 显示了当前打开的工程的状态信息和工程文件内容 它从工程文 件 .DPR 中取得信息 并在 File 列表区以树型结构将工程文件的主体内容显示出来 同时 9
    在 Path 列表区显示各个单元文件和窗体文件的存放路径 对包含多个文件的工程文件的管理 非常方便 特别是存在对独立单元文件或对其他工程中的单元文件共享时 这种直观的管理 模式就显得更加有效
    将工程组中的某个工程激活 Delphi 只编译 连接激活的工程 菜单命令也只对激活的工程有效 从工程组删除工程 由于 Delphi 菜单是对应一个工程而非工程组 所以没有对应的菜单命令项 但在 该窗 快捷菜单中有对应的选 项 创建一个新工程 并将其添加到当 前的工程组中 但用此按钮不能添 加程序单元 窗体等从属于工程的 程序元素 该按钮对应的菜单选项 是 Project 菜单的 Add New Project
    图 1-10 工程 器
    在工程管理器中连击某一窗体图标或单元文件名 就可以直接将其打开并进入窗体设计 或代码编写状态 也可以选择并删除某个文件和窗体等
    习    题 
    1.1 简述 Delphi 5.0 的技术优点 1.2 简述 Delphi 5.0 对计算机硬件环境的基本要求和安装 Delphi 5.0 的操作过程 1.3 简述 Delphi 应用程序由哪些文件构成 其中 3 种基本文件各起什么作用 1.4 Delphi 5.0 的开发环境由几个基本部分组成 1.5 简述如何建立一个新工程和新窗体 如何往窗体上放置组件并调整它的大小和位 置 1.6 1.7 1.8 如何利用对象监视器设置组件的属性值 什么是 事件 什么是 默认事件 如何进入 编辑窗口 如何打开和关闭代码浏览器
    10
    第2章
    Delphi 应用程序构成及开发步骤示例
    第 1 章学习了 Delphi 5.0 开发环境的基础知识 本章首先简介 Delphi 5.0 应用程序的构成 然后通过实例介绍 Delphi 5.0 应用程序的开发步骤
    2.1 应用程序的文件构成
    Delphi5.0 应用程序由相关的一组文件构成 文件类型包括工程文件 .DPR 单元文件 .PAS 窗体文件 .DFM Windows 资源文件 .RES 编译连接器开关设置文件 .DOF 等 本章仅简单介绍与程序设计密切相关的工程文件 单元文件和窗体文件
    2.1.1 工程文件 .DPR
    工程文件用于记录和控制应用程序中所有的文件 是每一个 Delphi 5.0 应用程序必须有 的 文 件 当 选 择[file]>[new application] 选 项 或 在[file]>[new…] 选 项 的 对 话 框 中 选 择 Application 或在工程 器窗 按 New 按钮 都可以创建一个新工程 Delphi 5.0 将为新工程建立一个默认名为 Form1 的空白窗体 一个默认名为 Unit1 的单元文件和 一个默认名为 Project1 的工程文件 如第 1 章图 1-2 所示 如果要查看工程文件的内容 可以选择[View]>[Project Source] 选项 也可以先选择 [View]>[Units…]选项 打开如图 2-1的 View Units 窗口 再点选其中的工程文件名 如 Project1 其内容就出现在代码编辑器中 同时 在代码编辑器的标题栏下面增加了一个 Project1 按 钮 如图 2-2 所示
    图 2-1 View Units 窗口
    图 2-2 所示的即是 Delphi 5.0 默认的工程文件内容 若选择[file]>[new form]选项为当前 工程添加一个新窗体 默认名为 Form2 则工程文件内容如图 2-3 所示 在图 2-3 所示工程文件的第一行说明本工程文件的名字为 Project1 其前面的单词 program 称为语句定义符或语句命令动词 其后的分号 表示语句的结束 由于从第二行 起到第五行行尾才有一个分号 所以这四行组成为一个 Delphi语句 前面的 uses 是语句定义 11
    符 说明该工程中有 2 个单元 单元名称分别为 Unit1 和 Unit2 所对应的单元文件名称分别 为 Unit1.pas 和 Unit2.pas 所对应的窗体名称分别为 Form1 和 Form2 第六行是控制编译过 程的编译指示 第七行到第十二行 由定义符 begin 开始 到定义符 end 结束 组成一个复 合语句 该复合语句中包含有四个语句 其功能分别为 执行程序初始化操作 分别创建窗 体 Form1 和 Form2 启动程序执行
    新 加 的 行
    图 2-2 默认的工程文件
    图 2-3 添加新窗体后的工程文件
    比较图 2-2 和图 2-3 可以看到 工程文件的内容是由 Delphi 自动修改维护的 甚至当程 序员将某个单元文件换名存盘 Save As… 时 工程文件中对应语句中的原文件名也会被自 动修改成新的文件名 所以一般情况下程序员不要自行改动它 也不要随便删除它
    2.1.2 单元文件 .PAS
    单元文件是实现一个窗体功能的 Object Pascal 源程序代码文件 它的文件扩展名为 .PAS Object Pascal 语言是以 Pascal 语言为基础发展而来的 它既支持可视化面向对 程 也保留了支持结构化面向过程编程的特色 Object Pascal 源程序代码文件是方便阅读的 文本文件 必须先对其进行编译 连接操作 形成扩展名为 .EXE 的可执行文件后才能脱 离 Delphi 开发环境运行 请参看本章第 2.3 节 当增加一个新窗体时 Delphi 会自动建立一个默认文件名为 Unit*的单元文件 其中 * 为新增窗体的序号 其中给出了描述单元文件基本结构的默认文件内容 用户只需添加必要 的程序代码即可 同时 单元文件开头的有些部分也具有自动维护功能 有效地简化了程序 设计操作 默认单元文件内容参看图 2-4 在增加了 4 个组件后 单元文件内容自动进行了 修改 图 2-5 中直折线所标的部分即为相应自动增加的语句 组成单元文件的基本单位是 Delphi 语句 语句分为说明语句和可执行语句两大类 由若 干个语句可以组成程序区段 程序区段可分为单元区段 整个单元文件 程序区段 若干个 语句 过程区段 完整的过程 函数区段 完整的函数 等 其中能实现某一处理功能的 过程区段和函数区段又被称为方法 依据基本功能 单元文件可分为两个主要构成部分 说明部分和执行部分 1 说明 或 部分 说明部分又分为单元名称说明和接口说明 前者仅为第一句 其开头的 unit 为语句定义 12
    符 后跟该单元文件的文件名 如 Unit1 当单元文件被换名存盘时 这里的文件名会自动 改变为新文件名
    图 2-4 默认的单元文件
    图 2-5 添加组件后的单元文件首部
    接口 interface 说明部分从说明定义符号 interface 开始 其后跟有关于接口说明的各 个区段 1 uses 语句开始的区段说明本单元文件要用到的系统程序库 在系统程序库中存放有 实现组件功能的程序 当用户在窗体上增加新组件时 若组件所属的程序库尚未包含在 uses 语句中 Delphi 就 自动把它添加进来 2 type 语句开始的区段说明本单元文件中所用到的组件 包括窗体 的类型 3 以 private 语句和 public 语句开始的区段分别说明单元中所要用到的私有数据 公共 数据及其方法 私有数据和方法在本单元中被引用 而公共数据和方法能够被其他的单元所 13
    引用 4 var 语句开始的区段说明 Form 对象变量等 2. 执行 Implementation 部分 执行部分包括程序员自己定义的函数和过程 包括窗体上所有对 事件处理 程序 方法 是程序设 主要区域
    2.1.3 窗体文件 .DFM
    窗体文件是一个二进制文件 Binary File 保存了窗体及窗体中所有对象在设计时期由 程序员设定的属性值 程序执行过程通过语句可以修改对象属性值 但不影响窗体文件内容 若要查看窗体文件内容 则将其转换为文本形式 即先用右键点击窗体 再在快捷菜单中选 择[View As Text]选项 即可看到如图 2-6 所示的窗体文件 在该窗体文件中记录了两个对象 在设计时期的属性值 一个是窗体 Form1 本身 一个是设置在窗体上的按钮 Button1 这说明当前窗体上仅仅设置了一个组件对象 若要回到窗体显示状态 可激活快捷菜单并选择[View As From]选项 也可以在 Text 显 示状态 按+组合键
    图 2-6 窗体文件内容示意
    2.2 程序单元的组成元素
    2.2.1 组成程序单元的基本单位 语句
    前面已经介绍了单元文件的说明部分 单元文件的执行部分也由若干个语句组成 区别 在于它主要由执行具体运算操作功能的语句所组成 每个语句都要确定一种运算功能 并由 语句定义符来表征 语句定义符可以是一个或多个英文单词 如 Readln If Then Else 等 14
    并称它们为保留字 也可以是简单的符号 如:= =等 从书写形式上看 一个语句既可以存在于一个文字行 也称为物理行 上 也可以存在 于多个文字行上 它的结束仅以分号 为标志 反之 一个文字行上也可以存在多个语句 两个语句间用分号 分隔 第 3 章对语句进行了较详细的描述
    2.2.2 程序中的基本量和表达式
    为了描述具体的运算操作对象 操作数 就要用到常数 变量 函数 运算符号和表 达式 其中常数 变量和函数称为基本量 表达式则是用 Delphi 定义的运算符号将基本量按 Delphi 的规则组织连接起来的式子 1 常数和变量 常数是指在程序运行过程中其值保持不变的量 依据所描述的对象 Delphi 将常数分为 许多数据类型 如 2356 和 Delphi 5.0 被分别称为整型常数和字符串常数 变量是指在程序运行过程中其值将要变化的量 要使用变量 必须先为其选定一个识别 名称 简称变量名 命名变量名要遵循以下规则 1 可用的字符包括 26 个英文字母 10 个数字字符和下划线符号 _ 2 首字符不能是数字字符 长度在 64 个字符以内 多于 64 个并不算错 只是后面的 字符不再起识别作用 相当于被忽略 且对字母不分大写或小写 3 不能使用保留字 Reserved words 如 If For Begin 等 和 Object Pascal 已经定义 使用的命令字识别符号 如 Tform Tedit 等 来命名变量 4 命名要尽量使之见名识义 即看到名字就知道它代表着什么意义 Object Pascal 的变量也分为许多数据类型 如整型 Integer 实型 Real) 字符型 Char 字符串型 String 等 基本内定数据类型的特性见表 2-1
    表 2-1
    类型类别 数据类型 Integer Integer Shortint Longint Word Single Real Double Real Boolean Char String Boolean Char String
    Object Pascal 内定的数据类型
    存储字节数 2 1 4 2 4 8 6 1 1 <255
    - 45
    取值范围或说明 -32768~+32767 -128~+127 -2147483648~2147483647 0~65535 1.5E-45~3.4E+38 5.0E-324~1.7E+308 2.9E-39~1.7E+38 True 或 False
    单个 ASCII 字符 0~255 个字符
    注 (1) 表中 1.5E-45 为实型常数的指数表达式 即是 1.5 10
    (2) 本节只是简单提出了数据类型的概念 请参阅第 3 章中的详细描述
    不容忽视的是 变量在使用前一定要先说明 以便于 Delphi 依据所定义的类型为其分配 所需要的存储空间 15
    变量说明开始于定义符 var 其后跟有若干个类型说明语句 变量类型说明语句的左端 为变量名表列 多于一个变量名时其间用逗号 , 分隔 右端为变量类型 冒号 既是 两端的分界符号 也是类型定义语句的标识符号 例如以下程序区段 var Value Code: Integer; Name: String; 分别说明了两个整型变量 Value 和 Code 一个字符串型变量 Name 有时为了确保常量值不会被改变 也将其用一个符号名来表示 并称为常量名 说明常 量名由定义符号 const 开始 常量说明语句的标识是用等号 = 将常量值和常量名连接起来 例如 const Pi = 3.14159 ; 说明了常量符号 Pi 所 的常量值为 3.14159 3. 内部函数和过程 为了方便程序设计 Delphi 把求 用的数学运算函数 如三角函数 对数函数等 字符串处理函数和类型转换函数等的程序组织起来 形成内部函数库和过程库 需要时仅需 按语法要求引用它们就能得到运算结果 函数和过程的主要区别是 1 函数可以作为表达式中的一个元素 即可以在表达式中引用函数 例如语句 Y := sin(x)+1; 在其赋值运算符号 := 右端的表达式中就引用了三角正弦函数 sin(x) 该语句将先引用 函数 sin(x) 并返回参数变量 简称参变量 x 的正弦值 再将其返回值与常数 1 相加 然 后将求和结果赋值给变量 Y 而对于过程 必须先引用 再通过引用其参变量使用其引用结果 例如: Val(str x k); Y := x*x-1; 即先通过执行语句 Val(str x k)引用过程 Val 将参数串 str 转换为数值 并由参数 x 返 回 在其后的语句中则通过参变量 x 引用其运算结果 2 函数要返回函数值 所以要设定它的数据类型 而过程不存在数据类型 4 运算符号和表达式 Object Pascal 支持算术 字符 关系 逻辑 集合和@运算等 6 类运算 因此具有 6 类 运算符号和相应类型的表达式 这里先介绍算术运算符号和字符串运算符号及其相应表达式 的基本构成 Object Pascal 的算术运算符号分为一元运算符号和二元运算符号 一元运算符号包括同号运算符 + 和反号运算符 - 运算对象是整型和实型数据 运算结果类型不变 + 运算结果的符号与原数值的符号相等 - 运算结果的符号与原数 值的符号反号 二元运算符号包括加运算符 + 减运算符 - 乘运算符 * 除运算符 / 整除 运算符 DIV 和求余 取模 运算符 MOD 前三个的运算对象是整型和实型数据 运 算结果类型不变 / 的运算对象是整型和实型数据 运算结果为实型 后两种的运算对象 16
    是整型数据 运算结果类型不变 在运算时 一元运算符号的优先级最高 乘除运算符号的优先级为第二 加减运算符号 的优先级为第三 同级运算符号连续出现时遵循自左而右的原则 若有括号 则先括号内 后括号外 由于 Object Pascal 表达式只能以单行形式书写 将数学式改写为 Object Pascal 表达式时 除了要正确选用算术运算符号外 还要合理地使用括号使运算次序符合原数学表达式的要求 表 2-2 是几个数学表达式与对应 Object Pascal 表达式比较的例子
    表 2-2 数学表达式与对应 Object Pascal 表达式的比较
    Object Pascal 表达式 2*sin(x) -y*y (a*b+1)/(d*g-1) ((x*x*x+1) * (y*y- (y-1) * (x+1)))-1
    数学表达式 2sinx-y2
    a ?? b +1 d ??g-1
    {(x3 +1)[y2 -(y-1)(x+1)]}-1
    可以看到 其一 数学表达式中省去的运算符号和函数参数的括号在 Object Pascal 表达 式中必须存在 其二 Object Pascal 只允许用小括号 但允许多层嵌套 Object Pascal 的字符串运算符号只有一个 + 称为字符串连结运算符号 它的运算数 据类型可以是字符型 Char 或字符串型 String 运算结果是字符串型 例如 字符串运 算表达式 abc + ABC 的运算结果是字符串 abcABC
    2.2.3 程序设计的重要技巧
    应用自定义过程和函数
    尽管 Delphi 提供了功能强大的内部函数和过程库 但仍然难以满足实际工程问题的需 要 设计和使用自己定义的过程和函数仍然是提高程序设计效率的重要途径 这里先通过举 例和分析 了解自定义过程和函数的基本方法 更多的内容在第 3 章中继续介绍 过程和函数的区块和单元区块类似 也由说明部分和执行部分两大程序区块组成 且说 明部分的第一个语句也是对过程或函数的名称及参数的说明 主要区分仅在于 1 过程区块开始于保留字 Procedure 函数区块开始于保留字 Function 2 函数必须说明函数返回值的数据类型 事实上 对同一个运算要求 往往既可以设计为过程 也可以设计为函数 下面以将给 定的实型数转换为字符串为例 给出可对比的过程区段和函数区段 1 将给定的实型数转换为字符串的过程区段为 Procedure Real_to_str(Inreal: Real var Resultstr: String); var Int1 Int2: Integer; begin Int1:=trunc(inreal); Int2:=round((Inreal-Int1) *10000); Resultstr:=inttostr(int1)+'.'+inttostr(int2); 17
    end; 其中第一行说明了 Real_to_str 是一个过程名 该过程有两个参数 第一个参变量 Inreal 的数据类型为 Real 型 第二个参变量 Resultstr 数据类型为 String 型 由名字可以分析得知第 一个参变量是输入参数 第二个参变量将返回处理结果 在由变量说明区中说明了只能在本过程中引用的 Integer 型变量 Int1 和 Int2 在由 begin 开始到 end 结束的执行部分中共有三个赋值语句 第一个赋值语句右端表达式中引用内部函数 trunc( )将实型变量 Inreal 的整数部分赋值给 了左端的整型变量 Int1 第二个赋值语句右端表达式中引用了对实型数小数部分四舍五入后取整的内部函数 round( ) 其参数表达式((Inreal-Int1)  *10000)把变量 Inreal 原有的四位小数移动并保留在了整 数部分 再将其小数部分四舍五入后取整 实际是将其原值的第五位小数四舍五入后将其转 换成了整型数 并赋值给左端的变量 Int2 第三个赋值语句右端的字符串运算表达式中两次引用了将整型数转换为字符串的内部 函数 Inttostr( ) 然后通过字符串连结运算把变量 Inreal 原来的实数值组织成了字符串形式的 结果 其中还实现了对其原值四舍五入保留四位小数的操作 并将其赋值给了左端的变量 Resultstr 2 将给定的实型数转换为字符串的函数区段为 Function Real_to_str(Inreal: Real):String; var Int1 Int2: Integer; begin Int1:=int(inreal); Int2:=round((Inreal-Int1) *10000); Real_to_str:=inttostr(int1)+'.'+inttostr(int2); end; 比较可以看到 区别仅出现在函数说明语句和第三个执行语句 在函数区段的函数名说 明语句中不但要说明函数名和各个参数的数据类型 还要说明函数返回值的数据类型 本例 中的函数返回值为 String 型 由于函数值要由函数名保存并实现返回 在函数运算结束前的 第三个执行语句把运算结果赋值给了函数名 Object Pascal 还允许使用默认变量 Result 保存 函数返回值 且无须对变量 Result 作任何说明 即本例中的第三个语句左端可以是变量 Result 它是自动被提供的
    2.2.4 开发程序的得力工具
    可视化组件
    Delphi的重要特色之一就是提供了大量 高效的可视化组件 使得原来十分繁杂 高难 度的应用程序界面设计变得非常简单 快捷 仅需要鼠标一点 一拖 一个窗体 一个按钮 一个文本编辑器 甚至一个复杂的数据报表就会出现在屏幕上 并可以随意改变其大小 色 彩等 可以说 在可视化组件的支持下 能否设计出一个良好的用户操作界面主要取决于你 有多么丰富的想像力 描述组件各种特征的项目称为组件的属性 一个组件可以有数十种属性 例如名称
    18
    Name 标题 Caption 色彩 Color 位置 Top 和 Left 尺寸 Width 和 Height 等 用户在程序运行时 对组件进行的操作或程序在运行时对组件属性产生的影响等被称为组件 的事件 例如鼠标点击 OnClick 内容变化 OnChange 进入 OnEnter 退出 OnExit 鼠标移动 OnMouseMove 等 下面通过对几个标准组件和窗体的简单介绍 加深对上述概 念的理解 1 按钮组件 Button Button 常用于对程序的操作控制 如 运行 停止 等 其主要属性有 1 Caption 属性 其值为显示在 Button 之上的标题文字 如 启动 运算 等 由 于它的取值完全由用户随意确定 称该属性的值为变量类型 2 Name 属性 其值为 Button 在程序中的标识符 如 But1 Key1 等 3 Font 属性 用于设置标题文字的字体 字号 色彩等 因为它有自己的下层次属性 项 该属性属于对 型 并称其为属性对象 在对 视器中点击该类属性名左边的 + 号即可展开下层次属性名表列 如第 1 章中图 1-8 a 所示 4 Visible 属性 取值为 True 时表示该按钮在程序运行时可见 取值为 False 时该按钮 在运行时不可见 由于其取值只有两种选择 称该属性的值为逻辑类型 5 Enable 属性 取值为 True 表示该按钮可用 取值为 False 时不可用 按钮为不可用 时将显示为灰色 6 Cursor 属性 用于设定当鼠标移动到该按钮时光标所变化的图形 其默认值为 CrDefault 表示当鼠标移动到该 Button 时光标仍为系统默认形式 当在对象监视器中选定该 属性 其原值的左边会有一个三角钮 点击三角钮 将弹出可供选择的 19 种属性值的表列 若选择 HourGlass 则当鼠标移动到该 Button 时光标将变为小手掌图形 由于该属性只能在 所给定的有限个值中选择一个取值 称该属性的值为枚举类型 有一些属性 如 Width 和 Height 标识 Button 的尺寸 顶 Top 和左 Left 标识 Button 的位置 这些属性的值将会随着设置 Button 时的移动等操作而自动变化 其他更多的属性将 在后面继续介绍 鼠标点击 OnClick 是操作 Button 组件的主要事件 所以它被设定为 Button 组件的默 认处理事件 即在窗体设计器中连击某一 Button 组件 例如 Button2 Delphi就自动在该窗 体的单元文件中自动增加 OnClick 事件处理程序的过程区段 该过程区段的预定内容如下: procedure TForm1.Button2Click(Sender: TObject); begin | end; 且光标 | 停在 begin 与 end 之间等待输入具体处理程序 在单元文件的说明部分 也自动增加了对过程 Button2Click 进行说明的语句 procedure Button2Click(Sender: TObject); 同时也在对象监视器 Events 页的 OnClick 项的值栏内填写了 Button2Click 2. 标签框组件 Label Label用于显示用户无法更改或指导用户操作的提示性信息 其 Caption 属性的值为显示 在标签框中的文字内容 如 乘数 欢迎 等 其他属性如 Name Font Visible Enable 19
    Width Height Cursor 等和 Button 的同名属性相同 但也有其特别的属性 如属性 AutoSize 设定边界是否会随显示内容增减自动变化 属性 WordWrap 设定在文字输入至右边界时是否 会向左滚动等 标签框组件的默认处理事件也是 OnClick 事件 3. 文本编辑框组件 Edit Edit 组件使用户在程序运行时能输入数据或显示程序运行结果 Edit 只能输入单行数据 最多可达 255 个字符 Edit 的 Name Font Visible Enable Width Height Cursor 等属性与 Button 的同名属 性一样 但它也有描述本身特征的其他属性 例如 它有 Readonly 属性 而 Button 和 Ladel 组件没有 Edit 有 PassWordChar 属性 而上述的其他两个组件都没有 4 窗体 Form Form 是设计和运行程序时的主要操作界面 也是最基本 最常用的对象 Form 具有许 多和前述组件相同的属性 如 Name Font Visible Enable Width Height Cursor 等 也 有相对特殊的一些属性 先将主要的几个属性介绍如下 1 Form 外形设置或控制的属性 l BorderIcon 属性 说明在程序运行时该 Form 标题栏内的显示项目 它有 4 个次层属 性 分别是控制菜单显示属性 biSystemMenu 最大化钮显示属性 biMaximize 最小 化钮显示属性 biMinimize 和求助钮显示属性 biHelp 每个次层属性的值都为逻辑型 由于它的取值是取值为 true 次层属性的集合 所以称其值为集合类型 l BorderStyle 属性 说明在程序运行时该 Form 的边框类型 其值为枚举类型 共有 6 种可能的取值 常取用的是 bsDialog 不可改变大小 标准对话框边框 bsNone 不 可改变大小 且没有边框 标题栏和控制菜单 最大化和最小化按钮 和 bsSizeable 标准的可变大小的边框 l Color 属性 设定用户区的颜色 共有 30 多种选择 其值为枚举类型 l ClientHeight 和 ClientWidth 属性记录除去标题栏和滚动条后用户可见的实际窗口尺寸 2 Form 状态设定或控制的属性 l Enabled 属性 设定 Form 是否有效 取值为 True 时 可以在 Form 上操作 也称 Form 可取得焦点 l FormStyle 属性 设定 Form 格式 有 4 个可选择的取值 其中 fsNormal 表示 Form 为一般格式 fsStayOnTop 表示 Form 总是保持在最前面 不被其他 Form 所覆盖 其值为枚举类型 l WindowsState 属性 记录 Form 当前状态 其值为 wsNormal 表示 状态 其值为 wsMaximized 表示 Form 最大化 其值为 wsMinimized 表示 Form 最小化 3 设定 Form 所用资源的属性 l Cursor 属性和 Font 属性与 Button 的同名属性相同 l Icon 属性 设定当窗体最小化时所显示的图标 4 用于控制 的属性 l ActiveControl 记录当前成为焦点 被选中 的是哪一个组件 l KeyPreview 属性 若其值设为 true 当 Form 上的任一对象有键盘输入时 就会首先 20
    触发 Form 的连带键盘事件 包括 OnKeyDown OnKeyUp OnKeyPress 然后再 将转到该对象的键盘事件处理程序 利用这一点 可以在 Form 的连带键盘事件处理 中进行对输入的过滤 即对键盘输入的信息进行预先处理 除了 Form 的连带键盘事件外 关于 Form 还会有许多事件发生 如 OnClick OnCreate OnClose 等
    2.3 应用程序开发步骤示例
    本节将通过实现简单加法运算的例子 复习已学习过的许多概念 了解 Delphi应用程序 的开发步骤 例 2.1 利用已经学习过的 Delphi 可视化组件 设计一个能够实现简单算术加法运算的 应用程序 步骤见本章第 2.3.1 节~2.3.5 节
    2.3.1 建立窗体 Form
    建立 Form 有如下几种操作方法 1 当启动 Delphi 时 Delphi 将自动建立一个新工程 Project1 同时为该工程建立一个 空白 Form 并在 编辑器中预置了该 Form 的单元文件构架 分别如第 1 章中的图 1-4 和 图 1-9 所示 这也是本操作举例中使用的方法 2 在 File 菜单中选择 New Application 选项 效果和 1 相同 3 在 File 菜单中选择 Form 选项 常用于给当前工程添加新空白 Form 4 在 File 菜单中选择 New …选项 再在弹出的窗 选 Form 图标添加新空白 Form 或在窗口中点击 Forms 页标 再在弹出的窗 选择一个窗体模板图标来添加预订形式的 Form 5 点击 Add to Project 快速按钮 或在 project 菜单中选择 Add to Project 选项 再在弹 出的窗口中选择一个已经存在的窗体添加到当前工程中 本例利用方法(1)建立窗体 Form1 其他方法请在有一定基础后再自行练习应用
    2.3.2 在窗体上加入组件
    在 Form 上加入组件有如下几种操作方法 1 直接在组件板双击选定的组件图标 该组件即以默认大小出现在 Form 中央 例如 双击 Button 图标 则 Delphi开发环境如图 2-7 所示 然后再利用鼠标将其拖动到预订的位置 并可拉动其四周的操作点设定其大小 2 先在组件板单击选定的组件图标 再移动鼠标在 Form 上 要放置的位置 然后 单击该处 组件即以默认大小显示在所选的位置上 3 先在组件板单击选定的组件图标 再移动鼠标在 Form 上 要放置的位置 然后 按下鼠标左键直接将组件拉到所需的大小 在例 2.1 中 我们在 Form1 上加入了 5 个 Label组件 3 个 Edit 组件和 2 个 Button 组件 当 Label和 Button 的 Caption 属性 Edit 的 Name 属性均取用默认值时 则 Form 的外观将如 21
    图 2-8 所示
    图 2-7 加入 Button 组件示意图
    图 2-8 加入组件后的 Form 外观示意图
    2.3.3 设定对象属性
    在 Delphi中通过设定对象属性 可以控制对象显示时的外观特征和执行行为 设定对象 属性既可以在设计时期 Design Time 也可以在运行时期 Run Time 进行 本节先介绍在 设计时期进行属性设定的方法 在运行时期设定属性的方法在第 2.3.4 节中介绍 1 选择 Form 中的组件 要设定一个组件对 属性 必须先选定它 被选定的组件周围会显示选取边框和 8 个 操作点 如图 2-7 中的 Button 组件就处在选定状态 选定组件有如下几种方法 1 单击 Form 上的某个组件 2 点击对象监视器对象选单行右端的三角按钮 再在对象下拉列表中选择对象 22
    3 若要一次选定多个组件 可以先按下 Shift 键 若要在面板组件 Panel 或组合框 GroupBox 内选取多个组件 则要先按下 Ctrl 键 再逐个单击要选的组件 也可以在要选定 的组件外按下鼠标左键并拖动 当虚线框包围所有要选的组件后放开 要注意的是 当单击 Form 上没有组件的地方 即选中了 Form 对象本身 2 设定对象的位置和大小 1 对选定的单个组件 在对 视器的属性列表中直接选择并修改属性 Top Left Width Height 的值 2 移动鼠标到某组件上 按下左键并保持 则选定边框变为黑实线框 再拖动鼠标即 可移动该组件的位置 移动鼠标到已选定组件的某操作点上 当光标变成双箭头时按下左键 并保持 则选定边框变为黑实线框 再拖动鼠标即可改变该组件的大小 3 若选定了多个对象 则在对象监视器的属性列表中只显示它们所共有的属性 用鼠 标拖动其中的一个对象 即可同时移动它们的位置 按下 Shift 键的同时 使用方向键可以同 时改变它们的大小 要对齐一组选定的对象 可以利用 View 菜单的 Alignment Palette 选项或 Edit 菜单的 Align 选项 具体操作请参看 Delphi 的在线帮助 3 设定对象的其他属性 在设计时期 除个别属性外 组件 对象 的大多数等属性都可以利用对象监视器进行 设定 通过程序 则可以设定对 所有属性 利用对象监视器进行属性设定的操作也很简单 例如要设定如图 2-8 所示的 Form1 上 Label1 的 Caption 属性 首先先选定该组件 则对 视器的属性列表中将显示当前的属性 值 如图 2-9 a 所示 再在属性列表中选择 Caption 属性 并在值栏中填写新值 欢迎使 用 Delphi 5.0 对象监视器将如图 2-9 b 所示 这时可以看到 Form 上原 Label1 组件随 之改变的过程 如果感到字体不够美观 则要设定其 Font 属性 选定 Font 属性后的对 视器将如图 2-9 c 所示 点 Font 属性值栏右边的三点按钮 将弹出字体设定的对话框 如 图 2-10 所示 在字体对话框中的字体页 字体样式页 字体大小页和颜色页分别选择幼园 斜体 四号和红色 然后单击确定 则设定字体后的 Form 外观如图 2-11 所示
    a
    b
    c
    图 2-9 设定对象属性中的对 视器
    23
    进行其他对象或进行其他属性的设定 其操作过程基本类似 若按表 2-3 中所列的属性 值对例 2.1 的各个组件进行属性设定 则设定后的 Form 如图 2-12 所示
    图 2-10 字体设定对话框 表 2-3
    组件类别 Button Caption Button Caption Name Label Caption Font Label Caption Label Caption Label Caption Label Caption Edit Text Edit Text Edit Text '' Name '' Ed3 空白 Name '' Ed2 空白 Name ' Ed1 空白 Name Name Name Name Name 组件属性名 Name
    图 2-11 设计中的 Form 例 2.1 中各组件的主要属性值
    设置的属性值 Button1 运算 Button2 退出 Lb1 欢迎使用 Delphi 5.0! 字体 幼园 形式 斜体 字号 四号 颜色 红 Lb2 被加数 Lb3 加数 Lb4 所求和 Lb5 '(分隔线)
    注 1. 表中未给出 Font 属性值的各组件 其 Font 属性值均设为 字体 宋体 形式 规则 字号 12 颜色 黑 2. 各组件的其他属性均采用默认值 3 表中括号内的内容均为说明文字 不属于值的组成部分
    24
    在进行组件添加和组件属性设定的同时 Delphi 将自动跟踪程序员的操作过程 对当前 Form 的单元文件内容和窗体文件内容进行自动更新 在完成组件属性设定之后 点击 编辑器窗口 或按 F12 功能键 或点击快速按钮 Toggle Form|Unit 均可切换到代码编辑器 通过阅读单元文件校对各组件 Name 属性的值非常 重要 因为在程序中 Name 属性的值是组件的惟一标识
    图 2-12 属性设置完成后的 Form
    5 保存 Form 设计结果 为了避免操作失误或计算机故障等造成劳动成果的丢失 及时保存文件永远是重要的 在对例 2.1 中 Form1 上的各个组件属性进行设定的过程中 或设定完成后 首先在 File 菜单 中选 Save All 选项 将依次弹出询问单元文件名和工程文件名的对话框 这时可以为它们重 新命名 也可以直接点击 OK 按钮仍以默认文件名存盘 但是作为课程练习 建议使用有意 义的文件名来重新命名文件 例如 对例 2.1 可分别用 Li21Unit1 和 Li21Project1 命名单元 文件和工程文件 说明它们是例 2.1 的单元文件 1 和工程文件 1 命名存盘后 就可以简单地 点击 Save 快速按钮随时保存当前的单元文件了 为了保证文件的完整 在操作结束前 要选 择 File 菜单中的 Save All 和 Close All 选项 它们将保存和关闭当前工程中的所有单元文件 并提示保存工程文件 将例 2.1 中的单元文件命名为 Li21Unit1 并存盘后 其内容如下 unit li21Unit1; {在命名存盘时 文件名已经自动更新了} interface {接口说明部分开始} uses {说明引用的单元文件库} Windows Messages SysUtils Classes Graphics Controls Forms Dialogs StdCtrls; Type {类型说明部分开始} TForm1 = class(TForm) {说明 TForm1 类型是派生于 Delphi 内部 已经定义过的类型 Tform} Button1: TButton; {说明 Button1 为 Tbntton 类型} Button2: TButton; Lb1: TLabel; Lb2: TLabel; La3: TLabel; 25 {说明 Lb1 为 TLabel 类型}
    Lb5: TLabel; Lb4: TLabel; Ed1: TEdit;
    {说明 Ed1 为 Tedit 类型}
    Ed2: TEdit; Ed3: TEdit; Private {Private declarations } public {Public declarations } end; var {变量说明开始} Form1: TForm1; {说明 Form1 为前面已经说明过的 Tform1 类型} Implementation {执行部分开始} {$R *.DFM} end. {程序单元结束} 其中大括号中的中文内容是编者增加的注释 注释是程序文件的重要组成部分 它不影 响程序文件的执行 仅用来为阅读 理解某个程序区段或某个程序语句的功能提供必要的信 息 Delphi程序中 注释信息用成对的大括号 { } 来标识 一段注释可以出现在一行或多 行上 从左大括号 { 开始 直到右大括号 } 结束 在程序编译时 所有的注释内容均 将被 Delphi编译系统所忽略 仅在文件清单列印时显示出来 帮助程序员理 序功能 在 本课程关于程序设 作业中 要求程序清单中要具有足够多的注释信息 尽管在上述文件清单中增加了注释 也只是使读者在形式上对单元文件有更多的了解 其中涉及到的许多概念 如引用 类型等 仍需要在后面章节中继续学习
    2.3.4 编写事件处理程序
    1. 事件和事件处理 前面已经提到 事件就是可能对某一个对象产生影响的因素 例如对组件 Button 而言 鼠标对其点击 ButtonClick 鼠标进入其区域 OnEnter 鼠标离开其区域 OnExit 鼠 标在其区域内按下 OnMouseDown 等都是可能的 Button 事件 若在某个事件发生时 程 序要完成一个指定的操作 就要为这个事件编写事件处理程序 双击 Form 上的一个组件 就会自动进入该对象默认事件句柄的编辑状态 默认事件是 指某对象的最基本事件 比如 Button 和 Label 对象的 OnClick 事件等 2. 编写事件处理程序 下面为例 2.1 中 Button 组件的默认事件编写简单的事件处理程序 具体操作步骤为 1 双击 Caption 属性值为 退出 的按钮 则 Delphi 自动在 Form1 单元的 Type 部分 增加一行关于该对象默认事件句柄名称 Button2Clickde 说明 procedure Button2Click(Sender: TObject); 其中小括号内说明该程序过程要用到参变量 Sender 且它的类型为 TObject 26
    同时 Delphi 还在单元的执行区增加了该事件处理程序的程序框架 procedure TForm1.Button2Click(Sender: TObject); begin | end; 若我们要求在运行该应用程序时单击该 退出 按钮就能结束程序执行 则仅需在 begin 和 end 之间添加能实现该功能的语句 Close; 即编写好的 Button2Click 事件处理程序 如下 procedure TForm1.Button2Click(Sender: TObject); begin Close ; end; 同理 如果我们想在运行该应用程序时 单击 运行 按钮就能将操作界面上原来显示 的文字 欢迎学习 Delphi 5.0! 改变为 请让我来帮你运算 仅需要在其 Button1Click 程 序区段的 begin 和 end 之间添加语句 Lb1.Caption := '请让我来帮你运算 ' ; 即将组件 Lb1 属性 Caption 的值改为'请让我来帮你运算 ' 其中 Lb1.Caption 称为对 Lb1 组件属性 Caption 的引用 符号 . 表示了两者的父子关 系 即点左为父 点右为子 利用这种形式还可以表述更多级的关系 例如 Font 属性表示显示 Caption 文字内容时所采用的字体 色彩等 即 Font 本身也有自 己的属性 所以 若要在改变显示文字内容的同时 也将文字的颜色由红色改变为绿色 就 要使用语句 Lb1.Font.Color:= cllime ; 其中的 Lb1.Font.Color 就体现了三者之间的两级父子关系 该语句的意义就是将 Lb1 的 属性 Font 的属性 Color 的值改变为 Cllime 添加上述两个语句后的 TForm1.Button1Click 程序 为 procedure TForm1.Button1Click(Sender: TObject); begin Lb1.Caption:='请让我来帮你运算 '; Lb1.Font.Color:= cllime ; end; 这时移动 编辑器右边上的滚动滑快 即可看到当上述两个事件处理程序编写完成之 后的完整单元 清单如下 unit li21Unit1; interface uses Windows Messages SysUtils Classes Controls Forms Dialogs StdCtrls; type TForm1 = class(TForm) Button1: TButton; 27
    Graphics
    Button2: TButton; Lb1: TLabel; Lb2: TLabel; Lb3: TLabel; Lb5: TLabel; Lb4: TLabel; Ed1: TEdit; Ed2: TEdit; Ed3: TEdit; procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button2Click(Sender: TObject); begin close; end; procedure TForm1.Button1Click(Sender: TObject); begin Lb1.Caption:='请让我来帮你运算 ';
    {自动增加的过程说明} {自动增加的过程说明}
    {编译控制信息} {句柄程序 2}
    {句柄程序 1}
    Lb1.Font.Color:= cllime ; end; end. 3. 运行程序 当预订的事件处理程序编写完成后 就可以运行程序 基本操作方法是 1 按 F9 键 2 在 RUN 菜单选 RUN 选项 在运行程序时 Delphi 首先要进行语法检查 若有错误 就给出关于错误性质的提示信 息 并将光标停留在第一个出现错误的位置 以方便程序员进行修改操作 例如 若将上面的语句 Lb1.Font.Color:= cllime ; 错写成了 Lb1.Font.Color:= 'cllime' ; 28
    就将看到如图 2-13 所示的带错误提示信息部分的 编辑器窗口 本例图 2-13 中的提 示内容大意是 字符串常量'cllime'和 Color 属性值的数据类型不匹配 依据提示进行分析 就会比较快地发现 Color 属性值的数据类型为枚举类型 在前面第 2.2.4 节曾简单提到过枚 举类型的定义 所以应该去掉 Cllime 两端的单引号
    图 2-13 语法错误提示窗口
    修改无误后 再运行程序 就会看到如图 2-14 所示的运行时用户操作界面 这时点击运 行操作界面上的 运算 按钮 就会看到如图 2-15 所示的运行结果界面 若点击 退出 按 钮 则返回 Delphi 开发环境
    图 2-14 运行时操作界面
    图 2-15 运行结果界面
    比较图 2-12 和图 2-14 可以看到 设计阶段与运行阶段窗 面的主要区别就是有或者 没有用于组件定位的网格 4. 利用内部函数编写求整数和的应用程序 由上述操作过程 可以看到 在程序设计过程中 Delphi替程序员完成了大量的准备工作 程序员的主要任务就是在给定的过程区段中添加能实现预订操作的处理程序代码 所以 在 了解 Delphi程序基本结构和开发过程之后 就要把注意力逐步转移到如何分配每个对 事 件 如何在事件处理程序中实现所要求的处理功能上来 下面我们仍然在例 2.1 已有的基础 上 继续讨论利用内部或自定义函数和过程编写实现更强功能的处理程序的基本方法 1 确定工作任务 29
    比如我们对例 2.1 应用程序的运行要求是 首先输入整型被加数和加数 并在点击 运 算 按钮后 显示整型 和 若要结束操作 则点击 退出 按钮 2 确定各个组件的功能 l Form 上的 Label 组件仅用于提供必要的操作提示信息 l Ed1 和 Ed2 分别用于输入被加数和加数 l Ed3 用于显示 结果 l Button1 用于控制启动运算 并显示运算结果 l Button2 用于控制程序运行结束 3 编写各相关组件的事件处理程序 l 由于操作提示信息已经在 Form 设计时完成 不再为各 Label 组件编写程序 l 由于各 Edit 组件仅用于输入和输出 只是对其 Text 属性值进行引用或改写 只要注 意正确使用引用形式 Ed1.Text 即可 也没有必要编写事件处理程序 l 由于当 Button1 被点击时要启动运算 必须为其编写 OnClick 事件处理程序 在该程序中要求计算两个任意整型数的和 就要用到三个整型变量 所以要在程序中增 加由定义符 var 开始的变量说明部分 由于 Edit 组件只能接收或显示属于字符串类型的文本数据 所以要引用内部函数 Strtoint(x) 函数 将字符串形式的数转变为整型数 完成整数运算后 还要引用内部函数 Inttostr(x)函数 将整型数转换为字符串 完整的程序代码如下 procedure TForm1.Button1Click(Sender: TObject); var x y z : integer; {说明了三个整型变量 x y z} begin x := strtoint(Ed1.text) ; y := strtoint(Ed2.text) ; z := x+y ; Ed3.Text := inttostr(z) ; {将字符串转换为整型数并赋值给整型变量 x} {求和运算} {将整型数转换为字符串并赋值给属性 Text}
    end; l Button2 的 OnClick 句柄在前面已经编写完成 不需修改 完成各个句柄 设计后 即可运行该应用程序 该程序的运行操作界面如图 2-14 所示 用鼠标点击标识为 被加数 的 Ed1 编辑框 其中会出现输入字符的光标 | 说明该对象 被选中 也称其为操作 焦点 这时就可以利用键盘输入被加数 被加数输入后 再点击标 识为 加数 的 Ed2 编辑框并输入加数 然后 点击 运算 按钮 再标识为 所求和 的 Ed3 编辑框中就会显示出运算结果 此后 可以在修改一个输入数据或同时修改两个输入数 据后点击 运算 按钮 就会 并显示当前两个输入数据的和 直到点击 退出 按钮结 束程序运行为止 5. 改进求整数和的应用程序 由于没有为 Edit 组件编写事件处理程序 即使在输入数据后按了回车键 程序也不会有 反应 但若为 Edit 对象的 OnChange 事件编写了事件处理程序 每当其编辑框中的内容有变 化时就会激活执行它 在例 2.1 中我们采用了句柄共享方式 使 Ed1 和 Ed2 的 OnChange 事 30
    件共享使用 Button1 的 OnClicK事件处理程序 实现即时显示运算结果 具体操作方法是 在 Form 设计器上双击 Ed1 在对 视器的 Events 列表中选择 OnChange 事件 再点 击其值栏右端的三角按钮 并在下拉表列中选择 Button1Click 即设置了 Ed1 的 OnChange 事件与 Button1Click 事件处理程序的关联 当有 OnChange 事件发生时 实际将转去执行 Button1Click 事件处理程序 用同样的方法再设置对象 Ed2 的 OnChange 事件与 Button1Click 事件处理程序的关联 设定共享事件处理程序后的单元文件中并没有 OnChange 事件处理程序区段 为了能正确运行程序 避免引用函数 Strtoint(x)时出错 参变量 x 的值不能是空串 也不 能含非数字符号 在 Form 设计时要将 Ed1 和 Ed2 的 Text 属性值设置为数字符号 如 0 在运行时既不能用退格键将编辑区清空 最好保留一个 0 也不能输入非数字符号 上述改进工作完成后再按 F9 功能键执行该应用程序 只要 Ed1 或 Ed2 的编辑区中内容 有变化 不需要点击 运算 按钮 Ed3 中就会随之显示对当前输入内容的运算结果 如图 2-16 所示 直到点击 退出 按钮结束程序运行
    图 2-16 改进后运行中的操作界面
    6 关于运行程序操作的说明 值得注意的是 按 F9 键运行程序时 Delphi 将监视程序的执行过程 一旦将输入框清 空或在输入框中输入了非数字符号 Delphi 就会给出错误警告 如图 2-17 所示 并将产生错 误的程序单元 显示在屏幕最上层 进入查错状态 对于在程序运行中由于操作或某种意外原因导致的出错 在 Delphi 中被称为 异常 显示错误信息就是 Delphi 系统自行处理 异常 的方法之一
    图 2-17 程序运行时出错的警告窗口
    点击图 2-17 中的 OK 按钮关闭错误警告窗口后 按 F1 键即可以启动 Delphi 的在线帮助 系统寻求出错原因 也可以按 F9 键继续执行程序 或者选择 RUN 菜单的 Program Reset 选 项返回程序设 态 31
    若按 F9 键继续执行程序 这时将再 到另一个关于错误原因的 消息报告框 2-18 所示 点击 确定 关闭消息框后 程序又会回到图 2-16 所示的用户操作界面
    如图
    图 2-18 错误原因的消息框
    为了保证程序 运行而要求 在操作时既不能将输入框清空 也不能输入非数字符号 是很不友好的 要使用户少受或不受限制 程序就要有自动处理这些 错误 的能力 所以 在后续章节中 如何使程序具有错误处理能力也是我们要学习的重要内容
    2.3.5 编译和运行应用程序
    设计 Delphi 工程的最终目的是生成一个可独立运行的可执行文件 即*.exe 文件或 Dll 动态连接 文件 将工程中的代码文件等转换为可执行文件的操作是由 Delphi 的编译和连 接程序完成的 编译程序将用户编写的程序 翻译成为一系列的目标块文件 连接程序再 把目标块文件组织成为可执行文件 要编译当前正在设计中的工程 比如 Li21project1 可选择 Project 菜单的 Compile Li21project1 选项 当然其中的工程名会随当前工程名的改变而改变 也可以直接按组合键 Ctrl+F9 编译完成后 Delphi 将给出编译报告 如图 2-19 所示
    图 2-19 编译完成报告窗口
    为 了 编 译 正 确 在 编 译 之 前 最 好 要 先 选 择 执 行 Project 菜 单 中 的 Syntax Check Li21project1 选项对工程进行语法检查 或利用 F9 键完成了程序运行 编译成功之后 选择 Project 菜单中的 Build Li21project1 选项即可实现对工程的连接 连 接完成后 Delphi 将给出连接报告 如图 2-20 所示 在该报告窗口的上部还给出了该工程文 件 Li21project1.dpr 的存放路径 要改变默认的编译和连接参数 可以选择 Project 菜单的 Options 选项 并在其对话框 中选择设定可执行程序的存放路径 可执行程序的图标或图案等 要进行该项操作 请先阅 读有关参考文献
    32
    图 2-20 连接完成报告窗口
    在完成工程的编译和连接并形成可执行文件后 比如 Li21project1.exe 就可以脱离 Delphi 开发环境独立运行了 甚至可以在 Windows 桌面建立它的快捷方式 直接点击运行 在例 2.1 中 经编译连接生成可执行文件 Li21project1.exe 之后 即可关闭 Delphi 然后 利用 Windows 的浏览工具 我的电脑或资源管理器工具 找到该文件 并双击运行它 就会 直接显示图 2-16 所示的操作界面 值得注意的是 这时即使将输入框清空 也仅仅给出如图 2-18 所示的消息框 并且在点击该消息框的 确定 按钮关闭它之后 程序会继续执行 说 明 Delphi 已经为该程序设置了错误处理能力
    习    题 
    2.1 简述组件和组件属性 常量和变量 语句和程序 事件和事件处理等的基本概念 2.2 改写例 2.1 的相关文件 重新命名存盘 实现整数的乘法运算 记录利用 F9 键执 行程序时的操作过程 2.3 记录语法检查 编译和连接你设计的工程的过程 并在关闭 Delphi 后执行它
    33
    第3章
    Object Pascal 程序设计入门
    在前面两章的学习中我们可以看到 Delphi 为我们提供了功能强大的可视化组件 使我 们可以方便地完成漂亮的用户操作界面设计 同时 我们也更应该看到 要使 机能够在 某个按钮被点击时 按我们的要求完成一定的数值运算和数据处理操作 就必须为它编写相 应的事件处理程序 简称编程 Object Pascai 语言就是 Delphi 所采用的基本编程语言 所谓编程 就是利用编程语言来描述求 个具体工程问题的方法和步骤 这些求解工 程问题的方法和步骤也被称为工程算法 从本章开始 我们将通过利用 机求 些常见 事例的具体过程 学习算法和编程的基本概念 并通过实现这些算法的应用程序的设计过程 更深入地了解 Delphi 的可视化组件及其基本组件的应用方法
    3.1 Object Pascal 的基本字符集和表征符
    任何一种语言都有自己的一套符号和语法规则 程序设计语言也是如此 从根本上讲 程序就是由具有一定意义的符号按照本语言所规定的语法规则顺序排列而成的指令 在 Object Pascal 语言中 符号由一个或多个字符所组成 是最基本的语言元素 被允许 参于形成符号的所有字符组成了 Object Pascal 的基本字符集
    3.1.1 Object Pascal 的字符集
    Object Pascal 使用 ASCII 字符集 包括 1 26 个英文字母的大写和小写 即大写字母“A”到“Z”和小写字母“a”到“z” 共 52 个 2 10 个阿拉伯数字 “0”到“9” 3 23 个其他标准字符 包括 “# ” “ $” “ &” “ '” “ (” “ )” “ *” “ +” “ ,” “?” “ .” “/” “:” “;” “<” “=” “>” “ @” “[” “ ]” “^” “{” “ }”
    3.1.2 Object Pascal 的表征符
    在 Object Pascal 中 表征符可以分为 特殊符号 special symbols 标识符 identifiers 保留字 reserved words 指令字 directives 数字 numerals 标号 labels 字符串 character strings 和分隔符 在各类表征符中 除字符串外 表征符中的字母没有大写和小写的区分 1. 特殊字符 特殊字符是指有固定意义的字符或字符的组合 包括基本字符集中除字母和数字外的 23 个标准字符 以及由他们组成的字符组合 “(*” “(.” “  *)” “.)” “..” “//” “:=” “<=” “>=”和“<>” 2. 标识符 标识符是用来表示常量 变量 域 类型 特性 过程 函数 程序单元 库和包等语 法成分的名称 标识符只能以字母或下划线 “_”开头 第一个字符之后可以跟数字 字母或 34
    下划线组成的字符序列 但不能包含空格 标识符的长度没有限制 但只有最前面的 255 个 字符有效 之后的字符虽被保留 但不再起标识作用 例如 A K_1 MyCup yourpen CalculateValue Calculatevalue 和 CALCULATEVALUE 都 是合法的标识符 但后面的三个被认为是同一个标识符 而 9A A&B My friand KEY 等都是非法的标识符 因为第一个用数字开头 第二个使用了非字母 非数字和非下划线 的 & 第三个含有空格 第四个使用了基本字符集之外的字符 3. 保留字 保留字是指 Object Pascal 已经定义了特殊意义和用途的表征符 如 And begin end 等 保留字不能被重新定义或用作其他用途 附录 A 列出了所有的保留字 4 指令字 指令字是指在 Object Pascal 中具有特殊意义的表征符 它们与保留字的不同之处是 指 令字只在特殊的程序位置 或当上下文关联时有意义的程序区段有自己特殊的意义 而在其 他场合 用户可以对其重新定义或用作其他用途 例如 在对 型定义时 private protected public published 以及 automated 等有其特殊的意义 类似保留字 但在其他地方可以用作一 般标识符 但最好不要这么做 要查阅所有的指令字 请看附录 B 另外 还有一些附录中没有列出的符号 如“at”和“on” 也具有特别的意义 不能随便 用作一般标识符 5 数字 在 Object Pascal 中数字用不带空格或逗号的数字序列来表示 并可以在前面加上符号+ 或-来表示正负号 没有符号的数就认为是 基本的写法如下 1 整数表示法 135 或+135 -2347 67258 或+67258 等 数值范围为+2147483647 到-2147483648 2 十进制实数的一般表示与其数学表示相同 如-123.341 123.0 或+123.0 等 3 十进制实数的指数表示法 是用数字中的字母 E 或 e 表明它是以 10 为底的指数表 示形式 其后跟的一位或两位整数表示 10 的指数 其前面的实数称为该数的尾数 其中 指数部分表示了数的表示范围 尾数则表示了数的表示精度 如 12.25e+6 或 12.25e6 对应的数学式为 12.25 106 而-12.25E-15 对应的数学式为 -12.25 10- 15 用一般表示法 4 字节的实数能表示的数值范围为+2147483647 到-2147483648 若用指 数形式 4 字节的实数能表示的数值范围的绝对值甚至为 1.5×10-35 ?? 3.4×1038 4 十六进制数的表示法,是用作为前缀的美元符号 $ 开始 后跟十六进制数 例如 $88AF $a23b7 等 十六进制数的表示范围为$00000000 到 $FFFFFFFF 将其转化为二进制 后 最左边的位 数的符号 0 表示正 1 表示负 6 标号 标号可以是 1 到 9999 之间的整数形式 也可以是一般标识符 例如 10, 8008, Pro2 等都 是合法的标号 标号主要和 Goto 语句配合使用 用于控制程序执行流程 7 字符串 字符串又称为串常量 是用一对单引号括起来的一串字符 在字符串中允许出现扩展 ASCII 字符集中的所有字符 在中文操作系统支持下 字符串中也允许使用汉字 但是对于 字符串中的控制字符等 要用特定的表示形式
    35
    1 若字符串中用到了单引号 就要在单引号的位置连续输入两个单引号 例如 字符 串 'It's a pen.',所表示的实际文字内容是 It's a pen. 2 字符串中允许以符号“#”开头 后面紧跟一个 ASCII 的形式表示一个字符 例 如 #65#66#67 它等价于 'ABC' #89#111#117 等同于'You' 但如果要在字符串中加入控 制字符 还要采用另一种插入形式 例如 'Line 1'#13#10'Line 2' 表示把回车换行符插放在 Line1 和 Line2 之间 必须注意的是 在用#跟 ASCII 形式表示的多个字符之间不能有空格存在 而在用 单引号括起来的字符串中 空格字符是有意义的字符 8 分隔符 用于分隔 Object Pascal 表征符的符号称为分隔符 分隔符一般为空格 它可以出现在任 何两个相邻的标识符 保留字 数字或标号之间 分隔符也可以是程序的注释或行结束符号 但它们仅能用于语句行之间的分隔 按 Object Pascal 语法要求 两个表征符之间并不一定需要有分隔符 例如程序 Size:=20;Price:=10*MyPay; 是两个语句的合法表示形式 但为了增加程序的可读性 往往将其写成以下形式 Size := 20 ; Price := 10 * MyPay ; 9 注释 注释是为了增加程序的可读性而添加的内容 在 Object Pascal 程序中 注释有两种表示 形式 1 程序清单上的一行若用符号 // 开始 则本行上的所有内容均为注释内容 2 用一对大括号 { } 或一对组合符号 * * 括起来的内容为注释内容 用这种 形式可以将多行内容表示为注释内容 但其中不能再含有{ } (* 或*)符号 一般情况下 编译程序对注释内容不做处理 即注释内容并不影响程序的执行 但当{ } 或 * * 所括的内容以符号 $ 开始 则表示它是编译命令 编译程序将按照编译命令的 要求对其后的程序 进行编译操作
    3.2 基本数据类型
    3.2.1 数据类型的概念
    数据类型用于确定数据在计算机中的存储方式和能够对它进行的操作 例如 Real 类型 (实型) 确定了一个包括零和在绝对值区间 5.0 10-324 1.7 10308 上取值的实数的集合 确定了每个实数可表示为 15 或 16 位的有效数位 确定了每个实数在 机中要占用 8 个字 节的存储空间 确定了对它可以进行的各种实数运算等 Object Pascal 是一种强类型的语言 即它对数据类型的定义 声明以及数据赋值和传递 操作等都制订有严格的语法规则 因此 掌握好数据类型的概念是设计好程序的关键 Object Pascal 支持丰富的数据类型 基本可分为简单类型 字符串类型和结构类型三大 类 我们先介绍前两类数据 结构类型数据的基本内容将在后续章节中结合应用予以介绍
    36
    3.2.2 简单类型
    Object Pascal 的简单类型数据包括有序类型和实数型 1. 有序类型 有序类型的值域是不连续的 有限的 每个取值之间存在严格的顺序关系 即在有序类 型中 每个元素值均有一个 的序号 且除第一个元素外 任何一个元素都有一个惟一的 先行数 除最后一个元素外 任何一个元素都有一个惟一的后继数 Delphi 中的有序类型包 括整数类型 字符类型 布尔类型 枚举类型和子界类型 对于整数类型 每个元素的序号 就是该整数的本身 而对于除子界类型外的其他有序类型 第一个值的序号是 0 第二个值 的序号是 1,并依此类推 在比较大小时 前面的元素小 后边的元素大 1 整数类型(Integer) 在 Object Pascal 中的整数类型分为基本整数类型(Fundamental type) 和一般整数类型 (generic type) 基本整数类型包括 Shortint Smallint Longint Byte 和 Word 等 基本类型的存放格式 和取值范围与 机的 CPU 和操作系统无关 如表 3-1 所示
    表 3-1
    数据类型 Shortint Smallint Longint Byte Word Longword
    基本整数类型的取值范围及存储格式
    存储格式 8-bit 16-bit 32-bit 8-bit 16-bit 32-bit
    取值范围 -128~+127 -32768~+32767  -2147483648~+2147483647  0~255 0~65535 无符号   无符号 无符号
    0~4294967295
    一般整数类型包括 Integer 有符号整数 和 Cardinal 无符号整数 两种 具体取值范 围和 机操作系统和 CPU 有关 例如在 Windows3.1 系统中 它们分别相当于基本整数类 型中的 Smallint 和 Word 而在 Windows98 系统中 它们分别相当于基本整数类型中的 Longint 和 LongWord 一般整数类型是最常用的整数类型 因为它可以充分利用 CPU 和操作系统的特性 适用于整数类型的运算包括 5 种算术运算 + 加 - 减 * 乘 DIV 除 MOD 取余 和 6 种关系运算 = 等于 <> 不等于 < 小于 <= 小于等于 > 大于 >= 大于等于 算术运算的意义与一般数学运算的意义相同 仅运算符号的形式有所不同 关系运算是 比较两个同类型元素的大小 相等关系的运算 对于有序类型 就是比较它们的序号 序号 大者为大 序号相同者为相等 当比较运算式成立时比较结果为 True 真 ,否则为 False 假 例如比较运算式 5>3 的值为 True 比较运算式 15>23 的值为 False 2 字符类型 Char 在字符类型中 Object Pascal 定义了两种基本字符类型 AnsiChar 和 WideChar 还定义了 37
    一种一般字符类型 Char AnsiChar 的顺序对应于 Ansi 字符集 扩展的 ASCII 字符集 每个字符占一个字节的存 储空间 WideChar 的顺序对应于 Unicode 字符集 每个字符占两个字节的存储空间 Unicode 字符集的前 256 个字符与 Ansi 字符集相同 Char 类型目前一般对应于 AnsiChar 但在某些 机中或后续的版本中也可能对应于 Unicode 字符集 因为 Unicode 字符占用两 个字节 可以有 65536 种不同的取值 能表达现 界上 机中使用的所有的字符 包括 图形符号和用于出版业的特殊符号等 适用于字符类型的运算有前述的 6 种关系运算 3 布尔类型(Boolean) Boolean 数据类型的常量只有 True 和 False两个值 但 Object Pascal 共定义了 4 种 Boolean 数据类型 分别是占 1 个字节的 Boolean 和 ByteBool 占 2 个字节的 WordBool 和占 4 个字 节的 LongBool 其中 Boolean 是最常用的 另外三种类型主要是为了与其他程序设计语言和 机操作系统的兼容 适用于布尔类型的运算包括逻辑运算 AND 逻辑与 OR 逻辑或 NOT 逻辑非 XOR 异或 和前述的 6 种关系运算 其中逻辑运算将在后面第 3.4 节中给予介绍 4 枚举类型 Enumerated 枚举类型是由用户自定义的简单类型 是用有限个标识符来表示一组连续的整数常数 在类型定义时就要列出该类型所有可能的值 目的是为了增加程序代码的可读性 定义枚举类型的语句结构为 type <枚举类型名> = 标识符列表 ; 例如 Type TPColor = (Red,Yellow,Blue) 定义了一个枚举类型 Color 它共有 3 种可能 的取值 Red Yellow 和 Blue 分别对应了序号 0 1 和 2 这 3 个标识符都可以作为常量使 用 定义枚举类型时 同一个枚举元素不能出现在多个枚举类型中 也不能直接用枚举类型 中的元素参加运算 枚举类型最常见的运算是赋值运算 具体应用见后面章节 5 子界型 Subrange 子界类型也是用户自定义类型 用于表示某种有序类型 如整数 布尔 字符及枚举型 等 在某一确定范围内的值 这种有序类型称为子界类型的宿主类型 当要限制一个变量的取值范围时可使用子界类型 定义子界类型 必须说明区间的最大 值和最小值 语法结构为 type <子界类型名> = <常量 1> ..<常量 2>; 其中两个常量 称为上界和下界,可以是表达式 必须是同一种有序类型 且第一个常数 必须小于或等于第二个常数 例如 type Tday = Sun .. Sat; 子界类型具有宿主类型的运算特性 但运算结果要在子界类型所确定的取值范围内 2. 实数类型(Real) 实数类型是带有小数部分的数值 严格地讲 由于 机中存储和运算数据时所用的数 位有限 所以这里的实数和数学意义的实数还是有区别的 它无法表示数轴上所有可能的点 实数类型主要满足科学 对高精度 大数值的要求 Object Pascal 预定义了 6 种不同的 Real 38
    数据类型 它们的表示范围 精 等特性见表 3-2
    表 3-2
    数据类型 Real Single Double Extended Comp Currency
    实数类型数据特性
    取值范围 有效位 15 ?? 16 7??8 15 ?? 16 19 ?? 20 19 ?? 20 19 ?? 20 存储字节 8 4 8 10 8 8
    5.0×10-324 ?? 1.7×10308 1.5×10-35 ?? 3.4×1038 5.0×10-324 ?? 1.7×10308 3.6×10-4951 ?? 1.1×104932 -263 +1 ?? 263 -1 -922337203685477.5808 ??922337203685477.5807 
    其中 Comp 类型实际上是 64 位二进制整数 但它不具有整数类型的某些运算特性 例 如递增或递减运算等 小数点位置固定的 Currency 类型则适合于货币计算对精度的要求 适用于实数类型的运算包括 4 种算术运算 + 加 - 减 * 乘 / 除 和前述的 6 种关系运算
    3.2.3 字符串类型
    1. 字符串类型的分类 字符串类型属于简单结构类型 在 Delphi 中提供了 3 种类型的字符串 分别是 ShortString 短字符串 AnsiString 长字符串 和 WideString 宽字符串 各字符串类型的主要特性 见表 3-3
    表 3-3
    类型名 ShortString AnsiString WideString 可用长度 0??255 个字符 0??231 个字符 0??230 个字符
    字符串类型数据主要特性
    存储字符类型 AnsiChar AnsiChar WideChar 所需存储空间 2B ?? 256B 4B ?? 2GB 4B ?? 2GB 结 尾
    不用字符 Nul 结束 用字符 Nul 结束 用字符 Nul 结束
    其中 ShortString 类型主要用来与 Delphi 和 Borland Pascal 的早期版本相兼容 AnsiString 类型的定义是动态分配的 内容由 AnsiChar 类型的字符组成 长度仅受可用 内存空间的限制 以空字符 Nul 作为字符串的结尾 Delphi 可视化组件的所有特性 事件均 使用 AnsiString 来传递参数 简化并统一了 VCL 可视化组件库 和 API Windows 应用程 序 之间的接口 为了编程的方便 Delphi 提供了 String 字符串类型 它既可以是 ShortString 类型也可以 是 AnsiString 类型 缺省定义是 AnsiString 类型 使用$H 编译命令可以改变该项缺省定义 即当在程序中使用编译命令{H-}时 String 缺省定义是 ShortString 类型 当在程序中使用编 译命令{H+}时 String 缺省定义是 AnsiString 类型 但如果在定义变量时指明了长度 最大 为 255 则 String 为 ShortString 类型 例如 var str1:string; str2:string[20]; 定义了默认长度 动态分配 的 string 类型变量 str1 和类型为 ShortString 的定长字符串 39
    变量 str2 ShortString 字符串所占用的字节数为其定义长度加 1 增加的一个字节放置于串首 序号为 0 用于记录串中实有字符个数 所以 本例中 str2 占用 21 个字节 2 字符串的运算 1 字符串的基本运算 字符串的基本运算为+ 连接 运算 即实现字符串的连接 例如 字符串运算表达式 'abc'+'ABC'的运算结果为 'abcABC' 2 关系运算 在进行两个字符串的比较时 是从两个字符串的首字符开始 逐个比较相对位置上的两 个字符的类型序号值 一旦不相同 则停止比较 并判定大序号字符所在的字符串为大 另 一个为小 若两个字符串的长度和相应位置上的字符完全相同 则称两个字符串相等 若某 字符串的前 M 个字符和另一个字符串相等 则该字符串为两者中大串 3 其他运算 其他运算包括子串检索 子串复制等 一个字符串中存在的若干个连续的字符称为子串 包含该子串的字符串称为它的母串 Delphi 提供了大量对字符串操作的函数和过程 请注意 后续章节中的介绍 另外 字符串中的字符也能够通过它们在串中的位置编号进行引用或赋值 例如语句 str2[12] := str2[15]; 即通过引用 str2 串中的第 15 个字符改写其第 12 个字符位置上的原有字符
    3.3 常量 变量与基本库函数
    3.3.1 常量
    常量 即在程序的执行过程中其值不能改变的量 在 Object Pascal 中常量也具有确定的 数据类型 例如 2.73 3.14 等为实型常量 100 608 为整型常量 Hello world! 为字符 串常量等 常量的表示方式有两种 一种是常量值本身 也称为字面常量 如数字 字符串 常量 表达式 如 1000-1 1.25/23.5 等 Delphi 预定义的常量 True False Nul 等 另一种是要 用 定义的标识符表示的常量 即要在定义部分先行 也称为 常量 声明常量又可以分为纯常量 true constant 和类型常量 两者在形式上是一样的 但使 用规则和目的却不相同 1 纯常量 纯常量是由先行 定义的标识符表示的常量 其值在定义后永远不会改变 声明纯常 量的语法规则为 const <纯常量名> = <常量表达式> 其中 常量名要符合 Pascal 标识符的命名规则 常量表达式则是一个在程序编译期间就 能计算出结果的表达式 例如 const Message =' Out of memory '; const ErrStr = 'Error:' + Message + '.'; 40
    const MyPI = 3.14159 ; Delphi 根据常量的值来判断常量名属于哪种类型 例如 因为 3.14159 是实数 MyPI 是实数类型 'Out of memory'是字符串 Message 是字符串类型 而常量 ErrStr 的定义式右端 为字符串常量表达式 因此它也是字符串类型 并且在该字符串常量表达式中引用了常量名 Message 说明常量名一经定义 就能被引用 2. 类型常量 类型常量用于保存数组 记录 过程以及指针等类型的值 类型常量不能出现在常量表 达式中 在缺省的编译器状态 即{$J+} 下 类型常量的值可以改变 这时类型常量更像初 始化过的变量 但当在程序中加入编译命令{$J-}时 则类型常量的值在运行期就无法改变 此时 类型常量才是真正的常量 声明类型常量的语法规则为 const <类型常量名>: <类型> = <常量值> 其中常量名要符合 Pascal 标识符的规则 类型是除了文件型和可变型之外的所有类型 常量值可以是一个和类型相应的常量表达式 例如 const Max: Integer = 100;
    3.3.2 变量
    变量是在运行时可以改变其值的量 为了表示和引用变量就要用标识符来命名它 并称 为变量名 变量必然有它的数据类型 因为类型确定了它的取值范围 运算方法和对存储空 间的要求等 因此 所有的变量都要遵循先 后引用的规则 1. 基本类型变量的 变量声明包括两部分 变量名和它所属的类型 由于 Object Pascal 对基本数据类型已经 给予了预定义 基本类型变量声明的语法规则比较简单 var <标识符表列> : <基本类型> ; 其中 基本数据类型包括整数类型 实数类型 字符类型 布尔类型和字符串类型 标 识符表列也就是变量名表列 当其中的标识符多于一个时 标识符间要用逗号 分隔 另 外 还允许在一个保留字 var 的下面 多个或多种类型的变量 例如 var AChar: char; Astr1 Astr2,Astr3 : String; 共 了 4 个变量 一个字符型变量 Achar 和 3 个字符串变量 Astr1 Astr2 和 Astr3 在 变量时还可以为其指定一个初值 例如 var I3: Integer = 7; 在 整型变量 I3 的同时为其指定了初值 7 在 变量时指定初值可以用和变量类型 一致的的常量表达式 声明变量时还要注意 尽管仅要求变量名是符合 Pascal 关于标识符规则的任意字符组 合 但尽量使定义的标识符便于记忆和阅读 也是要注意培养的好习惯 例如 用 Day Name 和 Total 分别命名变量来表示天 姓名和总数比用 X Y 和 Z 更好 2 自定义类型变量的声明 由于定义变量要涉及其数据类型 所以 定义自定义类型变量和定义基本类型变量的不
    41
    同之处就是要先在程序的 Type 区段声明自定义类型 例如 type TMycolor = ( Red,Blue,Green,Yellow); TYear = 1900..2100 ; var Color1 : TMycolor ; MyYear : TYear ; 即在 Type 区段中 了一个枚举类型 Tmycolor 和一个子界类型 Tyear Tmycolor 中包含有 4 个元素 Red,Blue,Green 和 Yellow 对应序号分别对应于 0 1 2 和 3 而子界类型 MyYear 的取值区间为[1900 2100] 在 var 区段中定义了变量 Color1 和 MyYear 并分别被指定为 Tmycolor 类型和 Tyear 类型 必须注意的是 类型说明仅仅是表明程序将要用到的某些类型的数据 只有将某一个 定的变量和某一个 的数据类型对应起来 变量才具有了该数据类型的特性 并依据其数 据类型获得存储空间 使该变量具备了被引用的基本条件 在上例的 var 程序区段中 就是通过 Tmycolor 类型和变量 Color1 的关联 Tyear 类型和 变量 MyYear 的关联完成了变量 Color1 和 MyYear 的定义
    3.3.3 常用库函数 Function 和过程 Procedure
    为了简化程序员的编程工作 在 Delphi的软件系统中提供了大量的预定义函数和过程 并称为库函数和过程 也称为标准函数和过程 只要通过简单的引用或调用 就能够完成相 当复杂的工作 例如式子 y + sin(x) - sqrt(z) + 1 中就引用了求变量 x 的三角正弦值的 sin 函数和求变量 z 的平方根的 sqrt 函数 并将求得的 函数值带回到式子中参与与变量 y 和常量 1 的运算 下面仅依据课程教学内容的要求 分类简单介绍库函数中的一小部分 更多的库函数和 [6] 过程的介绍请参看附录 C 和参考文献 1. 数值运算函数 Delphi的数值运算函数包含了常用的数学函数 如三角函数 对数函数等 和适合 机数据处理的其他函数 如求数组中的最大值 求三角形的斜边长等 由于数值运算函数引 用比较简单 仅以表格形式说明 见表 3-4
    表 3-4
    引用形式 abs(x) arcTan(x) cos(x) sin(x) Pi sqr(x) 参数类型 整数或实 数 实数 实数 实数 无 函数值类型 同参数类型 实数 实数 实数 实数 实数
    常用数值运算函数
    函数功能描述 求 x 的绝对值 整数表示所有整数类型 实数表示所有实数类型 求 x 的反 值 求 x 的余弦值 求 x 的正弦值 返回常数 的值 3.1415926535897932385 求 x 的平方
    续表
    42
    引用形式 sqrt(x) Power(x,y) ln(x) log10(x) exp(x) Frac(x) Int(x) Round(x) Trunc(x) Odd(x) Random[(x)] Randomize
    参数类型 实数 实数 实数 实数 实数 实数 实数 实数 实数 整数 整数 无
    函数值类型 实数 实数 实数 实数 实数 实数 实数 Int64 Int64 布尔 不定 无
    函数功能描述 求 x 的平方根 要求 x 0 求表达式 xY 的幂 求 x 的自然对数 要求 x>0 求 x 的常用对数 要求 x>0 求数学表达式 ex 的值 返回 x 的小数部分 返回 x 的整数部分 舍去小数部分 返回 x 的整数部分 对小数部分进行四舍五入处理 注意 若 x 的整数部分超出 Int64 的表示范围 则结果不 ' 返回 x 的整数部分 舍去小数部分 注意同上 当 x 为奇数时返回 True 当 x 为偶数时返回 False 当省略参数时 返回一个在区间[0 1]上的随机实数 当使用有 参数 x 时 返回一个在区间[0 x]上的随机整数 它是一个使随机数发生器初始化的标准过程 Randomize 调用形式为
    2. 字符串操作标准函数和过程 为了描述方便 本书后文中将用汉字 串 表示 String 类型 常用的字符串操作标准函数和过程如下 l 函数 UpperCase(x) 将参数串 x 中的小写字母全部转换为大写字母并返回 l 函数 LowerCase(x) 将参数串 x 中的大写字母全部转换为小写字母并返回 l 函数 CompareStr(x,y), 区分大小写字母比较参数串 x 和串 y 返回值为整数类型 当 x = y 时返回 0 当 x > y 时返回的值大于 0 当 x < y 时返回的值小于 0 l 函数 CompareText(x,y), 不区分大小写字母比较参数串 x 和串 y 返回值为整数类型 当 x = y 时返回 0 当 x > y 时返回值大于 0 当 x < y 时返回值小于 0 l 函数 Length(x), 求参数串 x 中的字符个数 即求串长度 返回值为整数类型 l 函数 Concat(x1,x2(,x3),x4…))), 连接所给参数串, 即运算式子 x1+x2+x3+ … 返回值 为串 注 参数表列中的( )表示可选择内容 这里就表示参数表列中允许有多个参数符号名 每个符号名之间要用逗号分隔 对这种表示法后面不再 l 过程 Insert(s1,s2,n), 将参数串 s1 插入到参数串 s2 中 插入点由整数类型参数 n 确 定 若插入后的串长度大于 s2 的定义长度 则尾部多余的字符被舍去 过程必须先调用 再利用其参数引用运算结果 例如 若 s1 和 s2 已定义为 String 类型 则如下 3 个语句 s2 := '123456789'; Insert( 'abc',s2,4); s1 := s1 + s2 ; 执行后 s2 的值为 '123abc456789' 即从参数 4 指定的位置将串 'abc' 插入到 s2 中 调用时其目标参数 本例为 s2 必须为变量 调用后 运算结果由参数 s2 返回 并可以被 引用参与后续程序的运算 如本例中的第 3 个语句 引用 s2 参与运算并为 s1 赋值 l 函数 Copy(s,n1,n2), 取子串 其参数 s 为串 n1 和 n2 为整数 返回从串 s 的第 n1 43
    个字符起 包括第 n1 个字符 取出的 n2 个字符 若取不够 n2 个字符 则取到 s 的 尾字符为止 若 n1 的值为 0 或大于当前 s 的串长度 则返回空串 如 copy( '12345',3,2) 的引用结果为 '34' copy( '123',5,2)的引用结果为 ''(即空串) l 过程 Delete(s,n1,n2) 在参数串 s 中的第 n1 个字符起 包括第 n1 个字符 删除 n2 个字符 若不够 n2 个字符 则删除到原末字符为止 若 n1 的值大于原串 s 的串长 度或为 0 则不删除 s 的任何字符 l 函数 Pos(s1,s) 求参数串 s1 在参数串 s 中的起始位置 即返回值为整数 若串 s 中 不包含有串 s1 则返回值为 0 l 函数 Trim(s) 清除参数串 s 头部与尾部的空格字符 返回值仍为串 l 函数 TrimLeft(s) 清除参数串 s 头部的空格字符 返回值仍为串 l 函数 TrimRight(s) 清除参数串 s 尾部空格字符 返回值仍为串 还有许多利用指针类型 本书将在后面给予极其简单的介绍 的变量作参数的字符串操 作函数 请参阅相关参考文献和本书后面的举例 3. 字符类型和数值类型间的转换函数 在现 理和信息处理系统中 字符信息和数值信息具有同等重要的地位 为了方便存 储和运算 实现字符信息和数值信息之间的转换已经是十分普遍的操作 Delphi 也提供有大 量实现字符信息和数值信息之间转换的标准函数和过程 现简介如下 l 函数 FloatToStr(x) 将实数参数 x 转换为串型值 并返回 l 函数 FormatFloat(s,x) 按参数串 s 所指定的格式串的要求 将实数参数 x 转换为串 型值 并返回 s 可取的基本形式与函数返回结果的对应关系如下 当 s 取值 ' ' 即格式串为空串 将 x 保持原样转换为串 并返回 当 s 取值 '0' 先将 x 的小数部分四舍五入 再将其整数部分转换为串 并返回 当 s 取值 '0.00' 或 '#.##' 先将 x 四舍五入保留两位小数 再将其转换为串 并返回 即由格式串中小数点后面 '0' 或 '#' 的个数 转换结果中的小数位数 当 s 取值 '0.00E+00' 或 '#.##E+00' 即表示要按数值的指数形式进行转换 且其中的尾 数部分为 1 位整数 两位小数 即将 x 四舍五入保留 3 位有效数位 s 可取用的其他格式请参看 Delphi 的在线帮助 l 函数 IntToStr(x) 将整数参数 x 转换为串型值 并返回 l 过程 Str(x:Width[:Decimals],s) 将实数参数 x 转换为串型值 并由参数串 S 返回 其中整数 Width 和 Decimals 可省略 分别确定转换结果的串长度 也称为场宽 和其中的小数位数 但当 x 的整数数位小于给定数位时 前面用空格字符补足 当 x 的整数数位大于给定数位时 转换结果的长度将不受 Width 的限制 l 函数 Chr(x) 返回以整数参数 x 0 l
    4
    l l l l l
    数参数 k 所确定的整数值 过程 Val(s,x,k) 将参数串 s 转换为数值 并由数值参数 x 返回 x 可以为整数类型 或实数类型 当转换不能继续时 由参数 k 返回使转换不能正常进行的字符的位置 已完成的转换结果仍由 x 返回 参数 k 必须为 Integer 类型 例如 若各变量均已经正确定义 则下面程序区段 str1 := '1234.567' ; str2 := '123e-10' ; str3 := '123a234' ; Val(str1,x,i); {执行结果 x 的值为 1.234567e+3,i 的值为 0} Val(str2,x,i); {执行结果 x 的值为 1.23e-8,i 的值为 0} V al(str3,x,i); {执行结果 x 的值为 123,i 的值为 4} 时间操作函数 函数 Now 返回当前的日期和时间信息 函数 date 返回 TdateTime 对象 其中含有年 月 日信息 函数 Time 返回 TdateTime 对象 其中含有时 分 秒信息 函数 dateToStr 将对象 TdateTime 所含的年 月 日信息转换为串值 函数 TimeTostr 将对象 TdateTime 所含的时 分 秒信息转换为串值 应用举例如下 Edit1.Text := ‘目前的日期为 ’ + DatetoStr(Date); Edit2.Text := ‘目前的时间为 ’ + TimetoStr(Time); 若执行上述两个语句 将会在 Edit1 和 Edit2 的编辑框内分别看到如下格式的信息
    目前的日期为 00.9.2 目前的时间为 19:26:12
    除以上介绍的 4 类标准函数和过程外 还有文件操作类 磁盘 类以及内存管理类函 数和过程 其中文件操作类函数在第 4 章中将有简单介绍和应用
    3.4 基本运算符和表达式
    在学习了各种数据类型以及常量 变量和函数之后 接着就需要学习如何将它们组织成 为能符合我们 要求的表达式 因此就需要更进一步了 类运算符及其他们之间的运算 规则
    3.4.1 算术运算符及算术表达式
    1. 算术运算符 算术运算符分为二元运算符和一元运算符两类 需要两个操作数的运算符是二元运算 符 有+ - * / DIV 和 MOD 共 6 个 只要求一个操作数的运算符是一元运算符 有+和 -共 2 个 表 3-5 和表 3-6 列出了分别列出了它们的操作 操作对象和结果数据类型
    45
    表 3-5
    操作符 + - *  /  DIV MOD 操作 加 减 乘 除 整数除 求余数
    二元运算符
    操作数据类型 结果数据类型 integer real integer real integer real real integer integer real
    integer real Integer real integer real integer real integer integer
    表 3-6
    操作符 + - 操作 保持原数符号不变 改变原数符号 即求负
    一元运算符
    操作数据类型 integer real integer real 结果数据类型 integer real integer real
    注意 1 在二元运算中 x/y 的结果类型总是 real,若 y=0 将产生运行异常 2 DIV 只对整数进行运算 且在 x DIV y 中 y 不能为 0, DIV 两边的空格不能省 运算 结果是 x/y 所得值的整数部分 小数部分被舍去 3 MOD 运算是求两个整数数相除后的余数 且在 x MOD y 中 y 不能为 0, 运算结果 的符号与 x 的符号相同 MOD 两边的空格不能省 4 在算术运算符中 一元运算符的优先级高于二元运算符 在算术二元运算符中 * / DIV 和 MOD 的优先级高于+和- 在同级运算符连续应用时 运算过程为自左而右的规则 若有括号 则括号内优先于括号外 可以看到 这和数学中的运算优先级安排基本一致 但 运算符号的表示形式有些不同 括号也只有小括号一种 2 算术表达式 为了完成预定的工程计算任务 算术表达式必然要和某个数学式对应起来 就是要利用 上述算术运算符将数学式改写为符合 Object Pascal 语言语法要求的算术表达式 这要考虑三 个问题 一是语法 二是优先级 三是类型 例如有下列数学式 y 2x ?? y 5x 2.3 + 3 1 2 3 lg 4.5 + 0.3ln 4.5 4 xz x 3/5 若变量 x y z 已经被定义为实数类型 且已经赋值 则上述数学式对应的 Object Pascal 算术表达式如下 1 y/(x*z) 2 (2.0*x-y)/x 3 Log10(4.5)+0.3*Ln(4.5) 4 (5*Power(x,2.3)+3.0)/(3.0/5.0) 可以看到几点 1 数学式中省略的运算符和表示函数参数的括号必须添加上去 因为在程序中 y*z 和 yz ln4.5 和 ln(4.5)等具有完全不同的意义 46
    2 必须注意优先级的处理 若表达式 y/(x*z)和(2.0*x-y)/x 中不用括号 即写成为 y/x*z 和 2.0*x-y/x 的形式 则对应的数学式显然不一样 3 必须恰当利用标准函数 如 只能用函数 Power(a,b)来表示指数运算 ab 4 必须注意数据类型 如 3.0/5.0 为实数除 结果为 0.6 而为整数除 结果为 0 若 将上面第 4 式中的分母写成 (3 DIV 5) 程序运行时就将引起用 0 做除数的错误 当然 几个简单例子只能使读者对算术表达式有一个简单认识 还有许多应该注意的事 项 比如类型的兼容性和如何保证运算精度等 需要在上机实践中继续探讨学习
    3.4.2 逻辑运算符
    逻辑运算符可分为布尔运算符 位运算符和关系运算符 1. 布尔运算符 布尔运算符只能对两个布尔型操作数进行运算 结果仍为布尔型 即只能为 True 或者 False 基本的布尔运算符有 4 个 NOT AND OR 和 XOR 分别简单介绍如下 1 NOT 是求 非 运算符 为一元运算符 操作数的相反数 如有 NOT a 则若 a 的值为 Ture 则运算结果为 False 若 a 的值为 False 则运算结果为 Ture 2 AND 是求 与 运算符 为二元运算符 如有 a AND b 当 a 和 b 的值均为 True 时 运算结果为 True 反之运算结果为 False 若定义开关闭合和灯泡亮为 True 则 与 运算中的两个操作数可以比作为电路中两个 串接的开关 如图 3-1(a)所示 即只有两个开关都闭合 灯才会亮 3 OR 是求 或 运算符 为二元运算符 如有 a OR b 当 a 和 b 的值有一个为 True 运算结果就为 True 反之只有 a 和 b 的值都为 False,运算结果才为 False 仍定义开关闭合和灯泡亮为 True,则 与 运算中的两个操作数可以比作为电路中两个并 接的开关 如图 3-1(b)所示 即只要有一个开关闭合 灯就会亮
    图 3-1

    或 运算示意图
    4 XOR 是求 异或 的二元运算符 如有 a XOR b 当 a 和 b 的值不同时运算结果为 True 反之当 a 和 b 的值相同时运算结果为 False 2 位运算符 位运算符也称逻辑运算符 是对 Integer 类型操作数的二进制形式的位执行操作 表 3-7 列出了 Object Pascal 的位运算符的操作 操作数类型和结果类型等 表中的 位 是指二进制的 bit 按位运算是指不考虑相邻位的运算结果 仅计算两 个操作数中的对应位 例如 1 若有操作数 x 其值的二进制数形式为 00100011 执行语句 y := NOT x; 后 y 值的二进制数形式为 11011100 47
    表 3-7
    操作符 NOT AND OR XOR SHL SHR 操作举例 NOT x a AND b a OR b a XOR b a SHL b a SHR b 操作数类型 integer integer integer integer integer integer 结果类型 integer integer integer integer integer integer
    位运算符
    功 能 说 明 即按二进制形式将每位求反 即 1 变为 0 0 变 1 将两者相对应的位进行 AND 运算 同为 1 时结果为 1 将两者相对应的位进行 OR 运算, 同为 0 时结果为 0 将两者相对应的位进行 XOR 运算,两者不同时结果为 1 将 a 的二进制值向左移动 b 位 左移一位相当于乘 2 将 a 的二进制值向右移动 b 位 右移一位相当于除 2
    2 若操作数 x 和 y 的二进制数形式分别为 00100011 和 11101110 执行语句 z := x AND y; 后 z 值的二进制数形式为 00100010 3 若执行 124 SHR 2 运算 由于常量 124 的二进制形式为 01111100 则运算的结果为 00011111,即十进制数 31 124/4 的结果 值得注意的是 右移操作时原值的低位丢失 高位补 0 左移操作时原值的高位丢失 低位补 0 例如 执行运算 35 SHR 2 SHL 2 即先把数 35 右移 2 位 再把其结果左移 2 位 但是结果 将是 32 而回不到 35 执行运算-35 SHR 2 结果将是 1073741815 这是由于在计算机内部 负数是以补码形 式存放的缘故 关于补码 请参看参考文献[1]中 机中数的编码 一节 3 关系运算符 关系运算符用于比较两个同类型量的值 共有 6 个 见表 3-8
    表 3-8
    关系符 = <> < > <= >= 操作 等于 不等于 小于 大于 小于等于 大于等于
    关系运算符
    结果类型 Boolean Boolean Boolean Boolean Boolean Boolean
    操 作 数 类 型 简单类型 字符串或可变类型 类 类引用 指针 集合类型 简单类型 字符串或可变类型 类 类引用 指针 集合类型 简单类型 字符串或可变类型 简单类型 字符串或可变类型 简单类型 字符串或可变类型 简单类型 字符串或可变类型
    注意 运算符= <> <=和>=也用于集合运算 当操作数为集合时它们的意义如下 假设 A B 是两个集合 若 A 和 B 所包含的元素完全相同 则 A = B的运算结果为 True, 否则 A<>B 的运算结果为 True 若 A 的每个元素也是 B 的元素 A<=B 的运算结果为 True, 并称 A 是 B 的子集 若 A>=B 的运算结果为 True 则 B 是 A 的子集 4 布尔表达式 布尔表达式由布尔运算符和布尔类型的操作数所组成 所以布尔表达式中的操作数可以 是任何运算结果为布尔类型的表达式 包括关系运算表达式和运算结果为布尔类型的函数[如 48
    Odd(x) FileExists(x)]等 但位运算的结果是整数类型 不能直接作为布尔操作数 利用布尔表达式可以描述比较复杂的判定条件 例如 要判定实数变量 x 的值是否落在 闭区间[3 5]上 可用布尔表达式 (x>=3.0) AND (x<=5.0) 来描述 要判定实数变量 x 的值是否落在闭区间[3 5]之外 可用布尔表达式 (x<3.0) AND (x>5.0) 来描述 下一节学习运算优先级的概念后 可以知道 上式中的括号是不能省的
    3.4.3 运算符的优先级
    除了以上介绍的运算符外 还有指针运算符 集合运算符 类运算符和取地址运算符等 其中有些运算符的应用已经超出了本书的内容范围 但所有的运算符和算术运算符一样 都 具有优先级的概念 在 Object Pascal 中 运算符的优先级可以分为 4 个级别 其中第 1 级别 优先级最高 第 4 级别优先级最低 见表 3-9
    表 3-9
    级别 1 2 3 4 @ 取地址 运 算 符 NOT - 一元运算符 乘法及类型强制转换运算符 加法运算符 关系 集合成员及类型比较运算符
    运算符的优先级
    分 类 描 述
    *, /, DIV, MOD, AND, SHL, SHR, AS +,-, OR, XOR  =, <>, <, >, <=, >=, in, is
    在学习了运算符的优先级之后 我们可以总结出考虑表达式中各操作数参与运算的顺序 要依据下述的三条基本原则 1 在两个操作符之间的操作数首先参加优先级别高的运算 例如 4+5*6,由于其中运 算符*的优先级高于运算符+ 所以先 5*6 再将其积与 4 相加 2 相等优先级的运算符连续应用时 运算按从左到右的顺序进行 例如 5+6-4,因为 运算符+和-的优先级相等 而+在-的左边 因此先计算 5+6 再将其和减去 4 3 括号内优先于括号外 例如 5* (6+4),因为 6+4 在括号内,因此先行运算 另外 在表达式中若有函数 则先引用函数 再将其函数值参与式子运算
    3.5 程序的顺序结构
    3.5.1 语句的基本概念
    在第 2 章的应用程序举例中 我们已经了解了语句的基本构成和功能 即语句是由语句 命令动词 或称语句定义符 一般都属于保留字或特定符号 和表示操作具体内容的表达式 当为简单系统控制语句时 可以没有表达式部分 所构成 语句的功能是控制程序执行或 控制编译系统的工作 按语句的作用时间 可分为说明语句和可执行语句两大类 说明语句包括单元说明语句 类型说明语句 变量说明语句 过程说明语句 函数说明 49
    语句 程序区段标识语句等 说明语句仅在程序编译期间起作用 告诉编译系统如何进行编 译操作和程序在运行需要用到的 机资源 比如要引用哪些标准函数和过程 要重新定义 哪些函数和过程 要定义多少个常量和变量 都属于什么数据类型等 而在程序开始执行后 这些语句一般不影响程序的运行 可执行语句包括赋值语句 运行控制语句 结构控制语句等 可执行语句则只有在程序 开始运行后 才能被依次执行 完成指定的运算或控制程序的执行流程等 按语句的描述形式 可分为简单语句 结构语句和复合语句 简单语句只含有一个语句 定义符或特殊标志 结构语句往往含有多于一个的语句动词 复合语句则是由 begin 和 end 括起来的若干个简单语句 结构语句和复合语句 即允许复合语句多层嵌套 但是要特别注 意 begin 和 end 的配对 一般情况下 常采用缩格的方法表示程序语句和结构层次 即将同一个结构语句的各个 子句或一对 begin 和 end 的开始字母写在同一列上 以方便程序阅读和查错操作 为了突出算法和程序设计方法 各类语句均将在后续章节中结合程序举例逐步介绍
    3.5.2 赋值语句
    1. 赋值语句的语法格式 赋值语句的语法格式是 <变量名> := <表达式> ; 赋值语句由变量名开始 后跟赋值符号 := 在 := 的右端是一个表达式 其结果的 数据类型必须和 := 左端变量的数据类型兼容 最后以分号 结束 2. 类型兼容 所谓类型兼容 数据类型不完全相同的量之间所能进行的运算和赋值操作 例如 若变 量 Value 被定义为 Real 类型 变量 Int1 被定义为 Integer 类型 则如下赋值语句 Value := 12 + 10 DIV 3 ; 将右端表达式的整数结果 15 转换为 Real 类型并赋给 Value,即符合赋值兼容 但语句 Int1 := Value ; 不符合赋值兼容 不能通过编译 要先利用 Round 或 Trunc 函数将其转换为整数再赋值 Int1 := Round(Value) ; 又如 运算符 两边既可以是实数类型或整数类型 也可以一个是实数类型 另一 个是整数类型 均符合运算类型兼容 但运算符 DIV 的两个操作数必须是整数类型 不 存在和其他类型的运算兼容 实际上 在表 3-5 中已经给出了算术运算中可能的兼容关系 3. 利用赋值语句给对象属性赋值 除了给一般变量赋值外 对象属性赋值也是程序运行时的重要操作 由于属性总是归属 于对象才有实际意义 所以引用属性时用符号 . 来连接表示其隶属关系 如组件 Edit1 的 文本属性表示为 Edit1.Text 其字体的颜色属性表示为 Edit1.Font.Color 给对象属性赋值的语句举例如下 Label.Caption := '输入加数' ; Edit1.Text := ' ' ; { 相当于将 Edit1 的文字编辑框清空} 50
    Edit1.Font.Color := clred ; { clred 为系统定义的表示红色的常量符号} Edit1.Font.Size := 24 ; { 定义 Edit1 所显示的文字的字号} Button1.Enabled := False ; { 使 Button1 无效 即显示灰色 不再响应点击} Button1.Enabled := False ; { 使 Button1 无效 即显示灰色 不再响应点击} Button1.Visible := False ; { 使 Button1 运行时 在操作界面上不显示} Button1.Visible := True ; { 使 Button1 在运行界面上有显示} 实际上 所有对 所有属性 都可以通过赋值语句在程序运行中随时改变其现行值 而且设定新值后的效果会立即在用户操作界面上表现出来 或对程序运行即时产生影响 4. 过程调用语句 在前面第 3.3.3 节中 我们已经学习了几个标准过程和调用它们的方法 这里再配合赋 值语句进一步了 程调用前后的参数处理操作 若变量 str1 已被定义为 String 类型 变量 x1 已被定义为 Real 类型 变量 n1 已被定义为 Integer 类型 则如下程序区段中各语句所完成的操作由注释说明 str1 := 'These are is samples.'; {串 str1 赋值 These are is samples.} Delete (str1,11,3); {调用过程 Delete,删除 str1 串中的多余字符' is'} Edit1.Text := str1 ; { 显示执行删除操作后的内容 These are samples.} x := 34.56183 ; {为变量 x 赋值} str( x:8:2,str1); {调用过程 str 将 x 值转换为长度为 8 其中 3 位小数的串} Edit2.Text := str1 ; {显示转换结果 34.562,在数码 3 前补有 2 个空格} str1 := '1234,56' ; Val(str1,x,n1); {调用过程 Val将串转换为数 可避免产生运行异常} str(x:8:2,Str1); {再将 Val转换的结果变为串} Edit3.Text:= 'x='+str1+' n1='+Inttostr(n1) ; {显示结果 x=1234.00 n1=5} {其中 n1 的值为 5 说明 Val过程转换到第 5 位字符时无法继续下去} 在上述程序段中 进行了多次过程调用 可以看到 在调用过程前其输入参数 如过程 Val的参数 str1,过程 str 的参数 x 等 要赋有 的值 在调用过程后 要及时引用其输出参 数 如过程 Val 的参数 x 和 n1,过程 str 的参数 str1 等 所返回的处理结果 否则过程在下次 被调用时 前次调用的处理结果就将会丢失 5. Close 语句 Close 语句关闭单元文件所属的窗体 若该窗体是主控窗体 则结束应用程序执行
    3.5.3 程序顺序结构和赋值语句应用举例
    由若干个赋值语句 过程调用语句或其他简单语句形成的程序段称为程序的顺序结构 即顺序结构中的语句都是被依次顺序执行的 不产生程序流程的其他转移 下面是程序顺序结构和赋值语句的应用举例 例 3.1 利用 Button Edit Label 组件和 showmessage 消息框完成求任意三角形面积的 应用程序 提示 已知三边 a b c 求三角形面积的公式为 A = s(s ?? a)(s ?? b)(s ?? c) 其中 s = (a+b+c)/2 51
    步骤为 1 分析题意 确定算法 所谓算法就是解题的方法和步骤 在有限个步骤内能求的 结果的称为有效算法 否 则只是解题过程 设计和选择算法要考虑两个问题 第一是便于用程序设计语言描述 第二 是保证高效率 在本例中 公式 也称数学模型 已经给出 即基本算法问题已经解决 剩下的工 作就是设计描述它的程序 要描述一个算法 首先就是安排变量 由于本例涉及到了 3 个输 入量 三角形的 3 个边长 1 个输出量 所得的三角形面积 共 4 个变量 分别为它 们命名 a b c 和 area 依据一般计算要求 它们均应为实数类型 另外为了描述算法需要 往往还要安排若干保存中间结果的变量 本例就需要一个实数类型的变量 s 2 选择编程工具 基本算法分析和变量分配之后 就要选择可用的编程工具 由于本例已经确定了要使用 的组件工具 就要对它们有所了解 其中对于组件 Button Edit 和 Label 在第 2 章中已有关 于它们的介绍和应用举例 可以参考 这里仅将 showmessage 消息框的功能及应用说明如下 showmeassage 消息框是 Delphi 常用的对话框之一 是一个在系统 Dialog 单元中预定义 了的标准过程 其在系统的 Dialog 单元中的声明语句为 showmessage(const Msg : string) ; 在该声明语句中 Procedure 即为过程声明语句的语句定义符 showmeassage 即被声明 的过程名 括号内为该过程形式参数 Msg 的说明 const 说明 Msg 的值在调用过程中不能改 变 且可以为常量表达式 而 : 号后的 String 则指明 Msg 为 String 类型 调用 showmessage 过程时 将弹出一个显示有参数 Msg 的值的小 窗口 例如执行语句 showmessage 欢迎您 朋友 将会弹出如图 3-2 所示的消息框 点击该窗 的 OK 按钮 窗口 就会关闭并回到调用它以前的运行状态 了解了各选用的组件功能后 就要安排它们在程序中的工作内容 图 3-2 消息框 比如可以安排 3 个 Edit 组件分别用于输入 3 个边长 安排 2 个 Button 组件分别控制开始计算和程序结束 安排用 showmessage 来报告每 结果 安排 2 个 Labe 组件分别显示本应用程序的标题和简单的用户操作说明 当然在安排使用这些组件时 还要考虑它们的应用特性 比如 Edit 组件所能接受的是 字符串类型 也称文本 数据 要用它输入数值数据 就要用到能将串转换为数值的标准函 数或过程 同理 showmessage 过程的参数类型也是串 要用它报告数值计算结果 就要用 到能将数值转换为串的标准函数或过程 当然 也可以自己定义函数或过程来实现特殊的类 型转换要求 3 设计窗体界面 在编程工具 之后 就可以开始设 体界面 创建新工程和新窗体的操作步骤在第 2 章中已有介绍 可以参照例 2.1 中描述的操作步骤 发挥个人的美学想象能力 完成窗体界 面设计 本例的窗体界面设 果可如图 3-3 所示
    52
    图 3-3 例 3.1 的窗 面设计效果
    需要再次强调的是 对于初学者 应把注意力集中到几个关键属性的掌握上 现将本窗 体上各组件的主要属性值列出在表 3-10 中 其他属性值 除大小 位置属性为窗体设计时用 鼠标拖动和放置组件操作的结果外 均采用默认值
    表 3-10
    组件 属性名 Name Label1 Caption Font Name Label2 Caption Font Name Edit1 Text Font Name Edit2 Text Font Name Edit3 Caption Font Name Button1 Caption Font Name Button2 Caption Font 属 性 值
    例 3.1 中各个组件的属性值
    设 计 操 作 说 明 在对 视器属性页状态下 点击属性名 在其右边的属 性值栏中输入该属性值 对 Font 属性 要先点击属性值栏右端标有三点的按钮 再
    Label1 求三角形面积 隶书 规则 小二 Label2 请在输入 3 个边长后点击 运算 按钮 隶书 规则 小二 Edit1 0 宋体 规则 四号 Edit2 0 宋体 规则 四号 Edit3 求三角形面积 宋体 规则 四号 Button1 运 算
    在其对话框中选择设定各子属性
    设计操作说明同 Label1
    设计操作说明同 Label1
    设计操作说明同 Label1
    设计操作说明同 Label1
    设计操作说明同 Label1 另外 选择不同的英文字体 对中文的显示效果也有影响
    默认英文字体 12 号 Button2 结 束 设计操作说明同 Button1
    默认英文字体 12 号
    4 编写程序 从上述窗体外观的设计过程来看 窗体外观的设计还是比较轻松的 但要使用户通过对 53
    窗体组件的操作完成预定的任务 关键的工作仍是编写程序代码 依据前述对各组件的用途安排 其中要求点击 Button1 时开始读入数据并进行 点 击 Button2 时则结束程序运行 所以本步骤仅需为两个 Button 组件编写 OnClick 事件处理程 序 为了能逐步加深对编写程序代码的过程的认识 先介绍对 Button2 的 OnClick 事件处理 程序的编写过程和应该了解的一些概念 在设 窗体上连击 Button2 组件 或在选择 Button2 后 在对 视器的事件页状态双 击 OnClick 事件的值栏 将转入代码编辑器状态 由于系统在连击 Button2 的时候已经在单 元文件的声明部分加入了事件处理程序的过程名 语句 procedure Button2Click(Sender: TObject); 并把插入点 光标 放置在预定的该事件处理程序框架中的保留字 begen 与 end 之间 即初 次进入某事件处理程序编辑状态的 编辑器窗 图 3-4 所示
    图 3-4 初次进入 编辑状态时的代码编辑器窗口示意图
    该程序框架的第一行 procedure TForm1.Button2Click(Sender: TObject); 称为过程体 语句 其后跟有过程程序的可执行程序 其中的 Tform1.Button2Click 是完整的过程名 它的 Tform1.部分说明了 Button2Click 过程是属于 TForm1 的 其参数 Sender 为 Tobject 类型 而 Tobject 类型是 Delphi 最基本的类型 所有的其他类型都源于 Object 类型 所以参数 Sender 可以接受所有类型的数据 请参看第 5 章 点击 Button2 的目的是结束程序运行 且本例仅一个窗体 仅在光标处加入语句 Close ; 就十分简单地完成了 Button2 的 OnClick 事件处理程序的编写 同理 在窗体上连击 Button1 组件转入和图 3-4 类似的代码编辑器状态 同时在单元文 件的声明部分自动加入了事件处理程序的过程名 语句 procedure Button1Click(Sender: TObject); 并把插入点 光标 放置在程序框架的 begen 与 end 之间 只是其第一行改成为 procedure TForm1.Button1Click(Sender: TObject); 由于本事件处理程序要完成所要求的三角形面积 就需要在过程名 语句和 begin 之间添加变量说明部分 然后再在 begin 和 end 之间编写程序的可执行语句序列 完成后的 Button1Click 事件处理程序过程如下 procedure TForm1.Button1Click(Sender: TObject); {过程体 语句} var a,b,c,s,area:real; str1:string ; {定义了 5 个实数变量和 1 个串变量} 54
    begin {可执行程序代码开始} a := strtofloat(edit1.text); {将 Edit1 中的文字串转换为实数 并赋值于 a} b := strtofloat(edit2.text); {将 Edit2 中的文字串转换为实数 并赋值于 b} c := strtofloat(edit3.text); {将 Edit3 中的文字串转换为实数 并赋值于 c} s := (a+b+c)/2 ; {按计算公式要求 中间结果并赋值于 s} area := sqrt( s* (s-a) * (s-b) * (s-c) ); {按计算公式计算面积并赋值于 area} str( area:8:3 , str1 ); {调用 str 过程将 area 的值转换为串并由 str1 返回} showmessage( '面积为 ' + str1 ); {调用 showmessage 过程 报告 结果} end; {本过程体结束} 其中注释部分为特意添加的每个语句的功能说明 以求方便对程序的理解 5 编译运行程序 在上述程序编写完成后 要先保存窗体和程序设 果 当然在窗体设计过程中 为了 保存你的劳动成果 可随时执行该步骤操作 可以选择 File 菜单的 Save All 选项 系统会 弹出询问保存文件名的对话框 最好不要使用默认文件名 而要输入有意义的文件名 比如 Li31Unit1 意义为例 3.1 的单元文件 1 然后点击 保存 按钮 首次保存操作时 系统会 接着弹出询问保存工程名的对话框 也应输入有意义的工程文件名 比如 Li31project1, (意义 为例 3.1 的工程文件 1) 再点击 保存 按钮 该步骤操作之后再查看 File 菜单 会发现 Save All 选项已经变虚了 在保存文件后即可按 F9 键 或选择 RUN 菜单的 RUN 选项 运行程序 若程序有语法错误 系统将在 编辑器下面列出关于各错误原因的提示信息 并将光 标停在第一个错误出现点 当修改错误后可以按 F9 键继续运行程序 也可以按 Ctrl+F2 组合 键或选择 RUN 菜单的 Progrem Reset 选项回到程序设计状态 当程序无误 执行后 就会看到如图 3-5 所示的运行操作界面 在运行程序后即出现的窗口界面上 各 Edit 组件的文本框内显示的是窗体设计时为其 Text 属性设定的值 在标有 运算 两字的 Button 组件上显示有虚线框 说明它处在默认的 选中状态 后文中将称它取得了输入 焦点 这时按回车键 就将直接启动运算操作
    图 3-5 例 3.1 的运行操作界面
    在运行操作界面上 用鼠标点击某 Edit 组件 它就会获得输入焦点 即在它的文本框内 显示出表示字符插入点的光标 此时操作键盘就能编辑输入数据 一个数据输入完成后 可用鼠标点击另一个 Edit 组件改变输入焦点 也可以点击 Tab 按 键 按照设计窗体时组件放入窗体的次序将焦点转换到下一个组件 每当 3 个边长值被 '输入到 3 个 Edit 文本框 就可以点击 运行 按钮开始计算其面 55
    积值 并看到如图 3-6 所示的运算结果报告
    图 3-6 例 3.1 的运行结果报告示意图
    由于本例程序中应用的 Floattostr 函数不能转换含有非数字字符的串 所以当输入数据中 含有字母 空格或其他字符时 程序运行将产生异常 并报告错误信息 为了使程序本身具 有自动处理这种情况的能力 可以采取如下措施 其一是选用其他的将串转换为数值的工具 如选用标准过程 Val s,x,n 因为如第 3.3.3 节中介绍 它在将串 s 转换为数值时 遇到非数字字符并不产生异常 而是由参变量 n 自动 记录非数字字符出现的位置 并将已完成的转换结果由参变量 x 返回 所以只要在调用它之 后紧接着分析变量 n 的值 就能依据 n 的取值进行各种处理 请参看下一节 其二是采用 Object Pascal 提供的异常处理工具 请参看第 6 章 由于在 Delphi 开发环境按 F9 键运行程序时 首先要对源代码程序 用各种高级程序设 计语言编写的程序均称为源代码程序 进行编译和连接 需要等待一段时间 实际上 一旦 程序被正常执行 Delphi 就完成了程序开发工作 并生成应用程序的可执行代码程序文件 本例得到的可执行 程序文件就是 Li31project1.exe 可以把该文件拷贝到本机的任何一个 目录 也可以拷贝到另外一台机器 完全脱离 Delphi 而直接独立运行 6 例 3.1 的完整单元文件清单 尽管本例的单元文件和前面第 2.3.4 节程序举例的单元文件有许多类同 但仍将完整的 清单给出如下 希望读者能通过反复比较加深对单元文件结构的了解 认识到 Delphi已经自 动地完成了大量的准备工作 我们的主体工作就是要研究题目要求 寻找合理的算法 设计 出有效的程序来 unit li31unit1; {声明单元文件名 在命名存盘时文件名会自动修改} interface {单元文件的接 0明部分开始} uses {声明本单元中将会引用到的其他程序单元} Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; {被 的程序单元名表列 当本单元体有新的引用时会自动更新} Type {类型声明部分开始} TForm1 = class(TForm) {声明 TForm1 为系统已预定义的 TForm 类型 该行后没有 分号 说明它不是一个语句 而是窗体成员类型声明的开始} Button1: TButton; {随着在窗体上放置组件而依次自动添加的各组件类型 } Button2: TButton; 56
    Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Label1: TLabel; Label2: TLabel; procedure Button1Click(Sender: TObject);
    {自动添加的过程名声明语句}
    procedure Button2Click(Sender: TObject); private {从这里开始 只能在本单元所属的各过程引用的标识符} { Private declarations } public {从这里开始 能在本工程所属的各单元引用的标识符} { Public declarations } end; {类型声明部分结束} var {变量说明部分开始 在这里 的变量能被本工程所属的各单元引用} Form1: TForm1; {声明变量 Form1 为 TForm 类型 类型只有和具体变量 名结合才有意义 变量被指定类型之后才能被引用} implementation {单元可执行程序代码部分开始} {$R *.DFM} {由$开始的注释串是编译命令} procedure TForm1.Button1Click(Sender: TObject); {过程 1 开始} var a,b,c,s,arer : real; str1 : string ; begin a := strtofloat(edit1.text); b := strtofloat(edit2.text); c := strtofloat(edit3.text); s := (a+b+c)/2 ; arer := sqrt( s* (s-a) * (s-b) * (s-c) ); str( arer:8:3 , str1 ); showmessage( '面积为 ' + str1 ); end; procedure TForm1.Button2Click(Sender: TObject);
    {过程 1 的代码结束} {过程 2 的代码开始}
    begin close; end; {过程 2 的代码结束} end. {带 . 的 end 表示单元结束 是必须有的部分} 由于在该单元文件中 两个过程的所有可执行语句都是依据在程序中出现的位置被顺序 执行的 所以属于顺序结构 顺序结构十分简单 但无法处理稍微复杂一点的问题
    3.6 条件语句和程序的分支结构
    称计算机为电脑 就是因为它具有逻辑分析和识别能力 能够依据运行过程中遇到的不
    57
    同中间结果进行不同的处理 但它的这种能力是通过运行预先编制好的程序来实现的 要编 写这样的程序 就需要用到程序的分支结构 就需要学习能够支持程序分支结构设 条件 语句 本节就将主要介绍条件语句的语法构成和基本应用方法
    3.6.1 If-Then-Else 语句和 If-Then 语句
    1 If-Then-Else 语句 If-Then-Else 语句是最常用的条件语句 简称 If 语句 它的语法规则如下 If <条件> Then <语句 1> Else <语句 2> 其中 条件 是一个布尔表达式 语句的执行流程如图 3-7(a)所示
    图 3-7 IF 语句执行流程示意图
    图 3-7 中 带箭头的线条称为程序流线 表明程序执行的方向 或去向 菱形框称为判 别框 矩形框称为执行框 采用这些标准图形来描述程序执行路线的图称为程序流程图 流 程图是分析算法 描述算法 设计程序的得力工具 在流程图中还会用到其他一些标准图形 如表示程序开始于结束的椭圆框 表示流线接点的带符号的圆框 表示标准 I/O 设备的输入/ 输出框等 从图 3-7 可以看到 将根据 If 后面的 条件 决定程序的执行方向 具体规定是 若表 示条件的布尔表达式的值为 True 则执行 Then 后面的语句 1 然后执行该 If 语句后面的出 句 即出 面紧接着的语句 若该布尔表达式的值为 False,则执行 Else 后的语句 2 接着执行该 If 语句后的出口语句 请看下面的 IF 语句应用举例 例 3.2 有如下函数 请设计应用程序 对每个输入的 x 值计算并显示其函数值 x2 +1 ( x < 1 ) F(x) = x3 +1 ( x >= 1 ) 步骤如下 1 分析题意 确定算法 本例 公式已经给出 仅涉及到一个输入变量 x 和一个 输出变量 y 但对每一个输入的 x 值要在所给的两个式子中选 择一个进行计算,即需要一个分支结构才能有效地描述这个算 法 假设变量 x 和 y 的数据类型已经被声明 则描述该 过 程 算法 的程序流程图如图 3-8 所示 2 选择编程工具
    图 3-8 例 3.1 程序流程图
    58
    本例输入和输出量简单 组件选择结果 以及它们主要属性值的设置情况见表 3-11
    表 3-11 例 3.2 各组件主要属性值设置
    组件 属性名 Name Label1 Caption Font Name Label2 Caption Font Name Edit1 Text Font Name Button1 Caption Font Font Button2 Caption Font Label1 求函数值 隶书 规则 小二 Label2 请在输入 x 后点击 运算 按钮 隶书 规则 小三 Edit1 0 宋体 规则 四号 Button1 运算 宋体 规则 四号 Button2 结束 宋体 规则 四号 设计操作说明同 Label1 设计操作说明同 Label1 设计操作说明同 Label1 设计操作说明同 Label1 属 性 值 设 计 操 作 说 明 在对 视器属性页状态下 点击属性名 在其右边 的属性值栏中输入该属性值 对 Font 属性 要先点击属性值栏右端标有三点的按 钮 再在其对话框中选择设定各子属性
    3 设计窗体界面 依照表 3-10 中关于各组件属性值的安排 窗体界面的设 果可如图 3-9 所示 当然 其中各组件的放置位置和大小 包括窗体大小 均为用鼠标拖放 移动的结果 在各组件上 显示的标题文字内容及其字体 字号和色彩等也可以按个人的美学观点自由改动
    图 3-9 例 3.2 的设计中的窗体
    4 编写程序 结束 按钮的 OnClick 事件处理程序同例 3.1 不再重述 仅将 按钮的 OnClick 事件处理程序 给出如下 由于 Delphi 对保留字不区分字母的大小写 程序中的保留字可 任意采用大写或小写字母形式 procedure TForm1.Button1Click(Sender: TObject); V x,y : real; str1 : string ; ar 59
    begin x := strtofloat(edit1.text); If x<1 Then y := x*x + 1 Else y := Power(x,3.0) + 1 ; showmessage( 'x=' + Edit1.text+ #13#10 + 'y=' + floattostr(y)); end; 特别提醒 3 点 其一 If-Then-Else 是一个整体性的语法结构 其中间不能插入作为语句结束符号的分号 所以 y:=x*x+1 后不能有 号 其二 函数 Power(x1,x2)是在系统单元 Math 中 的标准函数 若在本单元文件 部 分的保留字 Uses 后的被引用单元名称列表中没有含 Math 将报告 Power 标识符没有预先 的错误信息 这时 只要在 Uses 后的单元名表列中插入 Math 名即可 其三 在 showmessage 的信息串中加入#13#10(回车和换行控制字符的 ASCII )可以 换行显示信息 5 编译运行程序 例 3.2 的编译运行操作同例 3.1 其运行中的界面示意如图 3-10 所示
    图 3-10 例 3.1 操作界面
    2 If - Then 语句 当图 3-7(a)所示的的分支结构中的两个分支之一为空(无操作)时 即转化为如图 3-7(b)所 示的更为简单的分支结构 它由条件语句 If-Then 语句来描述 其语法规则为 If <条件> Then <语句> 执行过程为 当条件成立 条件表达式的值为 True 时执行 Then 后的语句 然后顺序 向下执行 否则跳过 Then 后的语句并顺序向下执行 应用举例如下 例 3.3 利用 If-Then 语句改写例 3.2 中的 Button1 的 Onclick 事件处理程序 完成例 3.2 中所给函数的 求解步骤 1 由于只是改写已有的程序 算法和窗 面设 步骤省略 仅给出改写程序的一 种结果和相应的流程图 见图 3-11 procedure TForm1.Button1Click(Sender: TObject); Var x,y : real; str1 : string ; 60
    begin x := strtofloat(edit1.text); y := Power(x,3.0) + 1 ; If x<1 Then y := x*x + 1 showmessage( 'x=' + Edit1.text+ #13#10 + 'y=' + floattostr(y)); end; 该程序的运行效果和例 3.2 中的程序的运行效果完全相 同 仍如图 3-10 所示 2 程序分析 上述程序的基本思路是 由于函数要求对于一个 x 值必 然要有一个对应的函数值 所以先假定 x>=1 并为变量 y 赋 值 x3 +1 然后判别该假定是否不对 即判别 not(x>=1) 是否成立 或判别另一个条件 x<1 是否成立 若假定不对就 图 3-11 例 3.3 流程图 改变对变量 y 的赋值 由于在 If-Then 语句后面只跟有一个语句 所以其后的分号不能省
    3.6.2 分支结构的嵌套和复合语句的应用
    1. 分支结构嵌套 分支结构嵌套 就是在分支结构的任何一个分支上完整地包含有另一个分支结构 若某 一分支上依据某项的不同取值又有多种处理方法 就要用分支结构的嵌套来描述它 2. 复合语句 块语句 用保留字 begin 和 end 把多个简单语句或结构控制语句 条件语句 循环语句等 括起 来 就形成一个复合语句 也称为块语句 虽然在复合语句中可以含有复杂的程序结构 并 允许复合语句的多层嵌套 但复合语句本身则作为简单语句来看待 应用非常广泛 3. 分支结构嵌套的应用举例 具体应用举例如下 例 3.4 设计应用程序 输入一元二次方程的系数 a b c 的值 求它的根 求解步骤 1 算法分析 由数学分析 要求一元二次方程的根 则首先要求系数 a 不为 0 然后才能依据条件 b2 -4ac<0 的是否成立分别计算其复数根和实数根 并依据 b2 -4ac=0 是否成立区分出是否为重 根 所以本例将涉及到 3 个输入变量 a b c 一个中间结果变量 Delta 两个结果变量 x1 x2 一个输出变量 str1 其中除 str1 为 String 类型外,其他均为 Real 类型 具体算法流程图如 图 3-12 所示 其中 可以看到 3 层的分支结构嵌套 第一层的两个分支是 当 a=0 时仅显示 输入错 否则才进行求根的 过程 在第 一层分支结构的 Else 后为求根的算法过程 由于该计算过程需要多于一个的语句才能完成 第二层分支结构是在第一层分支结构的 Else 后的复合语句中包含的一个完整的分支结 61
    构 其结构功能为当 Delta<0 时计算虚数根 否则 实数根 在其 Then 分支上使用了复合 语句 因为一个简单语句无法完成计算显示虚数根的任务 Else 分支则实现实数根的计算和 显示 第三层分支结构是在第二层分支结构 Else 分支上的分支结构 其结构功能为当 Delta=0 时计算显示相重实数根 否则 显示两个不相等的实数根
    图 3-12 例 3.4 算法流程图
    值得再次提醒的是 为了复合 If-Then-Else 是一个完整的语句 的语法规则 当多层分 支结构嵌套时 保留字 Else 前分号的使用就更需要多加注意 请参看后面程序清单里的注释 内容 2 选择编程工具和设 体界面 由于本例和例 3.1 的相比 输入和输出要求基本相同 仅计算要求不同 所以窗体界面 可以利用例 3.1 的窗体界面设 果 只要将应用程序标题文字内容由 求三角形面积 改 为 求一元二次方程的根 将操作提示内容由 在输入三条边长后点击运算按钮 改为 在 输入系数 a b c 后点击运算按钮 即可 3 编写程序 为了利用已有的例 3.1 的设计成果 减小编程工作量 可以采用如下的具体操作 首先选择 File 菜单的 Open Project…选项 在对话框中选择打开已存在的工程文件 L31project1 接着选择执行 File 菜单的 Save Project As…选项将打开的工程文件换名为 L34project1 存盘 再执行 File 菜单的 Save As…选项将单元文件换名为 L34Uint1 存盘 这其 中的两步存盘操作都是必要的 否则在后面修改程序或窗体后一旦执行存盘操作 原有的程 序文件就将被覆盖 62
    在完成对原有文件换名存盘后 即可进入窗体设计状态或 编辑状态进行修改窗体设 计和改写程序 的操作 在完成修改设计工作后 执行 File 菜单的 Save All…选项进行新 工程文件和单元文件的存盘 执行 Save 选项仅保存单元文件 依照图 3-12 所示的算法流程图重新编写本例 Button1 组件的 OnClick 事件处理程序 完 成后的清单如下 procedure TForm1.Button1Click(Sender: TObject); var {声明程序中用到的各个变量} a,b,c,delta : real; str1,str2 : string ; begin a := strtofloat(edit1.text); b := strtofloat(edit2.text); c := strtofloat(edit3.text); If a=0 Then showmessage('输入错 a=0') Else begin Delta := b*b-4*a*c ;
    {读入 3 个系数值}
    {第一层分支结构开始} {第一层分支结构的 Then 分支为一简单语句 但 该语句后不能以分号 ; 结束} {第一层分支结构的 Else 分支上复合语句的开始}
    If delta <0 Then begin {第二层分支结构的 Then 分支上复合语句的开始} str1 := floattostr( -b/(2*a)); str2 := floattostr( sqrt(abs(Delta))/(2*a)); showmessage('x1='+str1+'+'+str2+'i'+#13#10 +'x2='+str1+'-'+str2+'i'); end {第二层分支结构的 Then 分支上复合语句的结束 不能有分号} Else If Delta =0 Then {第二层分支结构的 Else 分支上嵌套的分支结构的开始} begin {第三层分支结构的 Then 分支上复合语句的开始} str1 := floattostr( -b/(2*a)); showmessage('x1=x2='+str1); end {第三层分支结构的 Then 分支上复合语句的结束 不能有分号} Else begin {第三层分支结构的 Else 分支上复合语句的开始} str1 := floattostr(-b/(2*a)-sqrt(Delta)/(2*a)); str2 := floattostr(-b/(2*a)+sqrt(Delta)/(2*a)); showmessage('x1='+str1+#13#10+'x2='+str2) end; {第三层分支结构的 Else 分支上复合语句的结束,该保留字 end 后 的 分号 ; 既是第三层分支结构的结束 也是第二层分支结构的结束 63
    end;
    因为 第二层分支结构的 Else 分支上仅有一个完整的第三层分支结 构 } {第一层分支结构的 Else 分支上复合语句的结束}
    end; 由以上清单可以看到 复合语句是一个非常重要的语言工具 它可以被多层嵌套 只要 保证 begin-end 的配对使用即可 在后面例 3.8 的程序清单中 有大量的复合语句的例子 除 了语法必须外 许多情况下 使用复合语句的目的仅仅是为了程序的书写格式更为清晰 程 序结构更为明了 4 编译运行程序 以上例 3.4 的应用程序的编译运行操作同例 3.1 不再重述 仅给出如图 3-13 所示的程 序运行时的结果报告窗口 以启发读者对窗体和程序的设计思路 其中输入值分别为 a=1 b=1 c=6 结果分别为 x1 = 0.5 + 2.39791576165636i x2 = 0.5 - 2.39791576165636i 能两行输出 就是因为在 ShowMessage 的串参数中加入了回车换行控制字符#13#10
    图 3-13 例 3.4 的运行操作窗 面
    3.6.3 Case 语句和程序的多分支结构
    1 Case 语句 Case 语句也称为情况语句 可根据条件在多个语句中选择一个执行 所以用 Case 语句 可以方便地描述具有多个分支的分支结构 简称多分支结构 其语法规则为 Case <表达式> of <常量表 1> : <语句 1> <常量表 2> : <语句 2> …… <常量表 n> : <语句 n> [ Else <语句 n+1> ] End 即 Case 语句由保留字 Case开始到它所对应的保留字 End 结束 其中保留字 Case 后的 表 达式 即为该语句向下选择执行的 条件 但和 If 语句不同的是它可以为任意返回值为有 64
    序类型(整形 字符型 布尔型 子界型和枚举型)的表达式 其中每个 常量表 中可以包 含一个或多个与表达式返回值类型相同 或兼容 的常量或常量表达式 且多于一个时 各 常量间要用逗号分隔 Case 语句的执行流程如图 3-14 所示 执行过程的语言描述为 表达式 的值 将此值依次和其后的各个常量相比较 一旦和某个常量相等 就 执行该常量所在常量表后面的语句 然后转该 Case 语句的后续语句执行 若在各常量表中均 没有找到和表达式值相等的常数 则执行保留字 Else 后的语句 然后执行该 Case 语句的后 续语句 由于 Else 部分为可选 当没有 Else 部分时 在各常量表中找不到与表达式值相等的 常数时即转去执行该 Case 语句的后续语句
    图 3-14 Case 语句的执行流程示意图
    要注意的 3 点是 其一 所有在 Case 语句常量表中的常量必须是惟一的 其二 Else 语句必须放在所有判断语句之后 其三 表达式和常量不能为 String 类型 2. Case 语句应用举例 下面是应用 Case 语句简单举例 例 3.5 在键盘上输入一个数字 根据该数字改变窗体颜色 并在读到非数字字符或超 出所允许的颜色选择范围时时给出提示 步骤 1 算法分析 依据该题要求 是在多种情况中选择一种 可以采用 Case 语句来实现 2 选择编程工具 设计窗体界面 前面我们已经熟悉了利用 Edit 组件输入数据的方法 这里再学习一种利用标准对话框 InputQuery 输入数据的方法 能使本例的窗体设 化到只有两个按钮 开始 按钮和 结 束 按钮 所以窗体上组件的主要属性值表格和界面图这里也与省略 InputQuery 也是系统单元 Dailogs 中定义的一个标准函数 它的返回值为 Boolean 类型 65
    具有 3 个参数 第一个参数表示对话框的标题 可为 String 类型的常量表达式 第二个参数表示对要输入量的提示信息 可为 String 类型的常量表达式 第三个参数用来接收输入的字符串内容 为 String 类型为的变量 其显示形式如图 3-15 所示 若函数返回值为 True 表示按了 OK 按钮 反之为按了 Cancel 按钮
    图 3-15 InoutQuery 对话框
    但目前 InputQuery 的 Cancel 按钮的返回值实际上还不好应用 若 需要对 Cacel 按钮 有所处理 可阅读参考文献 选用另一个对话框 InputBox 3 编写程序 由于 结束 按钮的功能和前例相同 这里仅给出 开始 按钮的 OnClick 事件处理程 序清单如下 procedure TForm1.Button1Click(Sender: TObject); var {声明程序中用到的各个变量} Flag : Boolean ; str1 : string ; chr1 : char ; begin flag := InputQuery(‘选择改变窗体颜色’ '请选择输入 1 ~ 4' chr1 := str1[1] ; If flag Then str1);
    {若用户按了 OK 钮 则分析变更颜色}
    Case chr1 of '1': Form1.Color := Clred ; '2v: Form1.Color := Clgreen ; '3': Form1.Color := ClBlue ; '4': Form1.Color := ClYellow ; Else showmessage('选择超出了范围 1 ~ 4'); End; {Case 语句结束,同时也是 If-Then 语句的结束 要有分号} end; 4 编译运行程序也同前 这里不在重述 例 3.6 修改例 3.5 程序 使在输入一个 1~12 间的数字时 报告对应月份有几天 步骤 66
    1 算法分析 本例主要说明 Case 语句的常量表中 能用逗号来表示多个不连续的有序常量 为了反 映 Delphi 的灵活性 本例将利用 Label 组件输出结果 2 设计窗体 由于要利用 Label 组件输出结果 所以和例 3.5 相比窗体上要多一个组件 Label1 如图 3-15 所示 表 3-12 仅给出组件 Label1 的主要属性值
    表 3-12
    属性名 Name: Caption Font.Size AutoSize 属性值 Label1 ‘ 20 True ’ 在程序中引用时的标识符 初值赋空格串 使程序开始运行时暂无输出显示 设定显示文字的大小 允许 lable 显示区的大小随文字内容多少自动变化
    例 3.6 中 Label1 组件的主要属性值
    说 明
    3 编写程序 依据题目要求 程序 编写结果如下 procedure TForm1.Button1Click(Sender: TObject); var Flag : Boolean ; str1 : string ; n : integer; begin flag := InputQuery('了解各月份天数' '请选择输入 1 ~ 12' str1); If flag Then {若用户按了 OK 钮 则分析变更颜色} begin N := StrtoInt(str1); Case n of 1,3,5,7,8,10,12 : Label1.caption := inttostr(n)+ '月份有 31 天' ; 4,6,9,11 : Label1.caption := inttostr(n)+ '月份有 30 天' ; 2 : Label1.caption := inttostr(n)+ '月份有 28 天' ; Else Label1.caption := inttostr(n)+ '选择超出了范围 1 ~ 12'); End ; {Case 语句结束} end ; {If-Then 语句的结束 要有分号} end; 编译运行程序操作同前 不再重述 窗口设计和运行界面如图 3-16 所示 例 3.7 修改例 3.6 程序 使在输入一个字符后 报告对其是否为数字或字母 若是字母 报告应是大写字母还是小写字母 步骤 1 算法分析 67
    本例主要说明 Case 语句的常量表中 能用两点 .. 表示一个连续的取值区间 算法同例 3.6 窗体设计也和例 3.6 类似
    基本
    (a) 运行操作界面 图 3-16 例 3.6 运行界面
    (b) 结果报告界面
    2 程序 设计 依据题目要求 将例 3.6 的程序代码编修改结果如下 procedure TForm1.Button1Click(Sender: TObject); var {声明程序中用到的各个变量} Flag : Boolean ; str1 : string ; Chr1 : char ; begin flag := InputQuery(‘判别输入字符类型’ 请输入一个任意字符 str1); If flag Then {若用户按了 OK 钮 则分析字符} begin Chr1 := str1[1]; Case chr1 of ‘A’..’Z’ : Label1.caption := chr1+’为大写字母’ ; ‘a’..’z’ : Label1.caption := chr1+’为小写字母’ ; ‘0’..’9’ : Label1.caption := chr1+’为数字字符’ ; End ; {Case 语句结束} end ; {If-Then 语句的结束 要有分号} end; 3 利用标准对话框函数 MessageDlg 丰富操作控制功能 如果要使上述程序在按其他键时能增加一项询问是否要结束程序运行的功能 仅需要在 其 Case 语句中增加 Else 分支部分 若要利用 Delphi 所提供的另一个标准对话框函数 MessageDlg 则增加该项功能所需要的语句十分简单 即仅需在 Case 的 End 保留字前增加 如下的内容 Else If MessageDlg( ‘输入了非字母 非数字字符 要继续吗 ’ mtConfirmation, [mbYes,mbNo],0)= mrNo Then Halt ; 则在程序运行时 如若按了一个非字母 非数字按键 将会显示如图 3-17 所示的对话框 若点击其 Yes 按钮 则程序继续运行 若点击了 No 按钮则程序结束运行 68
    简单一个语句就能有如此效果 可见函数 MessageDlg 的功能强大, 仅简介如下 函数 MessageDlg(str1,Atype,ButtonType,HelpDode)共有 4 个参数和一个返回值 第一个参数为 String 类型的常量或表达式 第 2 个参数为枚举类型 表示要选用的对话 框类型 共有四种预定的选择和一种自定义选择 见表 3-13 第 3 个参数为集合类型 表示 要选用的按钮类型 可在预定义的 9 种选择中任选两个 见表 3-14 第 4 个参数为在线帮助 说明 为长整形类型 函数返回值是 word 类型 表示按下按钮的序号
    图 3-17 MessageDlg 对话框
    表 3-13
    MessageDlg 对话框类型
    用 警告信息 错误信息 显示信息 提请确认 自定义型 途
    表 3-14
    按键类型 mbYes mbOk mbAbort mbIgnore mbHelp
    MessageDlg 对话框按钮类型
    返回值 按键类型 mbNo mbCancel mbRetry mbAll 返回值 MrNo MrCancel MrRetry MrAll
    对话框类型 MtWarning MtError MtInformation MtConfirmation MtCustom
    MrYes MrOk MrAbort MrIgnore MrHelp
    要认真比较本例中引用该函数时对各参数的选用形式和表中预定义的表示对话框类型 和按钮类型的常量符号 学会灵活使用该函数的方法 当然 Delphi 还提供有其他的对话框 标准函数 如 MessageBox InputBox 等 请在掌握本书内容后 再学习应用
    3.6.4 检查框类组件在分支结构程序设计中的应用
    除了上述的 If 语句 Case 语句能依据程序当前的某个条件选择执行某个程序流程外 Delphi 提供的许多组件还支持用户在程序运行时直接进行项目选择来控制程序流向 前面已 经学过的 Button 组件的应用 本节将介绍无线按钮 单选框 组件 RadioButton 和检查框 复 选框 组件 CheckBox 以及常配合它们使用的容器组件 1. RadioButton 组件 RadioButton 组件的主要属性中 Name 和 Caption 与 Button 的相应属性一样 其 Checked 属性为 Ture 表示被选中 否则未被选 其 Enabled 属性为 False 时 该按钮显示为灰色 表 示禁止使用 其 Alignment 属性则确定按钮图案显示在 Caption 文本的左边还是右边 默认值 是显示在文本右边 RadioButton 组件往往是多个一组地被应用 通过指定一个操作区域 区域内的所有 RadioButton 组件被看作一组 而对于一组 RadioButton 组件 在任何时刻只能选中其中的一 个 即它们之间是互斥的 所以称它为单选钮 2. CheckBox 组件 69
    CheckBox 组件和 RadioButton 组件的主要不同是 在一组 CheckBox 按钮中允许选择其 中的多个 所以称它为复选钮 除和 RadioButton 相同的 Name Caption 和 Checked 属性外,其 State 属性更进一步说明按 钮的状态 它共有 3 个可能的取值 cbUncheched(未被检查的) cbCkecked(已被检查的)和 cbGrayed(灰色的) 缺省值是 cbUncheched 3. Panel 组件 Panel 组件被称为操纵板 是因为常用它来作为其他对 背景 可以由它来划定一个 操作区域 并通过它的属性设置使该区域显示不同形式的边界 除 Name Caption 属性外 Panel 组件的其他主要属性分别是确定它在所属窗 的位置 确定 Caption 文字在 Panel 的 对齐位置和边界形式 对程序执行能产生影响的属性主要是 Enabled 属性 它 Panel 上所 有的组件是否可用 即该属性值为 False 则其上所有的组件的 Enabled 属性的值也同时为 False 4. RadioGroup 组件 RadioGroup 组件 也称单选按钮组组件 主要用于需要多个按钮的情况 除 Name Caption 属性外,它有 3 个重要的属性 Columns 属性 在单选按钮组中按钮的排列列数 默认值为 1 列 ItemIndex 属性表示当前所被选定的按钮的索引号 Item 属性是指本单选按钮组中的按钮列表 其中各按钮的次序号即它们的索引号 在窗体设计时 先选中单选按钮组 再在对象监视器选中 Item 属性 并点击其值栏右端 的 3 点按钮 即弹出如图 3-18 所示的对话框 图 3-18 中表示设置了 4 个单选钮 分别是加 减 乘 除 对应的 RadioGroup 形式如图 3-19 所示
    图 3-18 Item 属性设置对话框
    图 3-19 单选按钮组示意图
    5. GroupBox 组件 GroupBox 常用于复选框的组织 虽然复选框之间相互不冲突 但用一个容器组件将它 们组织起来 既便于对界面操作功能的理解 看起来也更清晰 整齐 参看例 3.8 6. Radiogroup CheckBox 和 GroupBox 组件的综合应用举例 例 3.8 设计简单的 100 以内的四则算术运算 Cai 教学软件 步骤如下 1 分析题意 确定编程工具 要使 机能模拟长者随意给儿童命题的过程 Delphi 提供的随机函数是一个非常好的 工具 在常用函数介绍一节曾经给出了随机函数的简单说明 这里结合实际应用再深入一步 70
    了解它的一些概念 随机的 意义是随时发生的不可预知的事情 例如你给儿童命题 3+5= 可能就是你 随口而出的结果 而且下一个命题和本次命题也无联系 但在 机中应用程序中 往往要 利用随机函数能够反复地重复某个实验过程 所以对随机函数能按照事先给予的一个约定来 产生随机数序列 Delphi 的随机函数引用格式如下 x := Random [ ( Range ) ] ; 即若引用时无参数部分 其返回值是一个在区间 [0 1 上取值的随机实型数 若带有 参数 可为整数类型的常量 变量或表达式 则返回值是一个在区间 [0 参数值 1 上取 值的随机整型数 若仅仅在程序中引用随机函数 Random 则每次执行该程序所产生的随机数序列是相同 的 为了使每次执行程序得到不同的随机数序列 可以先调用初始化随机函数发生器的过程 Randomize 也称为设置随机数种子过程 请参看其在本例程序中的应用 另外 Random 和 Randomize 是在系统 System 单元中预定义的 利用在线帮助也能更深入地学习 2 安排程序功能 可以假定为程序安排如下操作功能 l 操作者可以在选择初级操作或高级操作 l 初级操作仅能在加 减 乘 除 4 种运算种选择一种运算 l 机随机给出两个操作数 除法运算时可选择显示余数 l 可选择统计得分率 l 机在判定答案后给出贺词 3 算法和窗体设计 依据功能安排 窗体上需要 1 个 RadioGroup 组件,1 个 GroupBox 组件 3 个 CheckBox 复选钮 2 个显示操作数的 Ladel 组件,3 个报告作题效果的 Label 组件 8 个用于操作提示的 Label 组件 2 个操作者输入计算结果的 Edit 组件和 3 个运行控制的按钮 窗体设计完成时的界面如图 3-20 所示 其中主要组件的重要属性值见表 3-14
    图 3-20 例 3.8 的设计时期窗 意图
    对用于操作提示的 Label 组件的 Caption 属性 将在程序中依据操作者的选择更换其原有 值 由于本例的多个事件处理程序要同时对一些变量的值进行分析比较 还要说明一些在本 71
    单元文件中有效的变量 并在 Form1 建立时将统计变量清零 粗略的算法流程如图 3-21 所示
    图 3-21 例 3.8 算法流程图简图
    4 编写程序 本例选用 出题 和 OK 按钮的 OnClick 事件处理程序分别进行命题和判别答案的工 作 同时也利用了 RadioGroup 组件的 OnClick 事件来改变窗体上相应组件的状态 另外再在 Form1.Create 事件 即建立窗体 Form1 时 处理程序中完成统计变量清零和初始化随机函数 发生器的工作 除文件开始的声明部分外 全部 5 个过程的程序清单如下 var Form1 : TForm1 ; d,dy,j,k : Integer ;
    f
    :
    real ;
    {说明全局变量}
    implementation {$R *.DFM} procedure TForm1.Button3Click(Sender: TObject); {出题过程开始} var x,y : integer ; f1 : real ; {声明在本过程中有效的变量} Label Loop1 ; {声明在本过程中用到的语句标号} begin showmessage( '开始出题了 请注意 ' loop1 : x := random(100) ; If x=0 Then goto loop1 ; y := random(100) ; While y=0 do y := label3.caption := Edit1.text := '' 72 random(100) ; ) ; {保证 x 不为零} {保证 y 不为零}
    Inttostr(x); label5.caption := Inttostr(y); ; Edit2.Text := '' ; {一行上允许有多个语句}
    Case RadioGroup1.ItemIndex of 0 : begin {依据运算选择 更新提示 '结果} d:= x+y ; label2.caption := '被加数 '; label4.caption := ' 加数 '; end ; 1 : begin While (x-y)<0 do y:=Random(100) ;
    {保证减法运算结果非负}
    d :=x-y ; label5.caption := Inttostr(y); label2.caption := '被减数 ' ; label4.caption := ' 减数 '; end ; 2 : begin d:= x*y ; label2.caption := '被乘数 '; label4.caption := ' 乘数 '; end ; 3 : begin label2.caption := '被除数 ' ; If
    label4.caption := ' 除数 ' ;
    CheckBox2.checked Then begin {当要求除法运算回答余数时 '余数结果}
    label15.Enabled := True ; Edit2.enabled := True ; D := x Div y ; dy := x Mod y ; end {If-Then-Else 结构的 Then 部分结束 不能有分号} Else begin {否则 当一般除法运算时 要保证 x 能被 y 整除}
    While ( x/y) <> (trunc(x/y)) Do Y:=Random(100); d := x div y ; label5.caption := Inttostr(y); end ; {Case 条件 3 的复合语句中的 If-Then-Else 语句结束 需要分号} end ; {Case 条件 3 的复合语句结束} End; {Case 语句结束} end; {出题过程 TForm1.Button3Click 结束} procedure TForm1.Button1Click(Sender: TObject) ; {判别答案过程开始} var fd,fz,I : Integer; fc : Real; begin val(edit1.text,fc,i); {为避免转换格式错误中断程序运行而选用了该标准过程} If i<>0 Then {若无法转换则显示提示信息 并执行 Exit 语句退出本过程} begin showmessage( '输入结果格式错 ' ) ; exit; end ; fz := trunc(fc); If CheckBox2.checked and (radiogroup1.ItemIndex=3) Then begin {若选择除法 且要求回答余数 则读取判别余数结果} val(edit2.text,fd,i); If i<>0 Then begin showmessage( '输入余数格式错 ' ) ;
    exit;
    end ; 73
    If
    (d=fz) and (dy=fd) Then {若余数和商都 '则正确题数加 1 } begin k:=k+1 ; showmessage( '祝贺你 答对了 ' ); end
    Else begin {否则 错误题数加 1 并显示正确答案} j:=j+1 ; showmessage( '请比较正确答案 继续努力 ' ); edit1.text := inttostr(d); edit2.Text := inttostr(dy); end; end; If (radiogroup1.ItemIndex=3) and not(CheckBox2.checked) Then If d=fz Then {若为一般除法 仅判别商的正确与否} begin k:=k+1 ; showmessage( '祝贺你 答对了 ' ) ; end Else begin j:=j+1 ; edit1.text := floattostr(f); showmessage( '请比较正确答案 继续努力 ' ); end; If (radiogroup1.ItemIndex<>3) Then {非除法运算的结果判定} If d=fz Then begin k:=k+1 ; showmessage( '祝贺你 答对了 ' ); end
    Else begin j:=j+1 ; edit1.text := inttostr(d) ; showmessage( '请比较正确答案 继续努力 ' ); end; If checkBox3.checked Then {若选择了统 题正确率 则给予显示} begin label9.caption := inttostr(k); Label11.caption := inttostr(j); i := round(100*k/(k+j)) ; Label13.caption := inttostr(i); end Else begin label9.caption := ' '; Label11.caption := ' '; Label13.caption := ' '; end; end; procedure TForm1.Button2Click(Sender: TObject); {Close 按钮处理程序} begin Close ; end; procedure TForm1.FormCreate(Sender: TObject); {建立 Form1 时 清零统计变量} 74
    begin Randomize ; j:=0 ; k:=0 ; end; procedure TForm1.RadioGroup1Click(Sender: TObject); begin { RadioGroup1 中的选择被更新时 更新相应操作提示} If (CheckBox2.checked) and (RadioGroup1.ItemIndex=3) Then begin label15.Enabled := true ; Edit2.enabled := true ; end Else begin label15.Enabled := False ; Edit2.enabled := False ; end ; end; end. {单元文件结束 End 后跟点号 . } 5 编译运行程序 编译运行程序 将看到如图 3-22 所示的操作界面
    图 3-22 例 3.8 操作界面示意图
    3.7 程序的循环结构
    3.7.1 循环结构的概念
    在实际工作中 有许多工作都是在重复性着同一个操作过程 比如用计数器 100 个 数的和 其基本操作就是读一个数 做一次加法 若仿照于此直接用 Object Pascal 语言来描 述 起码要用 200 个语句 劳动量大 而且在数据量变化时要大量地修改程序 才能适应新 的要求 为了减轻对这类实际工程编程的工作量 提高程序的通用性 就要利用程序的循环 结构 使计算机能在某一条件的控制下重复执行一个称为循环体的程序区段 比如要计算 100 个数的和 其关键条件就是有没有读入并完成了 100 数的 为此可以设计一个计数器 每加一个数 计数器加一 若计数器不够 100 就再读下一个数 否则结束运算 这个 过 程的流程图如图 3-23 所示 并有 4 点说明 1 在图 3-23 中 循环结束的控制条件就是 t=100 由于通过变量 t 的增量和比较实现 75
    了对循环执行的控制 也称 t 为该循环的循环控制变量 并简称该循环为 t 循环 2 为了 '地开始循环 必须准备好循环运算和控制所需要的起始条件 如图中将求 和变量 s 和计数器变量 t 清零的操作 3 在图 3-23(a)中 判别条件的操作在执行读数求和操作之后 即在循环体执行之后进 行 可以用自然语言描述为 读数求和 直到 t=100 时结束循环 所以 称循环条件判别在 循环体之后进行的循环为直到型循环 而在图 3-23(b)中 判别条件的操作在执行读数求和操 作之前 即在循环体执行之前进行 可以用自然语言描述为 当 t<>100 时读数求和 否则结 束循环 所以 称循环条件判别在循环体之前进行的循环为当型循环
    图 3-23 基本循环结构流程示意图
    4 当型循环和直到型循环是两种基本的循环结构形式 多数循环运算可以任意选用其 中之一来实现 它们之间的区别就在于直到型循环的循环体起码要被执行一次 而当型循环 则在条件不满足时一次也不被执行 这一点在自动控制系统中将显得十分重要 另外在循环执行过程中 往往还有依据情况提前结束循环的要求 为此 Delphi 在提供 了标准的循环控制语句外 还提供了非正常流程的控制语句
    3.7.2 Repeat-Until 语句
    直到型循环结构可用 Repeat-Until 语句来描述 其语法规则如下 Repeat <语句 1>; <语句 2>; …… <语句 n>; Until <条件>; 其中 有保留字 Repeat 和 Until 括起来的语句 1 到语句 n 构成了循环体 执行过程如图 3-23(a)所示 即先执行循环体一次 然后判断保留字 UNTIL 后的条件(一个布尔表达式) 若 条件成立 值为 True 则终止循环语句的执行 转而执行其后面的语句 若条件不成立(值 为 False) 则继续执行循环语句 请参看下面的应用举例 例 3.9 统计 30 个 18 岁男性青年的平均身高 76
    为了突出算法的实现 作为本例操作界面的窗体上仅按排一个 启动 按钮 输入和结 果显示均通过对话框来实现 结束程序运行则通过点击窗体右上角的关闭窗口按钮来实现 启动按钮的 OnClick 事件处理程序编写结果如下 procedure TForm1.Button1Click(Sender: TObject); var {声明程序中用到的各个变量} High average : single; t : Integer ; Flag : Boolean ; str1 : string ; begin Average := 0 ; t:= 0 ; {准备循环开始条件} Repeat {循环结构开始} flag := InputQuery(‘身高统 序’, 请输入您的身高 单位 米 ,str1); If flag Then {若用户按了 OK 钮 则求和计算} begin Average:=average + strtofloat(str1); {求和} t := t+1 ; {计数器增量} end Else Continue; {If_Then_Else 语句的结束 要有分号} Until t=30 ; {循环结构终端} Average := Average/t ; {求平均值} str(average :8:3,str1); {按场宽 8 其中 3 位小数的格式将 Average 值转换为串} showmessage('30 人的平均身高为'+str1+'米'); end; 该例中人数已知 即循环次数已知 可以通过计数器变量的增量 加步长 记录循环体 的执行次数 并控制循环的继续执行或结束
    3.7.3 While-Do 语句
    当型循环结构可用 While-Do 语句来描述 其语法规则如下 While <条件> Do <语句>; 其循环体是跟在 While-Do 语句后的一个语句 执行过程如图 3-23 (b)所示 即先判断保 留字 While 后的条件(一个布尔表达式) 若条件成立 值为 True 执行循环体一次 若条件 不成立(值为 False),则终止循环语句的执行 即转而执行循环体后面的语句 请参看下面的应 用举例 例 3.10 统计一叠票据的数据之和 本例和上例不同的一点是所给票据的张数未知 所以无法通过计数器的方法控制循环的 继续和结束 而要指定 设计 一个结束的标志 用于循环结束的条件 一般原则是将一个 远离本项目可能取值范围的一个值作为结束标志 如人的身高不能为负数 课程成绩不可能 高于 1000 分等 77
    同例 3.9 为了突出算法的实现 作为本例操作界面的窗体上仅按排一个 启动 按钮 输入和结果显示均通过对话框来实现 结束程序运行则通过点击窗体右上角的关闭窗口按钮 来实现 启动按钮的 OnClick 事件处理程序编写结果如下 procedure TForm1.Button1Click(Sender: TObject); var {声明程序中用到的各个变量} Sum,x : single; t,k : Integer ; Flag : Boolean ; str1 : string ; begin Sum := 0 ; t:= 0 ; {求和和计数器变量清零 准备循环开始条件} flag := InputQuery('票据统计','请输入第'+Inttostr(t+1)+'张票据',str1); Val(str1,x,k); {为正常进入循环 在循环体外先读入一个数据} While (x >= 0) AND flag Do {若输入了 并按了"OK"钮则执行循环体} begin {循环体开始 本例循环体为一个复合语句} Sum := Sum + x ; {求和} t := t+1 ; {票据张数统计} flag := InputQuery('票据统计','请输第'+Inttostr(t+1)+'张票据',str1); Val(str1,x,k); {读入并转换输入的数据 或结束标志} end; {循环体的终端} str(Sum:10:3,str1); {按场宽 10 位 小数 3 位的形式将 Sum 值转换为串} showmessage( '共有单据'+Inttostr(t)+'张'+#13#10+'合计'+str1 ); end;
    3.7.4 For-Do 语句
    For-Do 语句主要描述循环次数已知的循环结构 其语法规则式分为两种 分别为 1 For <变量>:=<初值表达式> To <终值表达式> Do <语句> 2 For <变量>:=<初值表达式> Downto <终值表达式> Do <语句> 在两种形式中 变量即为循环控制变量 必须为有序数据类型 初值和终值表达式则是 其值和变量的数据类型相匹配的表达式 初值和终值差即表示了循环要执行的次数 第 1 种形式的执行过程是 1 循环准备 即首先计算初值表达式的值并赋予循环控制变量 再计算并记忆终值表 达式的值 2 执行循环体 即保留字 Do 后面的语句 3 每执行一次循环体 就将循环控制变量加 1 4 将循环控制变量和记忆的终值比较 若小于等于终值 返回 2 继续循环过程 反之 则结束循环 即执行循环体后面的语句 第 2 种形式的执行过程和第 1 种形式的执行过程基本相同 区分仅在于第 3 步操作 改为控制变量减 1 第 4 步操作改为若控制变量大于终值 返回 2 继续循环过程 反 之 则结束循环 例 3.11 假定 变量类型均已声明 编写 阶乘 N!的程序区段 78
    步骤 依据题目要求 即计算 1*2*3* …*N 和连续求和的过程类似 只是求积的变量的初值 不能为零 而一般赋值 1 则利用 For-To-Do 语句实现 N! 的程序段为 …… s=1 For i:=1 To n Do s:=s*I; …… 利用 For-Downto-Do 语句实现 N! 的程序段为 …… s=1 For i:=n Downto 1 Do s:=s*I; …… 例 3.12 编程序 分析读入的一个字符串中含有几个字母 几个数字字符 几个其他字 符 步骤 1 算法分析 依据题意 要逐一对每个字符进行比较 且字符个数已知 可以采用 For-To 语句控制的 循环结构 需要一个循环控制变量 对每个字符要比较 3 种情况 可以采用 Case 多分支结构 并需要 3 个整数型变量统计 3 个结果 该算法流程如图 3-24 所示
    图 3-24 例 3.12 算法流程图
    79
    2 窗体和程序设计 由算法分析 窗体仅需安排一个启动按钮 在其 OnClick 事件处理程序中利用对话框读 入字符串和报告实现统 果 其程序清单如下 procedure TForm1.Button1Click(Sender: TObject); var i,j,k,l : integer ; str1:string ; Flag : Boolean ; begin flag := InputQuery('字符统计','请输入一个任意字符串',str1); If Flag Then {按了输入对话框的 OK 钮 开始式对串的处理} begin { Then 分支上是一个复合语句} j := 0 ; k := 0 ; For i:=1 To 36 Do Case str1[i] of l := 0 ; {For 循环体是一个 Case 语句}
    'a'..'z','A'..'Z' : j:=j+1 ; '0'..'9' : k:=k+1 ; Else L:=L+1 ; End ; {是 Case 结构的结束 也是 For 循环结构的结束} str1 := str1+' '+inttostr(j)+' '+inttostr(k)+' '+inttostr(l); showmessage(str1); end; end;
    3.7.5 循环结构的嵌套和非正常流程控制语句
    1. 循环结构的嵌套 若一个循环结构中完整地包含有另一个循环结构 则称为循环结构的嵌套 循环嵌套在 程序设计中有着非常广泛的应用 先简单举例给予说明 例 3.13 若所需变量均已经 编写程序段 S = 5!+10!+15!+20 由题可知 本例是一个连续求和的 其中每项又是一个连乘积 且各项之间有一个 均等的递增关系 所以可以利用循环嵌套实现该 要求 程序段如下 …… Sum := 0 ; For I:=1 To 4 Do begin Fac := 1 ; For j := 1 To I*5 Do Fac := Fac*j ; Sum := Sum + Fac ; 80 {求和变量清零 准备开始循环运算过程} {外层循环实现连续求和的控制} {求积变量赋值 1 准备内层循环} {内层循环实现 N!的连乘计算} {内循环体仅为一个语句} {求和 }
    end ;
    {作为外循环体的复合语句的终点}
    …… Delphi 允许多层循环结构的嵌套 但必须被嵌套的循环结构的完整性 2. 非正常流程控制语句 1 GoTo 语句 GoTo 语句强行将程序转向一个用标号指定的语句继续执行 其语法规则为 GoTo <语句标号> ; 例如 在利用对话框读入操作时 为了避免读入空串 可以用如下程序段 …… Jump1: flag := InputQuery('票据统计','请输入票据数字',str1); If str1 = '' Then GoTo JUMP1; {如果输入为空串 则转到 JUMP1 再次执行输入} …… 使用 GoTo 语句要注意以下几点 l 标号在使用前必须先定义 且必须与 GoTo 语句位于同一个过程或函数的程序代码块 中 即不能利用 GoTo 语句直接转向另一个函数或过程去执行 l 不能利用 GoTo 语句从一个结构 循环或条件分支 的外部跳到一个结构的内部 虽 然编译时不提示这类错误 但程序执行时可能发生异想不到的结果 例如 若直接 跳到一个 FOR 循环结构内部 没有 确定循环控制变量 也没有计算其初值和终 值 循环控制将无法正确进行 从而导致循环无法结束 称为死循环 或无法执行 等 l 为了提高程序的可维护性 结构化程序设计方法被广泛采用 其基本原则是 一个 程序结构只能有一个入 一个出口 前面我们所学习的三种程序基本结构也都是 符合这一原则的 为了避免破坏程序的结构化 一个好的程序应尽量少使用 GoTo 语句 2 Break 语句 Break 语句引起控制流退出一层循环结构 例如要分析数 n 是否为素数 即只能被 1 和 自身整除的数 可以利用循环从 2 到 n-1 的数逐一试除 n 但只要有一次被整除 就证明该 数不是素数 试除也没有必要再继续下去 就可以利用 Break 语句结束该循环 程序段如下 …… Flag := true ; For k:=2 To n-1 Do If MOD(n,k) = 0 Then begin Flag := False ; Break ; end ; If Flag Then showmessage ( Inttostr(n)+'是一个素数') 81
    Else showmessage ( Inttostr(n)+'不是一个素数') …… 3 Continue 语句 Continue 语句引起控制流再次从头开始执行所在循环结构的循环体 例如统计 100 到 200 之间 3 的整倍数和 7 的整倍数各有几个 但同时能被 3 和 7 整除的数不参与统计 则能实现 该统计要求的程序段如下 …… j:=0 ; k:=0 ; For I:=100 To 200 Do begin If (MOD(I,3)=0) AND (MOD(I,7)=0) Then Continue ; {若同时能被 3 和 7 整除 则继续分析下一个数} If MOD(I,3) = 0 Then j:=j+1 ; If MOD(I,7)= 0 Then k:=k+1 ; end; …… 程序中的 continue 语句只是使不再执行其后的循环体部分 并使下一次循环继续进行 而 Break 语句则是结束其所在的整个循环层及其内嵌结构的执行 3 Exit 语句 Exit 语句的功能是退出当前的程序 块 如果 块是主程序 Exit 语句导致程序终 止执行 如果当前块是过程或函数 Exit 语句导致返回到调用该过程或函数的语句的下一条 语句执行 4 Halt 语句 Halt 语句导致程序的非 结束 并返回到操作系统 Halt 语句后可跟一个整数代码 以指定该 Halt 语句的位置 一般情况下不使用 Halt 语句 它并不执行释放应用程序所占用的 资源
    3.7.6 Memo 组件的应用和基本程序结构综合应用举例
    1. Memo 组件简介 Memo 组件和 Edit 组件十分相似 都可以用来输入数据或显示输出处理结果 它们的重 要区分是 Edit 组件只能允许用户在一行上输入或输出长度在 255 个字符以内的信息 而 Edit 组件允许用户在多行上输出输入多达 255KB 的数据 2 Memo 组件的重要属性和数据存取方法 由于要在多行上输入和显示信息 Memo 组件就有了和 Edit 组件不同的如下属性 1 属性 Lines Memo 组件的属性 Text 和属性 Lines 都可以用于存取其数据 它们的区分是 属性 Text 82
    和 Edit 的 Text 属性一样 用于存取 Memo 组件的全部的数据 而属性 Lines 是一个类型为 Tstring 的对象属性 即具有它的次层属性和方法 通过这些属性和方法能指定对 Memo 组件 的某个单行数据进行存取操作 所以对数据的存取操作更加灵活 下面仅举例说明通过 Lines 属性的方法和属性对 Memo1 组件数据的几种基本操作 l 存取指定行上的数据内容 如将第 4 行数据赋值到 Edit1 的 Text 属性可用语句 Edit1.Text := Memo1.Lines[4] ; l 改写指定行上的数据内容 如改写第 0 行数据内容可用语句 Memo1.Lines[0] := Edit1.Text ; 在 Memo1 组件数据的最后增添一行数据 可用语句 Memo1.Lines.Add ( ' New line is added. ' ) ; {其参数可为 String 表达式} l 要在 Memo1 组件的数据后追加信息 可用语句
    l
    Memo1.Lines.Append( 'Append a string ') ; 删除指定行上的数据内容 如删除第 1 行上数据内容可用语句 Memo1.Lines.Delete ( 1 ) ;{其参数可为整数表达式} l 清除全部数据内容 可用语句 Memo1.Lines.Clear ; l 在指定行之后插入新的一行内容 可用语句
    l l l l l
    Memo1.Lines.Insert (2, 'Insert a new line' ) ; 将内容保存到磁盘文件 可用语句 Memo1.Lines.SaveToFile ( A:\Newfile.Txt ) ; 读取磁盘文件到 Memo1 组件 可用语句 Memo1.Lines.LoadFromFile ( A:\OldFile.Txt ) ; 要交换 Memo1 组件上的两行数据 如交换第 2 和第 6 行数据可用语句
    Memo1.Lines.Exchange(2,6) ; 要统 件 Memo1 上共有多少行数据 可利用 Lines 的 Count 属性 即语句 I := Memo1.Lines.Count ; 2 属性 ScrollBars 属性 ScrollBars 确定 Memo 对象的滚动条的使用方法 共有 4 种可能的取值 ssHorizontai 表示有水平滚动条 ssVertical表示有垂直滚动条 ssboth 表示两者皆有和 ssNone 表示两者皆 无 ssNone 为其默认值 3 属性 WordWrap 属性 WordWrap 确定在往 Memo 组件输入文字过程中 文字长度大于其宽度时的处理方 法 若其值为 True 则自动换行 否则文字将往左延伸 但它必须在 Memo 组件没有滚动条或 只有垂直滚动条时才会有效 4 属性 WantReturn 属性 WantReturn 确定在给 Memo 组件输入文字时对按下键操作的处理方式 若 WantReturn 属性值为 True 当按下键时输入点会转到下一行 即作用于 Memo 组件 否则 若 WantReturn 属性值为 False 则按下键的操作作用于 Form 即如果当前 Form 上的输入焦点在 关闭 按钮上 按下键将引起关闭操作
    83
    5 属性 WantTabs 属性 WantTabs 确定在给 Memo 组件输入文字时对按下键操作的处理方式 若 WantTabs 属性值为 True 当按下键时输入点会跳到下一个 Tab 位置 即作用于 Memo 组件 否则 若 WantTabs 属性值为 False 则按下键的操作作用于 Form 即当前 Form 上的输入焦点会按次序转向下一个焦点对象 3. 程序基本结构的综合应用举例 例 3.14 查找并显示 1000~10000 之间的所有素数 步骤 1 分析题意 设计算法 由于素数是指除了 1 和自身之外 没有其他能整除它的数 所以要分析数 M 是否为素数 可以用从 2 到 M-1 的整数逐个去试除它 一旦有整除发生 就可以 它不是素数 只有把 全部要试除的数试完而没有一次整除发生 才能证明它是素数 即需要 M-3 次试除和比较操 作 实践证明这是一种可行的方法 但不是一种好方法 因为数论研究证明 证明一个素数 只需要试除到 M 的平方根即可,即试除区间仅为 2 到 M 运算速度将会大大提高 所以在 注意编写好程序代码的同时 更要注意算法的设计或选择 从根本上提高程序的运行质量 显然判别一个数是否为素数的过程是一个循环过程 前已经给出了一个完成这个过程的 程序段例子 要求 1000 到 10000 间的所有素数 就应该在上述判别素数的循环结构外 再套上能逐个产生待判别的数的循环结构 所以本例将用到循环的嵌套结构 要把若干个素数输出显示出来 利用 Edit 组件显然难以达到好的效果 而能够多行输出 的 Memo 组件就成为一个较好的选择 由于显示的数据较多 还应该使该 Memo 组件具有一 个垂直滚动条 即设置其 ScrollBars 属性值为 ssVertical 2 依据算法要求设计窗体 依据算法设计 输入数据由计算机自己产生 仅需要显示查找结果 所以窗体上只安排 两个控制运算 开始 和程序 结束 的按钮和一个用于显示运算结果的 Memo 组件 除按 算法分析要设置 Memo 组件的 ScrollBars 属性值为 ssVertical外 各组件的属性值均可以按个 人爱好设定 3 编写程序 结束 按钮的 OnClick 事件处理程序同前述各例 开始 按钮的 OnClick 事件处理程 序清单如下 procedure TForm1.Button1Click(Sender: TObject); var i,j,k,t : Integer; begin t:=0 ; {将统 数个数的变量 t 清零} For i:=1000 To 10000 Do {通过循环自动产生待判别的整数} begin { Round 函数将 sqrt 函数的实数结果变成为整数再为整数变量赋值} j := Round (sqrt(1.0*i)); {用 1.0*I 把整数 I 变成了 sqrt 要求的实数参数} k := 2 ; {j 为试除除数的终值 k 为试除除数的初值} While ((i MOD k) <> 0) AND (k<=j) Do k:=k+1 ; {利用 While 循环条件 简化了循环体 提高了运行效率} 84
    If k>j Then
    {依据退出循环的条件 判别 I 是否为素数}
    begin Memo1.Lines.Add( inttostr(i) ); t:=t+1; end ; end ; showmessage('共有'+Inttostr(t)+'个素数');
    {报告所查区间上素数的个数}
    end; 4 保存工程和单元文件 运行程序 如前面各例 保存工程和单元文件后即可编译运行程序 运行结果如图 3-25 所示
    图 3-25 例 3.14 的运行结果示意图
    例 3.15 求任意输入的两个整数的最大公约数 步骤 1 分析题意 设计算法 求两个整数的最大公约数 对一个初中毕业生来讲也不是一个难题 但现在的问题是要 使计算机完成该项运算 就必须遵循计算机运算的特点 找到一种能够用算法语言描述的算 法 本例将采用求两数的最大公约数的 转辗相除法 简介如下 若有 M 和 N 两个整数 先用 N 试除 M 若余数为零 则 N 就是它们的最大公约数 运 算结束 否则 就将 N 赋值于 M 将余数赋值于 N 再返回重复进行用 N 试除 M 的操作 2 依据算法要求设计窗体 依据算法设计 窗体上需要安排用于输入数据的 2 个 Edit 组件 用于显示结果的 1 个 Label 组件 用于控制运算 开始 和程序 结束 的 2 个按钮组件 3 编写程序 结束 按钮的 OnClick 事件处理程序同前述各例 开始 按钮的 OnClick 事件处理程 序清单如下 procedure TForm1.Button1Click(Sender: TObject); var M,N,R : Integer; begin M := strtoInt(Edit1.text) ; N := strtoInt(edit2.Text) ; R := M MOD N ; {为了 进入 while 循环 先求一次余数 R} 85
    While R<>0 Do begin M:=N ; N:=R ; R:= M MOD N; end ; Label4.Caption := Inttostr(N);
    {若 R 不为零 则继续执行循环体} {转辗赋值 并计算新的余数 R}
    {报告 结果} end; 4 保存工程和单元文件 运行程序 如前面各例 保存工程和单元文件后即可编译运行程序 运行结果如图 3-26 所示
    图 3-26 例 3.15 的运行结果示意图
    例 3.16 求 Fibonacci数列的前 20 个数 并将它们以每行 4 个的形式显示出来 Fibonacci 数列的第 1 和第 2 个数为 1 以后的每个数都是其前两个数的和 即 F1 = 1 ,F2 = 1 ,……,Fn = Fn-1 + Fn-2 (n>2) 步骤 1 分析题意 设计算法 由数列的通项公式 可以利用 转辗赋值法 来产生数列的各项值 即反复执行 F1 := F1 + F2 ; F2 := F2 + F1 ; 两个语句 依据赋值语句的特点 第 1 个语句执行前 F1 的值为第 I 个数 F2 的值为第 I+1 个 数 第 1 个语句执行后 F1 的值变为为第 I+2 个数 第 2 个语句执行前 F2 的值为第 I+3 个数 实际上在产生了 2 项值后又回到了第 1 个语句执行前的状态 由于要求显示的数据较多 需要选用 Memo 组件 由于要求在每行上显示 4 个数 所以 要利用 Lines 属性的方法实现对输出的控制 2 依据算法要求设计窗体 依据算法设计 窗体上仅需要用于显示结果的 1 个 Memo 组件 用于控制运算 开始 和程序 结束 的 2 个按钮组件 3 编写程序 结束 按钮的 OnClick 事件处理程序同前述各例 开始 按钮的 OnClick 事件处理程 序清单如下 procedure TForm1.Button1Click(Sender: TObject); var F1,F2,I : Integer; str1,str2,str3 :string ; begin 86
    F1 := 1 ; str(F1:8,str1); F2 := 1 ; str(F2:8,str2); str3 := str1+str2 ; I := 2 ; Repeat F1 := F1+F2 ; str(F1:8,str1); F2 := F2+F1 ; str(F2:8,str2); str3 := str3 + str1 +str2 ; I := I+2 ; If (I MOD 4) = 0 Then begin Memo1.lines.Append(str3); str3 := '' ; end ; Until I >= 20 end; 4 保存工程和单元文件 运行程序 如前面各例 保存工程和单元文件后即可编译运行程序 运行结果如图 3-27 所示
    图 3-27 例 3.16 的运行结果示意图
    例 3.17 判别输入的文字是否为 回文 回文 是指从左向右读和从右向左读是一样 的文字串 如 12321 就复合回文的判定规则 步骤 1 分析题意 设计算法 由题目要求 可以将输入的字符串 str1 按逆序存放到 str2 中 再比较它们是否相等 也 可以直接从首尾开始逐个进行对称位置字符的比较 并用消息框报告判别结果 本例采用先 逆序处理 再比较的方法 其中用到了将一个串插入到另一个串的指定位置的标准过程 请 注意它在程序中的应用 2 依据算法要求设计窗体 依据算法设计 窗体上仅需要用于输入的 1 个 Edit 组件 用于控制运算 开始 和程序 结束 的 2 个按钮组件 3 编写程序 结束 按钮的 OnClick 事件处理程序同前述各例 开始 按钮的 OnClick 事件处理程 87
    序清单如下 procedure TForm1.Button1Click(Sender: TObject); var i,n : Integer; str1,str2 :string ; begin str1 := Edit1.text; {读原始串} n := length(str1); {求原始串的长度} For i:=n Downto 1 Do {将串逆序存放到另一个串的循环过程} Insert ( str1[i],str2, n-i+1) ; If str1 = str2 Then showmessage ('是回文 ') Else showmessage ('不是回文 ') ; end; 4 保存工程和单元文件 运行程序 如前面各例 保存工程和单元文件后即可编译运行程序 运行结果如图 3-28 所示
    图 3-28 例 3.17 的运行结果示意图
    3.8 过程和函数的定义及调用
    在程序设计中 为了避免重复键入同样的代码 (Procedure)与函数(Function) 减轻 编写工作量 经常使用过程
    3.8.1 过程的定义和调用
    1 声明过程 要定义和调用一个过程 首先要在程序单元文件首部的 TYPE 区 该过程 其语法规 则如下 Procedure <过程名>(<形式参数及类型说明列表>) 即过程的声明语句由保留字 Procedure 开始 后跟过程名称 小括号内为调用该过程时 所涉及到的参数说明表 并以分号 结束 过程名的命名仅要求符合 Delphi 标识符的命名规则 它是调用该过程时所用的名称 形 式参数说明则比较复杂 若有参数 要依据不同要求说明参数的类型 是否为变量或常量等 当为多个参数时同类型参数之间用逗号 分开 不同类型间用分号 分开 也可以没 88
    有参数 这时过程名后紧跟作为语句结束的符号 请看后面的举例 2 定义过程区块 声明过程后 要在单元文件的可执行 区定义该过程的程序区块 或成为过程的附属 区块 过程区块的语法规则如下 Procedure <过程名> (形式参数说明) Type <过程内用到的类型说明部分> Const <符号常量说明部分> Var <变量说明部分> begin <执行部分> end 即过程区块的定义包括过程首部和执行部分 过程首部用于指定过程标识符和说明形式 参数 如果有的话 例如 要实现将整数转换为字符串 就要涉及到两个参数 一个带入数 值的整数类型参数和一个带回结果的字符串型参数 若将能实现该功能的程序定义为过程 NumString 则具体的步骤为 1 在单元文件的 Type 区 过程 声明过程 NumString 可使用语句 procedure NumString(N: Integer; var S: String) 其中 NumString 为过程名 在参数 部分说明了两个参 N 和 S 分别是 Integer 类型和 string 类型 参数 S 前面的 Var 表示参数 S 的值在调用过程中允许变化 参数 N 前没有 Var 则说明参数 N 的值在过程调用后仍保持调用该过程前的值 若一个参数前有 Const 则该参 数应为常量或常量表达式 由于在过程定义时 这些参数只是用来描述算法 并没有实际意义的值 所以被称为形 式参数 在被调用时 它们通过和对应的实际参数结合而获得并回送实际意义的值 2 在单元文件的执行区定义过程区块 过程声明之后 就应当在 Implementation 区定义这个过程的过程区块 procedure NumString(N: Integer; var S: String); var V: Integer; begin V := Abs(N); {将输入参数 N 的值取绝对值后赋予变量 V} S := ''; {运算前先把返回结果用的变量 S 置空串} Repeat {Ord 函数返回字符的 ASCII Chr 函数返回一 ASCII 码所 对应的字符 式 N MOD 10 + Ord('0') 的值是数 N 末位数字的 ASCII 码} S := Chr(N MOD 10 + Ord('0')) + S; {转换 N 的末位并连接到 S 串的首位} 89
    N := N DIV 10; Until N = 0; If N < 0 Then
    {将数 N 右移一位 舍去原末位} {若 N 为负数 在结果串的前边加一符号 - }
    S := '-' + S; end; {过程区块定义结束} 该过程区块中 仅定义了标准整数类型的局部变量 V 其他说明见注释信息 Delphi 为使用过程和函数做了大量的预定义工作 特别对事件处理过程 Delphi 会自动 提供基本程序区块的框架 并加上过程名称和参数等 参看前面例 3.1 3. 过程调用 过程经 和定义之后 就可以在程序中调用 调用的形式是 过程名 <实参> 例如 调用上面的例子中定义的过程 可以用语句 NumString(306 str ) 其中实际参数可以是常量 变量或表达式 但实参的个数和类型必须与形参完全匹配 若某形式参数前有 Const 则实际参数只能为同类型的常量或常量表达式 若某形式参 数前有 Var 则实际参数只能为同类型的变量 某形式参数前没有 Const 或 Var 则实际参数 可以为同类型的常量或变量
    3.8.2 函数的定义和调用
    1 函数定义和调用举例 函数的定义和调用与过程的定义和调用类似 不同的是函数的 语句和函数程序区块 由 Function 开始 并在形式参数说明跟有返回结果的类型说明 举例说明如下 例 3.18 若有函数 F(x,y) = sin(x) + cos(y) + 5,写出在 Delphi 开发环境中声明和定义该函 数的过程 并写出调用该函数 F(2.3,4.5)+log23 的语句 1 声明函数 function MyFunction(x,y: Extended): Extended; 即命名函数名为 MyFunction,参数和函数返回类型均为 Extended 即占用 10 个存储字节 的实数类型 它实际兼容了全部有符号的实数类型 2 定义函数区块 函数声明之后 就可在单元文件的 Implementation 区定义该函数的函数区块 Function MyFunction(x,y: Extended): Extended; begin Result := sin(x)+cos(y)+5;{ 或用 MyFunction := sin(x)+cos(y)+5;} end; 3 调用函数 调用函数和调用过程不同 调用过程必须是一个独立的语句 而由于函数必须返回一个 值 调用函数不需要独立的语句 而是通常出现在赋值语句的右边 例如,能完成题目所要求 的计算的语句为 f := MyFunction(2.3,4.5)+log10(23); 90
    2 关于返回值的讨论 过程没有返回值 而函数必须有返回值 有返回值就要定义它的数据类型 Delphi 允许 函数返回任意数据类型 包括标准类型和用户自定义的类型 函数返回值有两种方式 1 直接由函数名返回 例如 Function Squared(Value:Integer):LongInt; begin Squared:=Value*Value; { 结果直接赋值给函数名} end; 2 由 Delphi 的一个内置变量 Result 返回 例如 上面函数可改写为 Function Squared(Value:Integer):LongInt; begin Result := Value*Value; end; Result 实际上是函数名的一个别名 自动具有与函数结果有相同的数据类型 是 Delphi 自动 在函数中声明的局部变量
    3.8.3 函数和过程调用过程中的参数传递和变量的作用域
    1. 参数分类 由前节知道 参数用于在过程和函数的调用过程中传递所需要的原始数据和运算结果 在 Object Pascal 语言中 参数可分为值参数(value) 变量参数(Variable) 常量参数(const)和 外部参数(out) 其中 值参数总是有 类型的 而其他 3 种可以为有类型参数 也可以为 无类型参数 分别简单介绍如下 1 值参数 在 定义函数和过程时的参数说明表列中 值参数前面没有 Var,也没有 const 其的初 值来自于调用过程中所对应的实参数 在过程和函数中对值参数的修改并不影响对应实参数 的原有值 它所对应的实参数必须是一个表达式 但一个变量或一个常量就构成了一个最简 单的表达式 所以值参数对应的实参数可以常量 变量或表达式 实参数类型必须和值参数 一致 且不能为文件类型或含有文件类型的其他结构类型 例 3.18 中定义的函数 MyFunction 的参数 x 和 y 就是值参数 2 常量参数 在 定义函数和过程时的参数说明表列中 常量参数前面有保留字 Const,其初值来自 于调用过程中所对应的实参数 在过程和函数中只能对常量参数的值进行引用 不能给常量 参数赋值 常量参数所对应的实参数必须是一个表达式 实参数类型必须和值参数一致 且 不能为文件类型或含有文件类型的其他结构类型 简单举例如下 例 3.19 编写函数和过程 并显示 S=12 +22 +32 + +1002 的结果 能实现本例计算要求的函数和过程的程序区段如下 Function Add1(const I:Integer):Integer; {该函数返回常数参数的平方} begin Add1 := I*I ; {可以引用常量参数的值 但不允许给其赋值} 91
    end; Procedure Add; Var Sum1,K : Integer; begin Sum1 :=0 ; For K:=1 To 100 Do Sum1 := Sum1 + Add1(k) ;
    {该过程计算 12 +22 +32 +
    +1002
    并显示结果}
    {在赋值语句中调用函数 Add1 实参数 k 的平方}
    showMessage(InttoStr(sum1)); end; 3 变量参数 在 定义函数和过程时的参数说明表列中 变量参数前面有保留字 Var,在函数或过程 被调用时 变量参数和所对应的实参数建立关联 共享实参数的地址 在过程和函数中对变 量参数赋值时 对应的实参数的值也随之改变 变量参数所对应的实参数可以为任意数据类 型 包括文件类型 举例如下 例 3.20 若在窗体上放置有一个 Edit1 组件和一个 Memo1 组件 要求当在 Edit1 的文本 框中按回车键时将 Edit1 的内容添加到 Memo1 请编写恰当的事件处理程序 本例要求在按回车键时将 Edit1 的内容添加到 Memo1 所以应该为 Edit1 的 OnKeyPress 按键 事件编写事件处理程序 编写结果如下 Procedure Tform1.Edit1KeyPress(Sender:Tobject; Var KeyP:char); {该过程的形式参数 KeyP 为变量参数} begin If ( KeyP = #13 ) Then Memo1.lines.add(Edit1.Text); end; 在程序运行时 只要在 Edit1 的文本框内有键按下就将激活执行该事件处理程序 但只 有当按下回车键 其代码#13#10 的前一字节的值#13 传给了变量参数 KeyP,才执行将 Edit1 的 内容添加到 Memo1 的操作 4 数组参数 外部参数包括无类型参数和开放式参数 开放式参数又有开放式字符串参数和开放式数 组参数 简称数组参数 这里仅介绍开放式数组参数的基本概念 利用开放式数组参数可以把不同长度的数组传递给一个函数或过程 将参数说明为开放 式数组参数用 Array Of <类型标识符> 对应的实际参数必须为同类型的变量或元素类型与之 相同的数组变量 开放式数组参数可以是值参数 常量参数或变量参数 但为了保证在大数 组作参数时不产生溢出错误 最好用 Var 或 Const 说明数组参数 在用数组参数的函数或过 程程序区段中 利用标准函数 Low 和 High 可以分别求得对应实参数数组下标的下界和上界 利用标准函数 SizeOf 可求得实参数数组得长度 数组的定义和具体应用将在第 4 章学习 2 变量的作用域 实际上 每个变量 常量 对象方法 类型或其他的标识符都具有其 的有效范围
    92
    只有在有效范围内 才能够正确地取用它们 有效范围也称为作用域 作用域的判定依据是定义说明它们的程序区块和程序区块的层次 假如在 Project1 工程 中含有 Unit1 和 Uint2 两个单元区块 在 Unit1 中含有 A B C 3 个过程区块 在 Unit2 中含 有 D E F 3 个过程区块 如图 3-29 所示 1 在过程区段中说明的变量只能在本过程区段中取用 例如 在过程 A 中说明的变量 V1 只能在过程 A 中取用 若过程 B 中也说明了同名变量 V1 则相互的取值无关 2 在单元区段说明的变量只能在本单元中取用 例如 在单元 Unit2 中说明了变量 UV1 而不是在它的任何一个过程中说明了变量 UV1, 则变量 UV1 可以在单元 Uni2 的 3 个过程中被取用 即 Uint2 的 3 个过程均可以引用或改写 它的值 但它的取值和 Uint1 单元区段中说明的同名变量 UV1 无关 若有的话 ,即 UV1 对 于 Unit2 中的 3 个过程是共用的 但对于 Unit1 是本地的
    图 3-29 变量作用域示意图
    3 在单元区段的 Public 部分声明的变量可以被其他的单元所引用 例如 在 Unit1 的 Public 部分说明了变量 PV1,只要在 Unit2 的 Uses 部分添加 Unit1 就 可以在 Unit2 中引用 PV1 变量 由于在相同的作用域中 任何标识符都要求是惟一的 不能重名 同时也为了增加数据 的保护性能 减小不同程序块间的相互影响 要尽量把变量 常量等标识符的作用域定义到 最小 使其成为本地有效或局部有效
    3.8.4 定义过程 函数的次序和指示字
    使用指示字可以进一步指定过程或函数的产生方式 Delphi 为过程或函数分别提供了 Forward External Assembler 和 Block 指示字 1 Forward 指示字 由于在同一个单元文件中 在前面定义得过程或函数区块内无法调用在其后面定义的函 数或过程 所以要把最通用的函数或过程放置在单元文件可执行 区的最前面 但是有排 放只能顺序进行 单靠位置安排将避免不了需要调用的过程或函数无法在前面定义的问题 为解决这个问题 Delphi 提供了指示字 Forward 仅举例说明它的应用 Implementation Procedure Proc2(Var k:Integer); Forward; Procedure Proc1(Var J:Integer); 93 {对 Proc2 作 Forward 说明}
    begin …… Proc2(234);
    {调用定义次序在后的 Proc2 过程}
    …… end; Procedue proc2(Var K:Integer); begin <放置次序在后的 Proc2 的程序区块> end; 2 External 指示字 External 该指示字表示过程或函数是外部的 通常用于从动态链接库中引入过程或函数 External 后可以跟动态链接库名或表示动态链接库的有序数 也可以指定引入的过程或函数 名 3 Block 指示字 Block 是缺省方式 表示过程或函数的语句部分是 Pascal 程序区快 4 Assembler 指示字 Assembler 表示过程或函数是使用嵌入式汇编语言编写的
    3.9 集合类型 指针类型和过程类型
    作为 机基础教育教材 除了在一些组件属性值的设置中用到了集合类型值之外 本 书尽量避免了指针类型和过程类型的引用 但为了使读者对 Delphi 有一个较全面的认识 本 节简单介绍这些数据类型的基本概念
    3.9.1 集合类型
    1 集合类型的定义 集合类型是一群元素的集合 集合中的元素必须为同一种类型 可以为整数类型 布尔 类型 字符类型 枚举类型 子界 区间 类型等 定义集合类型的语句要放置在单元文件的 Type 区中 使用保留字 Set 完成集合类型的定 义 语句格式为 <集合类型名> = Set of <集合元素说明> ; 其中 集合类型名要符合 Delphi 符号名的命名规则 集合元素说明的形式请看如下的集 合类型定义语句的举例 Type Myset1 = Set of 1..20 ; {定义集合元素为子界类型 1..20} Myset2 = Set of Char ; {定义集合元素为所有的字符} Myset3 = Set of (Red,Blue,Yellow,White); {定义集合元素为枚举类型} 2 集合类型的运算 对于集合可以进行比较运算和元素验证运算 但和其他类型一样 集合类型定义之后
    94
    必须定义该集合类型的变量 才能进行运算和引用 1 集合的比较运算 集合的比较运算和其他类型的比较运算略有不同 例如 若 A 和 B 是两个已经定义并赋 值的集合变量 则 若 A 和 B 含有完全相同的元素 则 A=B 的值为 True 若 A 的每一个元素也是 B 的元素 则 A<=B 的值为 True 反之 若 B 的每一个元素也是 A 的元素 则 A>=B 的值为 True 2 集合元素的验证运算 集合元素验证运算的运算符号为 IN 判定一个有序类型操作数是否为集合的一个成员 例如 若 A 是一个已经定义并赋值的集合变量 则运算表达式 'h' IN A 在字符 h 为集合 A 的元素时值为 True 集合类型的简单应用程序段举例如下 Type TMyset1 = Set of Char ; var Myset1,Myset2 : Tmyset1 ; begin Myset1 := ['a','b','x','y','z'] ; Myset2 := ['x','y','z'] ; If Edit1.Text[1] IN myset1 Then Edit2.Text := 'You are clever!' Else edit2.Text := 'Please try again.' ; If Myset1 = Myset2 Then Edit3.Text := 'That Is good!' ; end ; 值得注意的一点是 集合变量的值可以为 空集
    {定义集合类型}
    {定义集合类型变量} {集合类型变量赋值} {验证集合中是否存在有某个元素}
    {比较两个集合是否相等}
    即不含有任何一个元素 记为[]
    3.9.2 指针类型和过程类型的基本认识
    1 指针类型 一个指针类型的变量的值是和它关联 或称指向 的另外一个变量在内存中的分配地址 通过指针变量可以对它所关联的变量进行任何可能的操作 对指针变量本身 也可以进行赋 值 比较等运算 把指针变量用作过程或函数的参数时 会带来参数传递操作上的极大便利和灵活性 也 是 Delphi 实现对操作系统底层进行控制编程的重要工具 但要达到预期的效果 还需要一定 的计算机系统结构和硬件组成等基础知识的支持
    95
    2 过程类型 在 Object Pascal 中 允许把过程和函数视作变量进行处理 即可以把过程或函数赋值于 某个变量 也可以作为参数来实现其值的传递 仅要求把这些变量和参数说明为过程或函数 类型
    习    题 
    3.1 简述 Delphi 的基本数据类型和它们相应的运算 3.2 举例说明什么是 Delphi 的保留字和命令字 3.3 举例说明标准函数和过程的应用 比较调用函数和过程时方法的不同之处 3.4 编写应用程序 30 张账单的总合 3.5 编写应用程序 输入若干学生一门课程的成绩 统计平均成绩 及格和不及格的人 数 3.6 编写应用程序 读入一个整数 不多于 5 位 分析它是几位数 3.7 编写应用程序 读入一个整数 当为 时显示 1 当为负数时显示 -1 当 为零时显示 0 3.8 编写应用程序 读入一行字符 统计其中有字母 数字 空格和其他字符各有几个 3.10 编写应用程序 统计并逐行显示 每行 5 个数 在区间[10000 50000]上的回文数 3.11 已知 sin(x) = 1+1/2!-1/3!+1/5!-1/7!+… … 编写过程或函数计算 sin(0.2)的值 要求 到求和项的绝对值小于 1E-10 为止
    96
    第4章
    4.1 算法和数据结构
    4.1.1 算法的概念
    常用算法及其程序实现
    首先 给出算法一个广义的定义 为解决一个实际问题而采取的方法和步骤称为 算法 (Algorithm) 也可称 算法 是问题解决方法的描述 其实 日常生活中有许多算法的例子 比如到商场购物 基本的方法与步骤是 挑选商品 询问价格 交款提货 每一个基本步骤 又可以细分为若干个子方法和步骤 比如在挑选商品时 会挑选商品的规格 色彩和功能等 购物者的大脑根据商品的情况与事先预定的条件进行判断并完成购物的过程 实际上就是在 实现 购物 问题的算法 对类似于购物的许多事情 人们已经非常熟悉 常常意识不 到要首先 设计算法 但这个过程却是普遍存在的 同理 要用 机解决某个实际问题 就需要为 机设计好 该问题的方法和步骤 而且这个方法和步骤要符合 机运算的特点和能力 这就是计算机算法 要把算法 告诉 给计算机 就需要编写能描述该算法的 机程序 即程序是用程序设计语言对算法的实现 是一种计算机可以识别 接受的算法的描述形式 所以 在设计计算机算法时 不但要考虑 算法的可执行性 能够被执行 和执行的有限性 能够在有限个执行步骤中得到运算结果 还要考虑其是否易于利用程序设计语言来实现 而对于程序的优劣 则要通过它的有效性 通用性 可读性和可维护性等来判定 其中有效性表示其运行时的高效率 通用性表示其对 同类问题的适应能力 可读性和可维护性则表示当程序在运行中产生故障时 或计算要求发 生局部改变时 能及时 方便地修 修改程序 通常,??根据所处理的对象和用途可将算法分为两大类 数值算法和非数值算法 数值算 法主要解决科学计算问题,??例如求方程的根 求函数的定积分等 非数值算法则要解决广泛 存在的各类管理问题 例如信息检索 信息分类 和决策支持 判定等
    4.1.2 算法描述
    常用的算法描述方法有 自然语言描述 流程图描述和程序设计语言描述等 用自然语言描述算法 虽然通俗易懂 但文字冗长 书写不便 特别是文字的 二义性 会导致描述不清 因此在实际的程序设计工作中并不采用 流程图是一种直观易懂的描述方法 该方法用一些规定的框图 流程线和框图中的说明 文字 算式等来表示各种类型的操作与步骤 既符合计算机程序的特点 又比较容易理 掌握 在第 3 章的一些例题中我们已经看到了它的应用 利用程序设计语言描述算法是我们本课程的学习目的 因为只有它才是计算机可以接受 并执行的算法描述 在第 3 章中通过学习程序的基本结构 顺序 分支和循环 我们已看到 了一些算法的程序描述 本章将继续学习利用程序设计语言描述算法的方法 97
    4.1.3 数据结构概念
    一些对人来讲看似十分简单的问题 比如按从小到大的次序排列读入的 10 个任意数 用计算机实现却并不是容易的事 要编程实现这些算法就要学习和掌握支持实现这些算法的 基本工具 其中主要是掌 法语言所提供的语句和它所支持的数据结构 关于语句 主要是掌握它们的语法规则和语句功能的应用 对此 我们通过第 3 章的学 习已经具有不少的经验和理解 本节仅介绍数据结构的基本概念 数据结构包括数据的逻辑结构和存储结构 逻辑结构描述数据之间的位置和次序关系 比如一个简单变量 没有左邻和右舍 是一 个完全独立的量 但一维数组中的元素 则除了第一个元素前没有元素 最后一个元素后没 有元素外 其他的所有元素之前和之后都有相邻的元素和确定的位置关系 存储结构则描述数据在 机存储器中的存放形式和次序关系 由于 机的存储器单 元都是顺序编号的 所以任何数据在 机内都是以一列 或行 的形式存放的 如何把一 个复杂的数据集合 如两维数组中的数存放到计算机存储器 就有着多种形式 但不同的存 储形式 了不同的数据存取操作 所以一种存储形式也就 了一组可能的运算 本章将 主要学习数组结构和记录结构的基本知识和应用
    4.2 数组及其他结构类型的定义和应用
    4.2.1 数组的定义
    通常 数组数据类型是将相同类型 且数量大小固定的数据类型集合在一起 但 Delphi 除了提供固定大小的静态数组(Static Array 外 也提供了可动态设定其大小的动态数组 (Dyanmic Array) 均以保留字 Array 来说明,其语法规则为 <数组类型名> = Array [ <下标范围> ] O f <数据类型名> 使用数组 需要 3 个步骤 定义数组类型 定义数组变量 使用数组元素 1 定义一维静态数组 定义数组首先要命名数组类型名和数组名 均要求符合 Delphi 标识符的命名规则 其中 数组类型仅说明了 机要如何为一个该类型的数组分配存储单元 在把数组名定义为数组 变量时才获得了真 存储空间 获得存储空间的数组才能被使用 例如语句组 Type TmyArray = Array[0..9] Of Integer ; var MyArray : Tmyarray ; 先定义了一个数组类型名 Tmyarray 该类型数组的数组元素为整数类型 数组中共有 10 个 元素 其下标依次为 0 1 … 9 接着在 Var 区定义了数组 Myarray 数组 MyArray 的类 型为 TmyArray 即数组 Myarray 中共有 10 个整数类型的数组元素 2. 定义二维静态数组 定义二维静态数组仅需要在[ ]内说明两组下标区间 用逗号 分隔 在 Delphi 中 98
    第一个下标表示行号 第二个下标表示列号 例如下面语句组 Type Tarray1 = Array[10..20] Of Real ; Tarray2 = Array[1..5,1..4] Of Word ; var ArrayA,ArrayB : Tarray1 ; Arrayc,ArrayD : Tarray2 ; 先定义了 2 个数组类型名 Tarray1 和 Tarray2 其数组元素分别为 Real 类型和 Word 类型 其 结构形式分别为一维数组和二维数组 分别含有 11 个元素和 20 5 4 个元素 接着在 Var 区定义了 2个数组类型为 Tarray1的一维数组 ArrayA和 ArrayB 定义了 2 个数组类型为 Tarray2 的二维数组 ArrayC 和 ArrayD 其元素的逻辑排列为 5 行 4 列的形式 3. 定义动态数组 若要定义动态数组 则先在 Var 区定义数组变量 但不指明数组大小仅指明数组元素的 类型 然后在程序可执行 区用标准过程 SetLength 设置数组大小 例如语句组 var ArrayF : Array Of Integer ; …… begin SetLength(ArrayF,9); …… end; 就先定义了动态数组 ArrayF 并在被引用前 设置其大小为 9 个元素 Delphi 还允许定义更多维的数组 而且数组下标允许用其他的有序类型来表示 请阅读 参考文献或在线帮助 学习它们的应用
    4.2.2 数组元素的赋值和输出
    数组变量一经定义 就可以为其数组元素赋值或引用其数组元素 必须注意的是 数组 变量 了数组中的所有数组元素 要引用其中的指定元素必须使用带下标的数组下标变量 名 一般情况下 常利用单重循环结构实现一维数组的赋值或输出 常利用双重循环结构实 现二维数组的赋值或输出 例 4.1 一维和二维数组的赋值与输出显示操作举例 本例窗体上放置用于输出显示的一个 Edit 组件和一个 Memo 组件 用于控制运行的两个 Button 组件 利用 Button1 的 OnClick 事件启动运算 利用 Button2 的 OnClick 事件结束程序 运行 Button1 的 OnClick 事件处理程序清单如下 运行界面如图 4-1 所示 procedure TForm1.Button1Click(Sender: TObject); Type Tarray1 = Array[1..6] Of Integer; {定义一个一维的数组类型 Tarray1} Tarray2 = Array[1..5,1..4] Of Integer; {定义一个二维的数组类型 Tarray2} var 99
    Ary1 : Tarray1; Ary2 : Tarray2; I,j : integer; str1,str2 : string ; begin Randomize; for I:=1 to 6 do ary1[i] := random(100); for i:= 1 to 5 do for j:=1 to 4 do ary2[i,j] := I*10+j ;
    {定义一维数组变量 ary1 和二维数组变量 ary2}
    {初始化随机数发生器} {利用单循环 为一维数组赋值 0~100 的随机数} {为数组元素赋值要用下标变量形式} {利用双重循环 通过 为二维数组赋值} {两维下标时 用逗号分隔两个下标} {利用单循环组织数组 ary1 的输出串} {将 ary1 的各元素值显示在 Edit1 上} {将 ary1 的各元素值显示在消息框上} {利用双循环组织数组 ary2 的输出串} {内循环完成一行上数据的输出串处理}
    for I:=1 to 6 do str1 := str1 + inttostr(ary1[i]) + ' '; Edit1.text := str1 ; showmessage(str1); for i:= 1 to 5 do begin for j:=1 to 4 do
    str2 := str2 + inttostr(ary2[i,j]) + ' '; str2 := str2 + #13 ; {一行处理完成后 加回车控制键的 ASCII } end; memo1.lines.text := str2 ; {在 Memo1 上显示 ary2 数组的内容} showmessage(str2); end; 本例采用了随机数方法为数组赋值 也可以利用其他从键盘读数赋值的方法 或者仅为 数组中的某个元素赋值 请参照本例多做各种赋值方法的练习 由于到目前为止 所学过的可用于输出显示的组件都只能接受 String 类型 所以组织输 出串是非常重要的基本技术之一 应参照本例对二维数组的处理过程 试用更多的处理方法 比如若在对二维数组的一行元素处理后加 #10#13 控制字符 显示的各行间会加一空行 使显示效果更清晰
    图 4-1 例 4.1 运行效果示意图
    100
    4.2.3 记录类型的定义和引用
    1 定义记录类型 记录类型和数组类型不同 记录类型由若干不同类型的域 或称数据项 记录元素 所 构成 记录类型在单元文件的 Type 区由保留字 Record 来说明 例如要定义一个存放学生姓 名 年龄 性别 地址信息的记录类型 可以用如下的定义 Type …… Trecord1 = Record Sname : string[20] ; Sage : 1..200 ; Ssex : 0..1 ; Saddr : string[50]; end;
    {记录类型定义开始 Record 后不能有分号 {开始说明各数据项的名称和它的数据类型} {说明 Sage 允许的取值范围 即为子界类型}
    }
    {一个记录的定义由一个 end 保留字结束 其后要用分号
    }
    …… 作为特例 Delphi 还允许一个数据项都没有的记录类型 称为空记录类型 2 定义记录变量 记录类型定义之后 就可以用它来定义记录变量 例如 var Rec1,Rec2 : Trecord1 ; 定义了两个类型为 Trecord1 的记录变量 Rec1 和 Rec2 3 存取记录内容 通过已经定义的记录变量可以整体引用记录 也可以用在记录变量名后跟数据项名的形 式引用记录中的指定记录项 例如 Rec1 := Rec2 ; {引用整个记录内容为记录类型相同的另一个记录赋值} rec1.Sname := '张三'; {为 Rec1 的 Sname 数据项赋值} Rec1.Saddr := '郑州市东风路 5 号 ; Rec2.Saddr := Rec1.Saddr ; {用一个记录项为另一个记录项赋值 要求类型一致} 其中记录数据项名和记录变量名间用点号 . 分隔 当记录变量的数据项较多 可利用 With 语句减少重复书写记录变量名的工作 例如 With Rec1 Do begin Sname := '张三'; Saddr := '郑州市东风路 5 号 ;
    {在 With 语句中已指明的记录名 Rec1 省略}
    end; Delphi 还支持记录常量和具有可变数据项的记录类型 本书不再介绍
    4.2.4 文件类型的定义和类型文件的基本操作
    类型文件是 Object Pascal 特有的的一种文件类型 它允许将各种数据类型的数据以原有 101
    的数据结构形式存入文件 并能以一定的数据结构形式将文件内容读出 由于在数据存取过 程中无须进行数据信息到字符串的转换 有较高的文件操作效率 1 文件类型的定义 由于文件都是记录的有序集合 要正确应用文件 必须指定文件中每个记录的存放内容 并将其称为文件类型 定义文件类型也在单元文件的 Type 区中完成 其语法规则为 <文件类型名> = File Of <数据类型名> ; 其中 文件类型名 必须是合法的 Delphi 标识符 数据类型 必须是大小固定的数据 类型 包括基类型和定义过的记录类型 例如 Type …… Tf1 = File Of real ; Tf2 = File Of Trecoad1 ;
    {定义文件类型 Tf1 为实数} {定义文件类型 Tf2 为自己定义过的记录类型}
    …… 定义文件类型之后 要定义文件类型变量 例如 var File1 : Tf1 ; File2 : Tf2 ; 当文件类型采用基类型时 允许将上述两个步骤合二为一 例如 Var File1 : File Of real ; 通过文件类型定义的文件类型变量所能表示的文件称为类型文件 2 读写类型文件的 4 个基本步骤 1 分配 关联 文件变量 为了读写一个类型文件 要先将它和一个类型文件变量关联起来 称为分配 格式为 Assign (<文件类型变量>,<文件名>); 其中 文件类型变量是定义过的文件类型变量 文件名为有效的 Dos 或 Windows 系统的 文件名 为字符串变量或常量形式 文件名还可以附加文件的路径说明 例如 Assign(File1,'A:\Delphi5\filetest1.Txt'); 这时将按路径名在 A:盘的 Delphi5 目录上查找文件 filetest1.Txt 否则将在当前目录上查找文 件 2 打开文件 Delphi 针对不同的操作目的提供 2 种打开类型文件的方式 分别介绍如下 用 ReWrite 方式打开类型文件的语句格式为 ReWrite( <类型文件变量> ) ReWrite 过程首先用和文件类型变量 如 File1 相关联的文件名 如 filetest1.txt 建立 一个新文件 并打开它 若已经存在同名的文件 则先删除它 再重新建立并打开它 总之 执行 ReWrite 过程之后 打开的文件内容为空 用 Reset 方式打开类型文件的语句格式为 Reset( <类型文件变量> ) Reset 过程用于打开已经存在的文件 即和类型文件变量 如 File1 相关联的文件必须 102
    存在 否则将产生 File not Found 的系统错误信息 为了避免系统给出这种错误提示 以在执行 Reset 过程之前利用 FileExists 函数检查文件是否存在 例如

    If FileExists( 'filename1.tes') Then Reset (file1); 即 FileExists 的参数为一个文件名 若该文件存在则返回值为 True, 否则为 False 若该文 件名 Filename1.tes 已经与文件类型变量 File1 建立了关联 则上述语句可以保证只有在文件 Filename1.tes 存在时才能执行 Reset(file1)过程 3 读写文件 由于类型文件文件的记录长度是固定的 Delphi 安排了文件读写指针来支持文件的读写 操作 在打开文件时 该指针指向文件的第一个记录 其编号为 0 在每次读写操作之后 该指针自动指向下一个记录 读写操作的语句格式分别为 Read <文件类型变量>,<变量> Write <文件类型变量>,<变量或常量> 其中 文件类型变量 必须已经关联有文件 且已经打开 Read 的 变量 项和 Write 的 变量或常量 项的数据类型必须与文件类型相一致 请参看例 4.2 4 关闭文件 为了保证文件数据的完整和安全 在完成文件的读写操作之后要关闭文件 格式为 Close ( <类型文件变量> ) 关闭文件时解除了文件类型变量和原文件之间的关联 即关闭文件之后 该文件类型变 量就可以用来和其他的文件建立新的关联 3 类型文件应用举例 例 4.2 先建立一个类型文件 fileTest.Int,它共有 6 个记录 每个记录上有两个数 一个数 和该数的平方幂 然后逐个记录读取并显示该文件的内容 分析该例要求 在窗体上仅安排一个启动运算的按钮 在显示文件内容后即执行结束操 作 为说明记录类型的定义和应用 给出完整的单元文件清单如下 unit Li42Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type trecord1 = record {定义一个有两个整数域的记录类型} Id1 : integer; Id2 : integer; end ; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } 103
    public { Public declarations } end; var Form1 : TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); var Rec1,rec2 : Trecord1 ; {利用已经定义的记录类型定义记录变量} f : file of Trecord1; {利用已经定义的记录类型定义文件类型变量} I : Integer; begin AssignFile(f,'FileTEST.INT'); {文件分配 建立 f 和 FileTEST.INT 的关联} Rewrite(f); {打开并建立一个新文件} Randomize; for i := 1 to 6 do begin rec1.id1 := Random(1000) ; {利用随机数给记录的一个数据项赋值} rec1.Id2 := rec1.id1*rec1.id1 ;{记录的数据项参与运算并给记录项赋值} Write(f,Rec1); {写一个文件记录} end; showmessage('A File wos created. '); Reset(f); {重新打开文件 复位文件读写指针到文件开始} while not Eof(f) do {利用 Eof 函数判定文件是否结束来控制循环执行} begin Read(f,rec2); {读取并显示一条记录的内容} showmessage(inttostr(rec2.id1)+' '+inttostr(rec2.id2)); end; CloseFile(f); {关闭文件} close; {结束程序运行} end; end. 由于本例建立的是类型文件 程序执行后 在 Delphi5 的当前工作目录上 如本例的当 前目录为 C:\Progrom Files\Borland\Delphi5\Projects 可以看到文件 fileTEST.INT 存在,但用 记事本 写字板等文字编辑软件打开时 将看不到能识别的内容 4. 和类型文件操作相关的函数和子程序 下面通过对例 4.2 程序进行功能完善和改进的说明 介绍与类型文件操作相关的几个标 准函数和过程的基本功能
    104
    函数 Eof(f)判定文件指针是否指向文件的最后一条记录之后的文件结束标记 若是 返回值 True 否则返回值 False 若将程序中的 Close 语句删除 则每点击一次按钮 Button1 就会执行 Rewrite 建立 一个新文件 将会看到不同的文件内容 若将打开文件的语句用如下分支结构来取代 如文件已经存在 点击按钮 Button1 将 会在文件的后面追加 6 条记录 文件不存在时才建立新文件 If fileexists('FileTEST.INT') Then begin Reset(f); Seek(f,filesize(f)); {函数 Seek 和 filesize 功能说明见段后} showmessage('A old file was opened'); end Else begin Rewrite(f); showmessage('A new file was created'); end; 其中 函数 filesize 返回文件变量 f 所关联文件中的记录个数 函数 Seek 则将文件读写 指针移动到指定的记录位置 例如 Seek(f,0) 将文件读写指针移动到文件的第一条记录 Seek(f,3) 将文件读写指针移动到文件的第 4 条记录 由于记录编号从 0 开始 所以 Seek(f,filesize(f))将文件读写指针指向文件最后一条记录的下条记录 为了仅仅保留前 6 条记录 可在 Close(f)语句前增加语句 Seek(f,6); showmessage(inttostr(filepos(f))); Truncate(f); 其中 函数 filepos(f)能返回文件指针当前的位置 函数 Truncate(f)则截除文件指针之后 的所有文件内容
    4.2.5 文本文件(Text File)和文本文件的基本操作
    文本文件是指用字符形式 ASCII 码 存放数据信息的一种文件类型 在文本文件中用 回车-换行 字符的代码序列 #10#13 作为一个文件记录的结束标志 所以也把文本文件 的记录形象地称为文件行 序列 #10#13 就成为行结束标志 由于文本文件只能存放字符 所以在存放或读取其他类型数据时 就要进行类型的转换 操作 影响了文件操作的效率 另外 为了标记各项数据的间隔和以字符形式存放数值类型 数据 都要多占用许多附加的存储空间 比如在类型文件中存放一个 Real 类型的数值总是占 用 6 个字节 而在文本文件中则需要 12 个字节 但是 尽管文本文件有以上这些缺点 它仍 然是最常用的一种文件类型 因为几乎所有的程序设计语言和应用程序都支持对文本文件的 操作 #10#13 序列也是公认的一个行结束标志 要在 Delphi 的应用程序中使用文本文件 也需要完成如下的各项操作 105
    1 定义文本文件变量 由于文本文件变量的类型只有一种 即 TextFile 所以不需要再定义文本文件的文件类 型 可以直接在 Var 区定义文本文件变量 简称文件变量 格式如下 <文件变量名> TextFile ; 其中 文件类型名 必须是合法的 Delphi 标识符 2 文本文件内容的基本读写操作 读写一个文本文件 同样包括如下 4 个步骤 1 分配 关联 文件变量 将文本文件和文件变量关联起来的语句格式为 Assignfile (<文件变量>,<文件名>); 其中 文件变量是定义过的文件变量 文件名为有效的 Dos 或 Windows 系统的文件名 为字符串变量或常量形式 文件名还可以附加文件的路径说明 2 打开文件 有 3 种打开文本文件的方式 分别介绍如下 用 ReWrite 方式打开文本文件的语句格式为 ReWrite( <文件变量> ) ReWrite 过程首先用和文件变量 如 File1 相关联的文件名 如 filetest1.tes 建立一个 新文件 然后打开它 若已经存在同名的文件 则先删除它 再建立并打开它 即用 ReWrite 过程打开的文件内容总为空 与用 ReWrite 过程打开类型文件的不同之处是 当过程 ReWrite 打开的是文本文件时 读写方式变为只写方式 若作为参数的文件变量关联于一个空文件名 则打开一个系统默认的输出设备 用 Reset 方式打开文本文件的语句格式为 Reset( <文件变量> ) Reset 过程用于打开已经存在的文件 即和文件变量关联的文件必须存在 否则将产生 File not Found 的系统错误信息 若文件已经打开 则先关闭它 再重新打开它 使文件 指针指向文件的开始 与用 Reset 过程打开类型文件的不同之处是 当过程 Reset 打开的是文 本文件时 读写方式变为只读方式 为了避免系统给出这种错误提示 也可以在执行 Reset 过程之前利用 FileExists 函数检查文件是否存在 若作为参数的文件变量关联于一个空文件名 则打开一个系统默认的输入设备 用 Append 方式打开文本文件的语句格式为 Append( <文件变量> ) Append 过程只能用于打开已经存在的文本文件 且在文件打开时 将文件读写指针指向 文件的结束位置 以便于向文件追加新的内容 若作为参数的文件变量关联于一个空文件名 则打开一个系统默认的输入设备 3 读写文本文件 读写文本文件除了可以用读写类型文件的 Read 和 Write 过程外 还可以使用 Readln 和 Writeln 过程 由于文本文件的记录仅仅用回车-换行字符序列作标志 长度不固定 所以它 的文件读写指针只能由系统使用而不能被用户操作和使用 所以虽然过程名一样 但具体操 作和类型文件有较大的不同
    106
    若文件变量 file1 已关联有文件且已 打开 则用于文本文件的 4 种读写操作方式分别 介绍如下 用 Write 过程写文本文件的语句格式为 Write <文件变量>,<输出项表> 其中 输出项表由若干个常量 变量或运算表达式所组成 当输出项多于一项时 各项 之间要用逗号 分隔 各输出项除了不能是数组变量或记录变量外 几乎可以是所有的数 据类型 包括数组下标变量和记录元素变量 并可以跟输出格式说明 例如 Write(file1,'sin(x)=',sin(x+1):8:2,'Name is ',name1); 其中参数 File1 为文件变量 sin(x)= 和 Name is 为串常量 sin(x+1)为数值运算表 达式(其后的 :8:2 表示输出占用 8 个字符位置 其中 2 位小数) name1 为串变量 Write 过程具有执行运算和将各种类型数据转换为字符形式的功能 使用非常方便 用 Writeln 过程写文本文件的语句格式为 Writeln <文件变量>,<输出项表> Writeln 的参数构成和 Write 的参数构成相同 操作功能的主要区分是 其一 Eriteln 只 能用于文本文件 其二 Write 过程在完成各输出项的写操作后不产生 #10#13 序列 而 Writeln 过程在完成各输出项的写操作后 接着向文件输出一个 #10#13 序列 结 束一个记录的输出操作 结果表明利用 Write 过程 能多次地向一个记录添加数据项 但要结束记录必须调用 Writeln 过程 且没有输出项的 Writeln 过程 仅输出一个 #10#13 序列 配合使用 Write 和 Writeln 是将数组内容写入文件的重要方法之一 例如,若文件变量 file1 关联的文件已经打 开 二维数组 a 已经定义并已经赋值 则语句组 For I:=1 To 50 Do begin For j:=1 To 4 Do write(file1,a[I,j]); writeln(file1); end; 和语句组 For I:=1 To 50 DO Writeln file1,a[I,1],a[I,2],a[I,3],a[I,4] ; 的功能是完全相同的,都是把数组 a 的内容写入文件 并且把数组中的一行数据对应写在文件 的一个记录上 共写了 50 个记录 但是前者的通用性比后者要优的多 用 Read 过程写文本文件的语句格式为 Read <文件变量>,<读入项表> 其中 读入项表除不能出现记录名 文件变量名和数组名外 允许使用各种数据类型的 变量 包括表示数组元素的下标变量和表示记录元素的记录项变量 当读入项表中的读入项 多于一个时 要用逗号 分隔相邻项 由于读入项表中允许各种数据类型的变量 所以其 次序必须和文件记录上各项数据的次序 类型相一致 否则将产生类型转换错误或运算结果 错误
    107
    Read 过程执行完读操作后不跳到下一行 且按下述情况对空格 Tab 字符 跳格键 和 行结束标志作不同处理 当参数项为 String 类型时 读取行结束标志 并读为一个空串 当参数项为 char 类型时 由于要一个一个的顺序读取字符 所以能将文件结束标志字符 Ctrl-z 码值为#26 读入到对应变量 当参数项为数值类型时 将跳过数字串前的所有空格 Tab 字符和行结束标志 并把下 一个空格 Tab 字符或行结束标志作为本项数字串的结束 当文件指针已经指向文件结束 但 Read 操作仍要继续时将产生系统错误的报告信息 用 Readln 过程写文本文件的语句格式为 Readln <文件变量>[,<读入项表>] Readln 和 Read 不同的地方是 它只用于文本文件 读取完文件当前的一个记录 并跳 到下一条记录的开始 若没有下条记录则跳至文件的结束 没有读入项的 Readln 过程 仅将读操作位置跳至下条记录的开始或文件的结束 4 关闭文件 为了文件数据安全 在完成文件操作之后要关闭文件 关闭文本文件的语句格式为 Closefile ( <类型文件变量> ) 关闭文件时解除了文件类型变量和原文件之间的关联 即关闭文件之后 该文件类型变 量就可以用来和其他的文件建立新的关联 3 文本文件应用举例 例 4.3 通过本例说明 文本文件的读写操作过程和应特别注意的事项 二维数组和文件的配合应用 记录类型变量 记录元素数组和文件的配合应用 为达到以上举例的目的 安排的基本操作步骤为 先定义记录类型 记录变量 记录元素数组 实数数组等 建立两个文本文件 Ftest1.dat 和 Ftest2.dat,其中 Ftest1.dat 用于存放二维数组 a 的数据 ftest2.dat 用于存放一维记录数组 b 的数据. 接着利用随机函数为数组赋值并写到文件保存 依次从两个文件中读取数据并逐行显示 分析该例上述操作步骤和要求 在窗体上仅需要安排一个启动运算的按钮 在显示文件 内容后即执行结束操作 下面给出 Uint1 单元文件 type 部分的相关语句和 Button1 OnClick 事 件处理程序的完整清单如下 并请注意程序中的注释 unit Li42bUnit1; …… type Rec1 = Record str1:string[10]; x:integer; y:integer; 108
    {定义一个记录类型 rec1}
    end; …… procedure TForm1.Button1Click(Sender: TObject); var a : array[1..5,1..2] of integer ; {定义二维整数数组 a} b : array[1..5] of Rec1 ; {定义一维记录数组 b} rc : Rec1 ; {定义记录变量 rc} i,j : integer; file1,file2 : Textfile ; str1 : string; str2 : string[8]; begin assignfile (file1, 'ftest1.dat'); assignfile (file2, 'ftest2.dat'); Rewrite(file1);Rewrite(file2); randomize; for i:=1 to 5 do begin for j:=1 to 2 do {定义文本文件变量 file1 和 file2} {串变量 str1 可容纳多达 255 个字符} {串变量 str2 最多可容纳 8 个字符} {过程的可执行 区开始} {建立文件和文件变量的关联} {建立并打开两个新文件} {为了简化输入操作 用随机数 } {外循环控制共向文件写 5 个记录} {内循环完成一条记录的组织和写入}
    Begin a[i,j] := random(10000); {给数组元素赋值 0~10000 的随机数} write(file1,a[i,j],' '); {向记录中写一个数据 并在后面加空格字符} if j = 2 then {当 j 等于 2 时 向 file2 中写一条记录} begin rc.x := a[i,j-1]; rc.y := a[i,j]; {给各记录项变量赋值}
    rc.str1 := 'recor1:'+inttostr(i) ; //writeln (file2,rc); {文本文件不能读写整个记录} writeln (file2,rc.str1,' ',rc.x,' ',rc.y); {可以用记录域变量} end; end; {内循环结束} writeln(file1); {写一个行结束标志到文件 file1 完成一个记录} end; reset(file1); reset(file2); {重新打开文件 使文件指针指向文件开始} for i:=1 to 5 do {逐条读取显示 file1 的各条记录内容} begin str1 := '' ; for j:=1 to 2 do begin read(file1,a[i,j]); str1 := Str1 + floattostr(a[i,j])+' {清空显示用工作串变量}
    ';
    {每次读一项数据} {组织显示串} 109
    end; readln(file1); {使读操作跳到下条记录的开始} showmessage('show1 '+inttostr(i)+': '+str1); {显示一条记录内容} end; for i:=1 to 5 do begin // readln(file2,rc); // readln(file2,b[i]); readln(file2,rc.str1,rc.x,rc.y); {逐条显示 file2 的各条记录内容} {不能整条记录读取} {也不能用其他的方式读写整个记录} {只能逐项读取数据 要特别注意串}
    str1 := rc.str1 + inttostr(rc.x)+' '+inttostr(rc.y); showmessage('show3 '+str1); {显示一条记录内容} end; closefile(file1); closefile(file2); {关闭文件} close; {结束程序执行} end; {过程的代码区结束} end. {单元文件结束} 针对上述程序清单 还要说明几个重要问题 记录数组的数组元素是整个一条记录 引用时也要用其记录类型的域变量形式 在 本例中即为 b[i].str1 (b[i].x)和 b[i].y) 实验证明 Write 过程并不在输出项之间添加必须的空格字符 所以要输出项之间添 加 1 个以上的空格字符 或给出输出格式说明 见例 4.4 保证各项不相连接 Write 过程写串值时除了该项附有输出长度说明 有几个字符写几个字符 但在 Read 过程中 要按串变量的定义长度读取 所以要特别注意读串和写串的长度匹配 由于本例建立的是文本文件 程序执行后 在 Delphi5 的当前工作目录上 如本例的当 前目录为 C:\Progrom Files\Borland\Delphi5\Projects 可以看到文件 ftest1.dat 和 Ftest2.dat,并 且可以利用记事本 写字板等各种文字编辑软件打开查看或编辑它 例如用记事本打开的文 件内容如图 4-2 所示 实际上 用记事本等文字编辑软件建立数据文件是提供程序运行所需 输入数据的重要途径之一 因为用记事本等文字编辑软件建立数据文件操作灵活简便 完成 输入后仅要求用文本格式存盘 由此 也可进一步看到文本文件得到广泛应用的原因
    图 4-2 例 4.3 建立的文本文件的内容
    5. 和文本文件操作相关的其他函数和子程序 为了避免系统错误 在对文本文件操作中也可以通过文件操作函数或过程获得文件当前 110
    的状态信息 或利用文件操作函数或过程完成各类文件拷贝 删除等项操作 下面介绍与文 件操作相关的几个标准函数和过程的基本功能 1 Eof(f) 函数 Eof 函数判定文件指针是否指向和文件变量 f 相关联的文件的结束 是则返回值 True 否则返回值 False 可用于任何类型的文件 2 Seekeof(f) 函数 功能同 Eof 函数 但仅能用于文本文件 3 SeekEoln (f) 函数 SeekEoln 函数判定和文件变量 f 相关联的文件的当前行是否结束 是则返回值 True 否 则返回值 False 只能用于文本文件 4 Erase(f) 过程 Erase(f) 过程删除和文件变量 f 相关联的文件 只能用于文本文件 5 Deletefile(filename) 函数 Deletefile 函数删除文件 filename 为串常量或串变量 删除成功 返回值 true,否则返回 值 false 可用于任何类型的文件 6 ReName(f,newname) 过程 ReName 过程将和文件变量 f 相关联的文件改名为 newname newname 为串常量或串变 量 只能用于文本文件 7 ReNamefile(oldname,newname) 函数 ReNamefile 函数将老文件改名为新文件名 oldname 和 newname 都为串常量或串变量 改名成功 返回值 true,否则返回值 false 可用于任何类型的文件 8 FileExists(filename) 函数 fileExists 函数判别文件是否存在 filename 为串常量或串变量 文件存在 返回值 true, 否则返回值 false 可用于任何类型的文件 9 Filecreate(filename) 函数 filecreate 函数建立新文件 filename 为串常量或串变量 建立文件不成功 返回值 -1, 否则返回值为 数 可用于任何类型的文件 Delphi提供的文件操作函数非常丰富 这里仅简单介绍几个以便于建立一个概念 即程 序中需要进行某项操作时 最好要查阅一遍所有和这项操作相关的函数和过程 从中比较选 择最适合的函数或过程 上述文件操作函数在后面的程序举例中有部分应用 请注意举例程序注释中的说明
    4.2.6 利用对话框打开和保存文件
    在进行文件操作时 若要打开或要删除的文件不存在 要存盘文件但同名文件已经存在 或存盘目录不清楚等 都会带来系统出错或程序运行结果出错 为了简化打开和保存文件的 操作 Delphi 提供了下述的两个对话框 1 OpenDialog 对话框 OpenDialog 对话框用于提示用户选择要打开的文件 包括选择存放文件的路径 该对话 框的外观如图 4-3 所示 由图 4-3 可以看到 它和 Windows 的打开文件对话框十分相似 可 111
    以选择路径 按文件分类显示文件名等 它的主要属性和方法介绍如下 1 Title 属性 Title 属性 对话框的标题文字 如 打开 打开数据文件 等 2 InitailDir 属性 InitailDir 属性 对话框显示文件名的当前目录名 3 FileName 属性 FileName 属性代表用户选定的要打开的文件名 文件名可以直接键入 也可以点击 InitailDir 属性区右端的小三角按钮 先进行磁盘各文件夹的选择 然后在窗 选择文件名 被选择的文件名会自动写入 FileName 属性区 但要注意 不符合文件过滤条件的文件名将不 会显示在对话框窗
    Title 属性 InitailDir 属性
    Filter FileIndex 属性
    FileName 属性
    图 4-3 OpenDialog 对话框示意图
    4 Filter 属性 Filter 属性 窗 显示文件名的条件 即文件过滤条件 窗体设计时 当该组件被 选中后 在对 视器属性栏选择 Filter 属性 然后点击其值栏右端的三点按钮 即会弹出 图 4-4 所示的文件过滤条件编辑器 进行过滤条件编辑输入和修改 本图设置了 3 项内容 all files,Text file 和 Pascal program file 标记分别为 *.* *.txt 和*.pas
    图 4-4 文件过滤条件编辑器
    5 FileIndex 属性 FileIndex 属性 具体选用哪一条文件过滤条件 112
    6 Execute 方法 Execute 方法用来接收对 OpenDialog 对话框 按钮的操作 若 OK 按钮被点击则 返回值为 True 若 Cancel 按钮被点击则返回值为 False 2 SaveDialog 对话框 SaveDialog 对话框和 OpenDialog 对话框相比 除了操作目的分别为 打开 和 保存 外 其他主要属性和方法均与 OpenDialog 对话框的相应属性和方法相同 不再重述 3. OpenDialog 对话框和 SaveDialog 对话框的应用举例 例 4.4 利用 OpenDialog 对话框 SaveDialog 对话框 按钮和 Memo 组件 实现文本文 件的打开 文件内容浏览和修改 并在文件内容修改后换名存盘 步骤 1 依据题意设计窗体 依据题意 设计窗体如图 4-5 窗体上设置按钮 3 个 Caption 属性值分别为 打开文件 保存文件 和 结束运行 用于程序操作功能选择和控制 安排 Memo 组件一个 用于 显示和编辑文件内容 即在打开文件后将文件内容灌入 Memo 的编辑区 在执行保存文件时 把 Memo 编辑区的内容写入文件 安排 OpenDialog 对话框和 SaveDialog 对话框图标各一个 用于辅助打开文件和保存文件的操作 各组件的主要属性值设置见表 4.1.
    表 4.1
    组件 Button Caption Button Caption Button Caption Memo Name Name InitialDir OpenDialog Title Filter Name InitialDir SaveDialog Title Filter Name Name 属性 Name Button1 打开文件 Button2 保存文件 Button3 结束运行 Memo1 OpenDialog1 D;\Mydir1 打开文件 All File *.* ,Text File .TxT SaveDialog1 D;\Mydir1 保存文件 All File *.* ,Text File .TxT 支持选择路径 选择或输入文件名 并用 选定的文件名保存文件 要先在 D;盘建立 MyDir1 目录 以方便文件查找 显示和处理文件内容 支持选择路径 选择或输入文件名 并打 开所选定的文件 要 先 在 D; 盘 建 立
    例 4.3 各组件在窗体设计时的主要属性值
    属性设定值 程序功能说明 打开文件 并将其内容显示在 Memo1 的 文本编辑区 把 Memo1 文本编辑区的内容命名存盘
    结束程序运行
    MyDir1 目录 以方便文件查找
    2 编写程序 由于只 3 个按钮分别用以控制 打开文件 为它们编写事件处理程序
    保存文件
    和 结束程序
    仅需要分别
    113
    OpenDialog 图标 SaveDialog 图标 注 程序运 行时这两个 图标将不显 示 这里只 是为了窗体 设计时比较 清楚
    图 4-5 例 4.4 窗体设计示意图
    下面给出 Button1 和 Button2 的 OnClick 事件处理程序 procedure TForm1.Button1Click(Sender: TObject); begin If opendialog1.Execute Then {若按下 OpenDialog 的 打开 钮则执行语句} begin memo1.lines.loadfromfile(OpenDialog1.filename); showmessage('Open file '); end {把所选文件内容送 Memo1} {显示程序运行信息}
    end; procedure TForm1.Button2Click(Sender: TObject); begin If SaveDialog1.Execute Then {若按下 SaveDialog 的 保存 钮则执行语句} memo1.lines.savetofile(SaveDialog1.filename); {把 Memo1 内容存盘} showmessage('save file'); end; 编译运行上述程序 点击操作界面上 打开文件 按钮 选择文件名 如图 4-6 所示
    图 4-6 在 OpenDialog 窗口中选择要打开的文件
    比如选择了上例中建立的文件 Ftest1.dat,则运行界面外观如图 4-7 所示 114
    图 4-7 例 4.4 运行时打开文件后的外观示意图
    点击操作界面上 保存文件 按钮 即可在与图 4-6 类似的 SaveDialog 窗口内选择或键 入文件名 完成对 Memo1 内容存盘的操作 图示略 可以看到 利用 OpenDialog 和 SaveDialog 对话框不但可以随时查找路径和文件名 方 便了操作 而且大大提高了程序功能 还使得用户可以在程序运行时修改 变更文件名 增 强了程序的通用性 例 4.5 利用 Memo 组件和文本文件的配合 实现给数组的大量元素赋值 依据本例要求 在窗体上安排 4 个按钮组件 两个 Memo 组件 一个 OpenDialog 组件 和一个 SaveDialog 组件 各组件的主要属性设置见表 4.2
    表 4.2
    组 件 Button Caption Button Caption Name Caption Button caption Button Caption Memo Memo Name Name Name InitialDir OpenDialog Title Filter Name InitialDir SaveDialog Title Filter Name Name Name 属 性 Name
    例 4.5 各组件在窗体设计时的主要属性值
    属性设定值 Button1 打开文件 Button2 数组内容存盘 Button3 打开文件给数组赋值 Button4 结束运行 Button5 Memo2 存盘 Memo1 Memo2 OpenDialog1 D;\Mydir1 打开文件 All File *.* ,Text File .TxT SaveDialog1 D;\Mydir1 保存文件 All File *.* ,Text File .TxT 选择或输入文件名 并用选定的文 件名保存文件 选择或输入文件名 并打开所选定 的文件 把 Memo2 内容命名存盘到外部文 件 显示和处理文件内容 显示二维数组内容 程序功能说明 打开文件 并将其内容显示在
    Memo1 的文本编辑区 把二维数组的内容命名存盘为外部 文件 打开文件给数组赋值 在 Memo2 显 示数组内容 结束程序运行
    115
    窗体设计时外观如图 4-8 所示
    图 4-8 例 4.5 窗体外观设计示意图
    本例主要的事件处理程序清单如下 …… var {在单元文件的声明区定义本单元的各个过程都能引用的变量}
    Form1: TForm1; a : array[1..10,1..4] of real ; file1,file2 : textFile ; fileName1 : string; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); {Button1 处理程序} begin If opendialog1.Execute Then {若 OpenDialog 的 打开 按钮按下则执行…} begin memo1.lines.loadfromfile(OpenDialog1.filename); showmessage('open file'); label2.caption := OpenDialog1.filename ; end; end; procedure TForm1.Button2Click(Sender: TObject); var Sfn1 : string ; i,j : integer ; begin If SaveDialog1.Execute Then {文件内容显示到 Memo1} {显示打开的文件名}
    {Button2 处理程序}
    {若 SaveDialog 的 保存 按钮按下则执行…}
    begin sfn1 := SaveDialog1.filename ; If FileExists(sfn1) Then {若命名的文件名已存在 先删除它} If MessageDlg('Want to delete '+ExtractFileName(sfn1) +'?',mtWarning,[mbYes,mbNo],100)=mrYes Then 116
    DeleteFile(sfn1); assignfile(file2,sfn1); rewrite(file2); for i := 1 to 10 do
    {经对话认可后 删除指定的文件} {用选定的文件名关联文件变量 file2} {把数组中的 10 行数据写成 10 个文件记录}
    begin for j:=1 to 4 do write(file2,a[i,j]:8:2); {把实数值按指定格式 8 位 其中小数两位 写入到文件中 对整数值和串 用 变量 场宽 指定输出格式} writeln(file2); {添加行结束标志#10#13} end; showmessage('save array to file'); end; closefile(file2); {及时关闭打开的文件} end; procedure TForm1.Button3Click(Sender: TObject); {Button3 处理程序} var str1: string ; {串变量 Str1 长度可变 最多可容纳 255 个字符} str2 : string[12]; {串变量 Str2 长度限定 最多可容纳 12 个字符} i,j : integer; begin If OpenDialog1.Execute Then begin assignfile(file1,OpenDialog1.filename); reset(file1); for i:= 1 to 10 do begin for j:=1 to 4 do read(file1,a[i,j]); readln(file1); end; closefile(file1); memo2.lines.clear; for I:= 1 to 10 do begin str1 := '' ; for j:=1 to 4 do begin str(a[i,j]:10:2,str2); str1 := str1 +str2 ; 117
    {用双重循环读文件为二维数组赋值}
    {使读操作转到下一行的开始}
    {及时关闭文件} {清除 Memo2 文本区的现有内容 准备显示新内容}
    {把数值按指定格式(8 位场宽中 2 位小数)转换为串}
    end; memo2.lines.add(str1) ;
    {在 Memo2 的文本区后面添加一行内容}
    end; end; end; procedure TForm1.Button4Click(Sender: TObject);
    {Button4 处理程序}
    begin close ; end; procedure TForm1.Button5Click(Sender: TObject); {Button5 处理程序} begin If SaveDialog1.Execute Then {若按下 Savedialog 的 保存 钮则} memo2.lines.savetofile(SaveDialog1.filename); {把 Memo1 内容存盘} showmessage('save Memo2 to file'); end; 若没有可用的数据文件 在程序运行后 可以先在 Memo2 文本区编辑输入数据 共 10 行 每行 4 个数 数间有空格 如图 4-9 所示 数据输入编辑完成后再点击 Memo2 存盘 按钮并命名保存建立可用的数据文件 即可进行下一步操作
    图 4-9 例 4.5 中利用 Memo 组件建立数据文件
    若已经用文本编辑软件建立了数据文件 程序运行后可直接进行如下操作 先点击 打开文件 按钮选择 查看准备使用的文件内容 再点击 打开文件给数组赋 值 按钮打开已查看过的文件给数组赋值 并将数组内容显示在 Memo2 上 然后依次点击 数组内容存盘 和 Memo2 存盘 按钮将它们分别命名存盘 最后再点击 打开文件 按 钮查看刚建立的两个文件内容进行比较 理解文件操作过程 见图 4-10 其实 在本例操作界面上所设计的 5 个操作按钮之间可以有多种很有意义的操作步骤组 合 应上机多做练习 若练习修改本例程序 实现整数数组 字符串数组的读文件赋值和写 文件保存 对加深对文件应用概念的理 是很重要的 还应该和例 4.3 的程序加以比较 分清类型文件和文本文件操作函数和过程的异同之处 提高选择应用的能力 本例程序看起来较长 但各按钮的事件处理程序都相对独立 可以直接在其他程序中应 118
    用 尤其是利用文件给数组赋值 将数组内容存入文件和 Memo 组件和文件之间的内容交换 等 都是工程应用中最常用的操作 要认真学习掌握
    图 4-10 例 4.5 中比较数据文件和数组的内容
    4.3 查找和分类算法
    在现今的 机应用领域 应用占了 80%以上 远远超过了传统数值运算 而在管 理应用领域 娱乐应用领域 信息量浩大 要使得在海洋一样的信息资源中迅速查找到有用 的信息 就必须有好的查找算法和好的良好数据资源环境的支持 Delphi 在这些计算机 应用领域内显示出其巨大的优势 为了方便具有数据查找功能的程序设计 Delphi 提供了支持查找操作的函数和过程 例 如 Pos 和 StrPos 函数查找一个字符串在另一个字符串中出现的位置 FindFirst 函数可以在 指定路径上查找符合条件的第一个文件等 特别在数据库 系统中 都提供了各种对数据 进行检索 排序的命令 本节的教学目的主要是通过几种常用检索和排序算法的学习 了解 这些检索和排序操作在计算机中究竟是如何实现的
    4.3.1 查找算法及程序实现
    1 一维数组数据的顺序查找算法 对于命题 在一维数组中查找有或者没有其值等于给定数 x 的元素 最基本的方法就 是从数组的第一个元素开始 依次取出各个数组元素与 x 比较 一旦相等就说明数组中存在 有 x 查找过程就可以结束 若直到取出数组的最后一个元素都没有发现和 x 相等的数 则说明数组中不存在数 x 这种在全部查找范围内逐一比较的查找方法称为顺序查找算法 若数组 A 已赋值 能实现该算法的程序段如下 x := strtofloat(edit.text); For I:=1 To N Do If ( A[I] = x ) Then Break ; If (I <= N) Then {从 Edit1 组件读入要查找的数 x }
    {若某数组元素值和 x 值相同 退出循环} {依据退出循环时的查找位置判断查找结果} 119
    sowmessage( 'FOUND'+floattostr(x)+'IN LOCAL'+Inttostr(I) ) Else sowmessage( 'DON''T FOUND'+floattostr(x)) ; 由上述算法分析和实现该算法的程序说明 要在 N 个数中用顺序查找算法查找一个数 最快只需要比较一次 最慢要比较 N 次 平均要比较(N+1)/2 次 2 一维数组数据的对分查找算法 顺序查找算法简单 且对原数据的排列次序无任何要求 但执行效率较低 平均查找次 数为(N+1)/2 若数据为有序排列 或是按从小到大的次序存放 或是按从大到小的次序存放 则可采用对分查找算法 现假定数组 A 中的数据为从小到大依次排列存放 且用变量 L 和 H 分别表示要查找区间的下界和上界 作为特例 下界就是数组的第一个元素位置 1 上 界就是数组的最后一个元素位置 N 对分查找算法可用自然语言描述为 1 在指定查找区间中取中间位置上的数 A[(L+H)/2]和给定数 x 比较 若相等则查找 结束 否则 2 当 A[(L+H)/2]大于 x 说明若 x 存在 一定在 A[(L+H)/2]的左边 所以修改查找 区间 使 H = (L+H)/2 - 1 即舍去原查找区间右边的一半 当 A[(L+H)/2]小于 X 说明若 x 存在 一定存在于 A[(L+H)/2]的右边 所以修改查找区间 使 L = (L+H)/2 + 1 即舍去原查 找区间左边的一半 3 查找区间修改后 若新区间中的元素个数大于等于 1 即 H > L 则转到步骤 1 继续新一 查找 否则说明新区间中已无数据 即不存在数 x 结束查找过程 具体到在已知的 7 个数 1 3 4 6 8 9 14 中是否存在数 5,则查找过程为 1 由于查找区间下限为 1 上限为 7 (1+7)/2=4 则首先将 5 与第 4 个数 6 比较 2 由于 5 小于 6 第 4 个数之后一定不存在数 5 应该舍掉该部分区间,即将下次查找 区间的上限改为 4-1=3 3 由(1+3)/2=2 将 5 与第 2 个数 3 比较 4 由于 5 大于 6 第 2 个数之前一定不存在数 5 应该舍掉该部分区间,即将下次查找 区间的下限改为 2+1=3 5 由(3+3)/2=3 将 5 与第 3 个数 4 比较 6 由于 5 大于 4 第 3 个数之前一定不存在数 5 应该舍掉该部分区间,即将下次查找 区间的下限改为 3+1=4 这时就产生了下限大于上限的情况 说明当前查询区间已空 结束 查找过程 利用对分法 共经过 3 次比较 就可以 数 5 不在上述给定的数列中 由于对分查找算法每次比较均可确定放弃查找区间的一半 平均比较次数为 (log(N)+1)/2 远小于顺序查找算法的(N+1)/2 次 即执行效率大大提高 假设 数组 A 中的各元素值已经为从小到大排列存放 则能对数组 A 实现对分查找算 法的程序段如下 K := 1 ; x := strtoint(edit1.text) ; 120 {设定变量 K 的值表示查找状态 1 表示还没有找到} {读入要查找的整数 x }
    While (L<=H) AND (K=1) Do begin K0 := (L+H)/2 ; If A[K0]=x Then K := 0 Else If A[K0] {由于比较次数为知 用当型循环控制查找过程} { 查找区间的中点位置} {若存在 x 的值 设置结束循环的条件 K 值非 1}
    {否则依据比较结果修改下次的查找区间}
    {退出循环后 要依据退出循环的条件 H>L 或 K<>1 判断是否找到了 x} If (K<>1) Then showmessage('DON''T FOUND') Else showmessage(' FOUND and AT '+ inttostr(K0)); 应当提请注意的是 在本例中用变量 K 的不同取值来表示查找的状态 即先为变量 K 赋值 1 表示 尚未找到数 x 只要其值不为 1 就表示找到了数 x 把这类表示某种程 序运行状态的变量称为标志变量 合理使用标志变量控制程序的执行流程 是我们应当认真 学习掌握的编程技巧之一
    4.3.2 分类算法及程序实现
    为了在信息海洋中迅速地搜索查找到所需的数据 就应该采用高效率的查找算法 而高 效率的查找算法对原始数据的存放格式都是有要求的 本节学习能实现把数据按指定要求排 序的算法和程序 1. 选择排序算法 选择排序算法是基于顺序查找算法的一种排序算法 若要按从小到大的次序对给定的 N 个数排序 选择排序算法的自然语言描述如下 1 在 N 个数中找出最小的数 并将它放在第一个数的位置上 2 在余下的 N-1 个数中找出最小的数 并将它放在第二个数的位置上 3 在余下的 N-2 个数中找出最小的数 并将它放在第
  • 下载地址 (推荐使用迅雷下载地址,速度快,支持断点续传)
  • 免费下载 PDF格式下载
  • 您可能感兴趣的
  • vfoxpro教程  xpro1  富士xpro1  摄之宝xpro  xe1xpro1  xpro2  xpro近摄镜  富士xpro  xpro1x100s  富士xpro2