烟丝缭绕 さんのプロフィールdqf01フォトブログリストその他 ツール ヘルプ
このユーザーに知り合いを紹介しましょう

烟丝缭绕

好きなもの/好きなこと
リスト
フォト アルバムがありません。

dqf01

12月11日

又是一个不眠夜

    今晚又失眠了,脑子里乱糟糟,不知道想些什么。
    对与小安子我真的是似乎没有什么脾气,也许就是一物降一物,对于女人真的是笨的很。看到她很开心,看不到她心里烦,我知道我是喜欢她,可是不知道该怎么更好的让她明白我。将来会怎样,我不知道,只是看她现在过的很开心,很充实,也许我并不需要我的照顾。我在她眼里是什么样的角色,也许会有些滑稽。
    我的心结有十年了,早该结芭了,也是该划个句号的时候了。都说三十而立,年少轻狂无知固执的岁月过去了,现在的状况和儿时的梦太不一样,也许该抛弃那些梦了。
 
11月9日

vf&grid 白屏问题

数据源表(别名)上记录的物理删除
  网格对象对应的是一张表(TABLE)、别名(ALIAS)、视图(VIEW)、查询(QUWRY)之类的二维表格,在实际应用中,必然会遇到表(别名)中记录的删除的情况。我们知道,VFP中有两种记录删除命令:DELETE命令和PACK命令,DELETE命令是给删除的记录作标记,在执行PACK命令前并不从表上做物理删除, 而且做了删除标记的记录可以恢复。 PACK命令从表中物理删除标有删除标记的记录。当我们在表(别名)上用PACK命令删除记录时,会发现以该表(别名)为数据源 的网格控件会显示一片空白。如果我们仔细分析一下PACK命令的工作原理,就不难明白出现这种情况的原因: 当使用PACK命令时,VFP 把所有没有删除标记的记录复制到一个临时表(TEMPORARY TABLE),执行完PACK命令后,VFP 把原表从所在磁盘上删除,同时用原表名命名临时表。可以看出,在执行PACK命令过程中,作为网格对象数据源的表(别名)有过短暂的关闭,导致了网格对象找不到数据源从而显示一片空白,原表(别名)再打开也无济于事。找到了问题的关键所在,也就找到了解决办法:
  . 在网格清除(DESTROY)之前不使用PACK命令,如果要执行删除操作,可用DELETE命令,为了不在网格上显示被删除的记录,可用SET FILTER TO DELE()=.F.将它们屏蔽掉,在清除网格或其所在的表单时再执行PACK命令
  . 在界面设计时,将网格控件所在的表单或页面暂不显示,待激活显示时在其ACTIVATE事件过程中用命令方式重新设置网格控件的属性,完成网格操作后再隐藏网格对象,PACK命令的使用放在网格对象隐藏时完成。以上两种方法都可以解决由于删除命令而导致网格控件失效的问题。
11月8日

vf 事务处理

 

BEGIN TRANSACTION 命令

启动一个事务处理。仅对数据库中的表支持事务处理。

有关如何在数据库中创建和增加表的详细信息,请参阅CREATE DATABASE 和 ADD TABLE。
BEGIN TRANSACTION
备注
为了保存所做的修改并终止事务处理,应发出 END TRANSACTION 命令。如果事务处理失败(如服务器有故障或工作站有故障,以及没有提交事务处理就退出 Visual FoxPro),或者用户发出 ROLLBACK 命令,事务处理中的文件就恢复成原状态。
事务处理最深可嵌套五层,如果要进行第六层嵌套,则产生错误。
当修改一个数据库的记录,而该数据库又是事务处理的一部分时,网络上的其他用户在您终止事务处理之前无法访问(读或写)这些记录。
如果网络上的其他用户要访问您已经修改的记录,就必须等待您终止事务处理。在记录可用之前,用户将一直收到“记录不可用...请等待”的消息。因此,减小事务处理长度或在其他用户不需要访问期间进行事务处理就变得非常重要。
所有 IDX 索引文件 (非结构化, 或非基于 CDX 索引) 在事务处理期间必须关闭。在事务处理中只支持结构化索引。
在事务处理期间不支持下列命令和函数:
命令和函数
ADD TABLE                                   DELETE CONNECTION
APPEND PROCEDURES               DELETE DATABASE
CLEAR ALL                                    DELETE TRIGER
CLOSE ALL1                                   DELETE VIEW
CLOSE DATABASES1                   MODIFY CONNECTION
COPY INDEXES                             MODIFY DATABASE
COPY PROCEDURES                    MODIFY PROCEDURE
CREATE CONNECTION              MODIFY VIEW
CREATE DATABASE                   Remove Table
CREATE TRIGGER                       RENAME TABLE
CREATE VIEW                               REQUERY()
CREATE SQL VIEW

1 在事务处理期间执行 CLOSE ALL 命令,则关闭所有打开数据库中的表,但数据库仍保持打开状态;在事务处理期间执行 CLOSE DATABASES 命令,则关闭当前数据库中的所有表,但数据库仍保持打开状态。
另外, 不能对参与事务处理的表使用以下命令和函数:
命令和函数
ALTER TABLE                                MODIFY STRUCTURE
CREATE TABLE                             PACK
CURSORSETPROP()                       REINDEX
DELETE TAG                                 TABLEREVERT()
INDEX                                             ZAP
INSERT

示例
在下面示例中,打开数据库 testdata 的 customer 表,为 customer 表设置开放式表缓冲。接着显示 cust_id 和 company 字段内容。然后用缓存的数据替换 company 字段内容。
发出 BEGIN TRANSACTION 命令来启动一个事务处理。用 TABLEUPDATE() 把修改写到表中,再显示新的字段内容。发送 rollback 命令来恢复 company 字段的原来内容,然后再次显示 cust_id  和 company 字段的内容,这时的 company 字段保持原值。
CLEAR
CLOSE DATABASES

* 事务处理只支持数据库
OPEN DATABASE (HOME(2) + 'Data\testdata')

SET MULTILOCKS ON      && 要求缓存


USE customer
=CURSORSETPROP("Buffering",5)
? '原来的 company 字段'
LIST FIELDS cust_id, company NEXT 5
Replace All company WITH "***" && 修改字段内容

BEGIN TRANSACTION
   =TABLEUPDATE(.T.)
   GO TOP
   ? '修改后的 company 字段'
   LIST FIELDS cust_id, company NEXT 5
   ROLLBACK           && 恢复原始字段内容

=TABLEREVERT(.T.)
GO TOP
? '恢复后的 company 字段'
LIST FIELDS cust_id, company NEXT 5

11月7日

vf备忘

1.菜单. do 菜单名.mpr with this, .t.
2.去掉vf主窗口. application.visible=.f. (执行该命令的form设置为顶层表单)
3.事件循环. do events/clear events
4.sys(2003)当前目录,sys(5)返回当前 Visual FoxPro 的默认驱动器。
5.select distinct * from xxx into tables xx (删除重复记录)

转贴:事件驱动程序设计

9.5  事件驱动程序设计

    Windows提供了一个多任务的图形用户接口界面(GUI),在其环境下运行的程序是事件
驱动(Event Driven)的。也就是说,这种类型程序的结构和操作主要由用户生成的事件来
控制。Foxpro不仅支持结构化程序设计, 而且也支持事件驱动程序设计( Event- Driven
Programming)。

9.5.1  事件驱动的概念
    所谓事件(Event)就是一个对象能够识别的一个动作,例如, 单击鼠标或按下键盘上
的某个键等。事件只发生在程序运行阶段,而不会发生在编程阶段。事件驱动指的是程序代
码的执行依赖于一个事件的发生,它是一种非常适合于图形界面的编程方式。在Foxpro中,
一旦某个事件发生后,Foxpro立即调用相应的事件过程中的代码执行。
    事件驱动程序设计与传统的程序设计是有区别的。传统的程序设计是一种面向过程按顺
序进行的工作, 必须事先安排好先做什么, 后做什么, 因此属于顺序驱动(  Sequence
Driven)。顺序驱动在编程时已经规定好了用户必须遵守的操作顺序, 并且只有按照该顺
序操作,才能达到该程序运行的目标。相反,事件驱动程序设计所关心的是某个事件发生后
应该做什么,不必特意去编写调用程序,也不必编写按精确次序执行的每个步骤,只需要编
写响应事件的过程代码就可以了,而这些代码的执行由用户启动的事件来激发。对于事件驱
动程序来说,为完成给定的任务,用户可以自己规定其执行步骤,使用过Windows 应用程序
的读者对此会深有体会的。
    下面是一个简单的Foxpro事件驱动程序,读者可以上机运行该程序,从中体会事件驱动
的含义。

SET TALK OFF
ON ESCAPE WAIT "这就是事件驱动,按任一键继续..." WINDOW
CLEAR
I=1
DO WHILE INKEY()<>6
    ?"I=",I, "按Esc键响应事件,按End键结束运行"
    I=I+1
ENDDO
 
    程序中的ON ESCAPE命令是Foxpro事件驱动命令家族中的一员, 利用该命令可在程序运
行期间截取用户按下的Esc键并执行事先设计好的事件过程代码(WAIT " 这就是事件驱动,
按任一键继续..." WINDOW)。执行完事件过程代码后, 程序再从被中断命令的下一行继续
执行。INKEY()为Foxpro的标准函数,它可以返回用户按键的键值。若用户按下了End键(键
值为6),则结束该程序的运行。
    在Foxpro程序设计中,事件泛指所有系统内部和外部激发的动作。例如,键盘按键、程
序运行期间出错、激活菜单以及屏幕控制等都可以理解为事件。在此只对键盘、鼠标以及出
错事件的处理加以讨论,有关激活菜单和屏幕控制的事件程序设计将在下一章介绍。

9.5.2  键盘事件处理命令与函数
    ⒈  返回按键的ASCII码──INKEY()
    【格式】INKEY([<数值表达式>][,<字符表达式>])
    其中,<数值表达式>用来规定等待按键的时间(秒数),若为0值, 则停止程序运行,
直到按下任一键为止。省略<数值表达式>时立即返回所按下键的ASCII码。<字符表达式> 指
定等待按键期间是否显示光标或检测是否按下鼠标器的左按钮。如果需要显示光标,应使用
"S"(缺省方式)。若隐藏光标,可以使用"H"。如果需要检测是否按下鼠标器的左按钮,可
使用"M"。在允许检测鼠标的情况下,若按下了鼠标器的左按钮,INKEY()将返回151。M也可
以与S和H联用。有关INKEY()函数的返回值见附录C。
    【功能】返回用户按键的ASCII码或鼠标按钮值。

    例如,下面的程序一直在屏幕上显示“Foxpro 事件驱动程序设计.”,直到按下End 键
或鼠标器的左按钮为止。

SET TALK OFF
CLEAR
KeyorMouse=0
DO WHILE NOT (KeyorMouse=6 OR KeyorMouse=151)
   ? "Foxpro 事件驱动程序设计."
   KeyorMouse=INKEY("HM")
ENDDO
SET TALK ON
RETURN
  
    ⒉  返回结束全屏幕编辑命令时所按下键的值──READKEY()
    【格式】READKEY([<数值表达式>])
    其中,<数值表达式>用来确定最后一次的READ是如何被终止的,它可以为任何值。省略
时,返回结束全屏幕编辑命令时所按下键的值。
    【功能】返回结束全屏幕编辑命令时所按下键的值。
    【说明】
    ①该函数用来确定结束全屏幕编辑命令(APPEND、BROWSE、CHANGE、CREATE、 EDIT 、
INSERT、MODIFY和READ等)时所按下的键。
    ②结束全屏幕编辑时,若数据未修改,则返回0 ̄36之间的值,否则返回256 ̄292 之间
的值。有关READKEY()函数的返回值见附录D。
    ⒊  返回最后一次按键的ASCII码──LASTKEY()
    【格式】LASTKEY()
    【功能】返回用户最后一次按键的ASCII码值。
    【说明】当需要判断READ命令是否要停止时,可以使用LASTKEY( ) 函数, 其返回值与
INKEY()函数完全相同。
    【例9.20】编写一个浏览修改通讯录数据库(TXLK.DBF)记录的程序。在修改过程中,
如果用户按下了Esc键,则对当前记录的修改作废。

程序如下:
*PROG9_20.PRG浏览修改数据库记录
SET TALK OFF
DIME MA[6]
CLEAR
USE TXLK
SCAN
  SCATTER TO MA     &&将当前要修改的记录存入数组MA
  @2,10 SAY "姓名:" GET 姓名
  @3,10 SAY "性别:" GET 性别
  @4,10 SAY "职务:" GET 职务
  @5,10 SAY "工作单位:" GET 工作单位
  @6,10 SAY "邮政编码:" GET 邮政编码
  @7,10 SAY "通讯地址:" GET 通讯地址
  @10,20 SAY "按Esc键取消对当前记录的修改,按Ctrl+W结束"
  READ
  IF READKEY()=268   &&按下Esc键
     GATHER FROM MA  &&恢复修改之前记录内容
  ENDIF
  IF LASTKEY()=23    &&按Ctrl+W结束
    EXIT
  ENDIF 
ENDSCAN    
USE
SET TALK ON
RETURN


    ⒋  捕获Esc键──ON ESCAPE
    【格式】ON ESCAPE [<命令>]
    ON ESCAPE命令是Foxpro事件驱动命令家族中的一员,<命令>可以是一个用DO 命令调用
的程序、过程或一条其它的Foxpro命令。
    【功能】设置和捕获当Esc键被激活时要执行的命令或程序。
    【说明】
    ⑴当ESCAPE被设置为OFF(即SET ESCAPE OFF)时,ON ESCAPE后面的命令不会执行。
    ⑵不带<命令>选项可取消ON ESCAPE的设置。
    ⒌  捕获任一按键──ON KEY
    【格式】ON KEY [<命令>]
    【功能】设置和捕获在程序运行期间任一键被按下时要执行的命令、程序或过程。
    【说明】
    ⑴当ON ESCAPE和ON KEY均处于有效状态时,按下Esc键后将优先执行ON ESCAPE 后面的
命令。
    ⑵不带<命令>选项可取消ON KEY的设置。
    ⒍  定义热键──ON KEY =|ON KEY LABEL
    【格式】ON KEY=[<数值表达式>][<命令>]
            ON KEY [LABEL<键名>][<命令>]
    其中,<数值表达式>为要定义热键的键码,可供使用的键码值见附录E。<键名>为要定
义热键的识别名称,Foxpro中每个按键对应的识别名见附录F。
    【功能】设置和捕获在程序运行期间定义的“热键”被按下时要执行的命令、程序或过
程。
    【说明】
    ⑴利用ON KEY=命令只能定义一个热键,使用ON KEY LABEL命令可以定义多个热键。
    ⑵利用ON KEY LABEL命令定义的热键在程序结束后仍然有效,因此在结束程序前应使用
ON KEY命令清除所定义的热键。
    【例9.21】热键的使用举例。

程序如下:
*PROG9_21.PRG热键的使用举例
SET TALK OFF
ON ESCAPE DO STOP
ON KEY LABEL F1 DO PROC1
ON KEY LABEL F2 DO PROC2
ON KEY LABEL F3 DO PROC3
CLEAR
LP=.T.
DO WHILE LP
   ? "Foxpro 事件驱动程序设计中热键的使用,按Esc键结束"
ENDDO
ON KEY  &&清除ON KEY LABEL命令定义的热键
SET TALK ON
RETURN
PROCEDURE PROC1
WAIT "用户按下了F1键,按任一键继续..."WINDOW
RETURN
PROCEDURE PROC2
WAIT "用户按下了F2键,按任一键继续..."WINDOW
RETURN
PROCEDURE PROC3
WAIT "用户按下了F3键,按任一键继续..."WINDOW
RETURN
PROCEDURE STOP
  LP=.F.
RETURN

    ⒎  按键的堆栈操作──PUSH KEY/POP KEY
    在一个程序中,允许使用ON KEY LABEL命令定义多个热键。假如在过程或自定义函数中
定义的热键名与主程序中定义过的热键名相同,则执行过程或自定义函数后,主程序中同名
的热键将会被覆盖。为了解决这一问题,Foxpro提供了按键的堆栈操作命令PUSH KEY和POP
KEY。
    ⑴PUSH KEY命令
    【格式】PUSH KEY [CLEAR]
    【功能】将当前所有ON KEY LABEL命令的设置存入堆栈(内存中某个区域), 带CLEAR
选项可清除当前定义的所有热键。
    ⑵POP KEY命令
    【格式】POP KEY [ALL]
    【功能】从堆栈中取出存入的ON KEY LABEL命令的设置,带ALL 选项将清除堆栈中所有
的ON KEY LABEL命令。
    通常在过程或自定义函数中定义热键之前先执行PUSH KEY CLEAR命令,而在返回前再执
行POP KEY命令。例如,下面程序中的过程PROC就是一例。

SET TALK OFF
ON KEY LABEL F1 DO PROC
ON KEY LABEL F2 LP1=.F.
LP1=.T.
DO WHILE LP1
   ? "按F1键执行过程,按F2键结束!"
ENDDO
ON KEY
PROCEDURE PROC
PUSH KEY CLEAR            &&将当前的ON KEY LABEL命令入栈
ON KEY LABEL F2 LP2=.F.   &&重新定义热键F2的功能
LP2=.T.
CLEAR
DO WHILE LP2
  ? "正在执行过程,按F2键返回主程序执行!"
ENDDO
CLEAR
POP KEY    &&恢复以前存入的热键

9.5.3  出错事件及相关函数
    ⒈  捕获出错事件──ON ERROR|ON READERROR
    ⑴ON ERROR命令
    【格式】ON ERROR [<命令>]
  【功能】设置错误事件发生时要执行的程序、过程或命令。不带<命令>选项时将取消原来
ON ERROR的设置。
    通常将ON ERROR命令与出错事件有关的函数联用,来编写错误处理程序。
    ⑵ON READERROR命令
    【格式】ON READERROR [<命令>]
    【功能】设置在执行@…GET期间,出现数据输入错误时要执行的程序、过程或命令。不
带<命令>选项时将取消原来ON READERROR的设置。
    ON READERROR命令主要用来定义用户输入错误时的提示信息。输入GET 域时常见的错误
有非法日期、输入的数据超过RANGE选项指定的范围、不符合VALID选项所检查的输入值。
    ⒉  与出错事件有关的函数
    ⑴返回错误代码──ERROR()
    【格式】ERROR()
    【功能】返回错误代码。
    ⑵返回错误信息──MESSAGE()
    【格式】MESSAGE([1])
    【功能】返回当前的错误信息字符串(不带参数1)或出错的命令行(带参数1)。
    ⑶返回当前执行程序的名称──PROGRAM()
    【格式】PROGRAM()
    【功能】返回当前执行的程序的名称。
    ⑷返回当前执行程序的行号──LINENO()
    【格式】LINENO()
    【功能】返回当前正在执行的程序的行号。

    【例9.22】编写一个出错处理程序,要求给出错误代码、出错信息、错误命令行、出错
程序名称及错误程序行等信息。
    本例属于错误事件驱动程序设计,可以使用ON ERROR命令和ERROR() 、 MESSAGE( ) 、
PROGRAM()、LINENO()函数来编程。为了捕获出错事件, 可以在程序中人为设置一带错误的
命令行。

程序如下:
*PROG9_22.PRG出错处理程序
SET TALK OFF
ON ERROR DO ERRPROC WITH ERROR(),MESSAGE(),MESSAGE(1),;
    PROGRAM(),LINENO()  &&设置错误事件发生时要执行的过程
? A,B,C  &&设置一错误命令行
ON ERROR
RETURN
PROCEDURE ERRPROC  &&出错处理过程
PARAMETERS P1,P2,P3,P4,P5
CLEAR
@5,5 SAY "错误代码:  "+LTRIM(STR(P1))
@6,5 SAY "出错信息:  "+P2
@7,5 SAY "错误命令行:"+P3
@8,5 SAY "出错程序名:"+P4
@9,5 SAY "错误程序行:"+LTRIM(STR(P5))
WAIT "按任一键继续..." WINDOW
RETURN

转贴:Foxpro备注型字段的编程

 9.2  Foxpro备注型字段的编程

    在开发数据库管理系统时,有时需要对备注字段进行操作。Foxpro中的备注字段的内容
可以理解为一个长字符串,因此对于字符串操作的有关函数也适用于备注字段。为了便于备
注字段的处理,Foxpro提供了专门用于备注字段操作的命令和函数。

9.2.1  备注字段操作命令及函数
    ⒈  将文件存入备注字段──APPEND MEMO
    【格式】APPEND MEMO <备注字段> FROM <文件名> [OVERWRITE]
    【功能】将指定文件的内容存入数据库当前记录的一个备注字段中。
    【说明】
    ⑴指定的<文件名>应为该文件的全名,可以带盘符和路径。
    ⑵该命令以追加方式将文件的内容存入备注字段中已存在内容的末尾,带OVERWRITE 选
项将会覆盖原来备注字段的内容。
    ⒉  复制备注字段内容到磁盘文件──COPY MEMO
    【格式】COPY MEMO <备注字段> TO <文件名> [ADDITIVE]
    【功能】将数据库当前记录中指定备注字段的内容复制到磁盘文件中。
    【说明】
    ⑴命令中<文件名>前可以带盘符和路径,如果没有指定文件的扩展名,则取缺省扩展名
.TXT。
    ⑵如果使用ADDITIVE选项,则以追加方式将备注字段内容添加到文件末尾,否则将会覆
盖原来文件的内容。
    ⒊  备注字段的编辑或浏览──MODIFY MEMO
    【格式】MODIFY MEMO <备注字段1> [,<备注字段2>,…]
              [NOEDIT] [NOWAIT] [RANGE <数值表达式1>,<数值表达式2>]
              [[WINDOW <窗口名1>] [IN [WINDOW]<窗口名2>|IN SCREEN]]
              [SAVE]
    【功能】利用Foxpro的文本编辑器来编辑或浏览指定备注字段的内容。
    【说明】
    ⑴如果在命令中指定多个备注字段,则按层叠方式排列备注字段窗口,最后一个备注字
段的窗口在最前端。
    ⑵使用NOEDIT选项只能浏览备注字段的内容,不允许进行修改。
    ⑶NOWAIT选项仅用于程序方式,允许程序在备注字段窗口打开的同时继续执行。
    ⑷使用RANGE选项可以在进入编辑窗口后以突出方式显示要编辑的区域, 编辑区域的起
止位置分别由<数值表达式1>和<数值表达式2>指定。如果指定的两个数值表达式的值相等,
则进入编辑窗口后光标便停留在该位置上。
    ⑸缺省情况下(IN SCREEN)是在Foxpro桌面窗口上打开编辑窗口,如果使用 WINDOW <
窗口名1>可将用户定义的窗口作为备注字段的编辑窗口,也可以使用IN [WINDOW]<窗口名2>
在当前活动窗口内打开备注字段编辑窗口。如果希望在完成备注字段的编辑后保留编辑窗口,
可以使用SAVE选项。
    ⒋  关闭备注字段编辑窗口──CLOSE MEMO
    【格式】CLOSE MEMO <备注字段1> [,<备注字段2>,…] [ALL]
    【功能】关闭使用MODIFY MEMO或BROWSE命令打开的备注字段编辑窗口。如果使用ALL选
项,将关闭所有打开的备注字段窗口。
    ⒌  设置备注字段的输出宽度──SET MEMOWIDTH
    【格式】SET MEMOWIDTH TO <数值表达式>
    【功能】设置备注字段的输出宽度。
    【说明】
    ⑴命令中<数值表达式>的取值范围在8 ̄256之间,缺省值为50。
    ⑵在Windows下的Foxpro中,输出将受当前系统桌面或窗口的字型、字号大小的限制。
    ⒍  指定备注字段编辑窗口──SET WINDOWS OF MEMO
    【格式】SET WINDOWS OF MEMO TO <窗口名>
    【功能】指定编辑或浏览备注字段内容时所使用的编辑窗口。
    例如:
      .
      .
      .
    USE MYDBF
    SET WINDOWS OF MEMO TO memowin  &&其中memowin为用户定义的窗口的名称
    MODIFY MEMO mymemo NOEDIT       &&在memowin窗口内浏览备注字段mymemo的内容
      .
      .
      .
    ⒎  返回备注字段的行数──MEMLINES()
    【格式】MEMLINES(<备注字段名>)
    【功能】返回指定备注字段中内容的行数。
    【说明】该函数的返回值将受当前SET MEMOWIDTH设置的影响。 如果当前指定的备注字
段为空,则返回0值。
    例如:
CLEAR
USE MYDBF
SET MEMOWIDTH TO 70
SCAN
  IF MEMLINES(mymemo)=0
     WAIT STR(RECNO())+"号记录的备注字段为空" WINDOW NOWAIT
  ELSE
    DISPLAY mymemo
  ENDIF
ENDSCAN
   .
   .
   .

    ⒏  返回备注字段中指定行的内容──MLINE()
    【格式】MLINE(<备注字段名>,<数值表达式1>[,<数值表达式2>])
    其中,<数值表达式1>表示所取内容的行号,<数值表达式 2>表示从距离备注字段开头<
数值表达式2>个字符位置开始计算行号。
    【功能】返回指定备注字段中指定行的内容。
    【说明】该函数返回的内容将受当前SET MEMOWIDTH设置的影响。 如果指定的的行数超
出该备注字段的总行数,则返回一个空串。
    除了上面介绍的备注字段操作命令和函数以外,Foxpro中的许多命令和函数都与备注字
段有关。下面列出一些常用的命令和函数,供读者编程时参考。
    支持备注字段操作的其它命令如下:
    @…SAY/GET、@…EDIT、?、??、\、\\、LIST、DISPLAY、APPEND、BROWSE、 CHANGE 、
EDIT、REPLACE、SCATTER、GATHER、SAVE TO、RESTORE FROM、 SAVE  SCREEN 、 RESTORE
SCREEN、SAVE WINDOW、RESTORE WINDOW等。
    支持备注字段操作的其它函数如下:
    AT()、ATC()、ATCLINE()、LEFT()、RIGHT()、SUBSTR()、RAT()、RATLINE()、LEN()、
EMPTY()等。

9.2.2  备注字段编程举例

    【例9.13】在计算机上进行某门课程的考试时,首先应该考虑试题的安全问题。假如需
要用Foxpro编写一个“Foxpro程序设计”课程的考试系统,试题内容采用数据库的备注字段
来存放。请编写一个对数据库备注字段进行加密的程序。假设试题库名称为TEST.DBF,每一
道试题的内容存入名为content的备注字段中。
    分析:本题求解的关键在于确定加密算法。可以按照这样的思路来确定备注字段的加密
算法:首先计算出备注字段中每个字符的ASCII码,然后用该字符的ASCII码值减去该字符在
备注字段中的位置值被10除后所得到的余数,得到一个新的ASCII码值, 最后再把所得到的
全部ASCII码组合成一个字符串,即为加密结果。

程序如下:
*PROG9_13.PRG的功能是实现备注字段的加密
SET TALK OFF
USE TEST
SCAN
  length=LEN(content)  &&计算备注字段的长度
  string=""
  FOR I=1 TO length
    string=string+CHR(ASC(SUBSTR(content,I,1))-I % 10)  &&加密处理
  ENDFOR
  REPLACE content WITH string  &&将加密之后的内容写回原备注字段
ENDSCAN
USE
SET TALK ON
RETURN


    【例9.14】在Foxpro程序设计课程上机考试系统中,上机操作的题目存放在名为 EXAM
.DBF的数据库中,其中的两个备注字段EXAM_DOC和EXAM_PRG分别用来存放改错题(或编程题)
的试题说明和程序代码(或部分程序代码)。为了便于考生操作,要求在抽取试题时,按照
Foxpro中注释语句的格式将试题说明与程序代码合并后存入一个文件中(试题说明在程序代
码之前)。假设改错题的程序文件名为MODI.PRG,编程题的程序文件名为PROG.PRG。请编写
程序实现这一功能。

程序如下:
*PROG9_14.PRG
SET TALK OFF
CLEAR
?"正在抽取Foxpro程序设计试题,请等候..."
SELE 1
USE EXAM INDEX EXAM  &&打开试题库及索引文件
MODI_NO="225000"
PROG_NO="235000"
RANDUM=0
DO WHILE RANDUM=0
  RANDUM=INT(RAND(-1)*(90)) &&随机抽取一套试题
ENDDO
IF RANDNUM<10
   MYSTR="0"+STR(RANDNUM,1)
ELSE
   MYSTR=STR(RANDNUM,2)
ENDIF
MODI_NO=MODI_NO+MYSTR    &&改错题目编号
PROG_NO=PROG_NO+MYSTR    &&编程题目编号
SEEK MODI_NO  &&抽取改错题目
=MAKEDOC("MODI.PRG") &&对改错题的说明进行注释,并存入MODI.PRG
COPY MEMO exam_prg to MODI.PRG ADDI &&将改错题的程序代码追加到试题说明之后
SEEK PROG_NO  &&抽取编程题目
=MAKEDOC("PROG.PRG")  &&对编程题的说明进行注释,并存入PROG.PRG
COPY MEMO exam_prg TO PROG.PRG ADDI &&将编程题的程序代码追加到试题说明之后
USE
RETURN
FUNCTION MAKEDOC  &&建立注释文件
PARA PROGNAME
SET TEXT TO &PROGNAME ON NOSHOW  &&设置输出命令\将结果存入指定文件中
SET MEMOWIDTH TO 78     &&设置备注字段每行宽度为78个字符
NUM=MEMLINES(exam_doc)  &&取备注字段行数
\*                          ●试题说明●
FOR I=1 TO NUM
  STRING="* "+MLINE(exam_doc,I)  &&对该行进行注释说明
  \<<STRING>>                    &&将STRING的内容输出到文件
ENDFOR
SET TEXT TO    &&关闭建立的输出文件
RETURN

                          9.3  Foxpro通用型字段的编程

    在Foxpro中,对OLE技术的支持是通过通用型字段(General)实现的。使用OLE技
术,可以很方便地将其它Windows 应用程序生成的数据、 图像、 声音等信息链接或嵌入到
Foxpro的数据库中,从而开发出图文声像并茂的数据库应用系统。

9.3.1  在Foxpro中使用OLE技术
    ⒈  什么是OLE
    对象的链接和嵌入(OLE──Object Linking and Embedding)是Windows 环境下应
用程序之间传送和共享信息所使用的一种技术。正如其名称所提示的那样,它提供了两种服
务功能。应用程序之间传送的信息可以是一张表格、一幅图像、一段文字或一种声音,这些
信息统称为对象(Object)。对象由Windows应用程序生成。例如,使用Excel制作的表格、
由画图程序产生的位图、由扫描仪扫描的图片等。
    嵌入一个对象时,是对源信息的一个拷贝,拷贝过来的信息与源应用程序不再有任何联
系,编辑所嵌入的对象或编辑源文档互不影响。例如,在Foxpro数据库的一个通用字段中嵌
入了一个位图,如果在Foxpro中修改了该位图,源位图文件不会改变,反之亦然。而链接对
象则不同, 它不是源信息的拷贝, 而是只记录了指向源应用程序文档的指针。 因此, 在
Foxpro中编辑一个链接的对象时,实际上是编辑源文档中的信息。而在源应用程序中对该文
档所作的任何修改,也都会直接反映到Foxpro应用程序中。可见,使用对象的链接操作,实
现了信息的共享,保证了数据的一致性。
    在Foxpro中,对OLE的支持是借助于通用型字段(General)实现的。General字段的
宽度由系统指定为10,当没有链接或嵌入对象时,该字段以“gen”标识, 若在其中已经存
在链接或嵌入的对象,该字段以“Gen”标识。与备注字段的操作类似, 通用字段也存在输
入、输出和修改等操作。这些操作既可以在Foxpro系统菜单下完成,也可以使用命令编程实
现。
    ⒉  在Foxpro系统菜单下使用OLE
    ⑴  嵌入或链接对象
    在菜单方式下嵌入或链接指定对象到通用字段分以下两种情况:
    ①嵌入对象
    当对象为一个.BMP(或.PCX)格式文件时,按照图9.1 所示的操作步骤可以嵌入所选对
象,关闭通用字段编辑窗口后,字段标识由“gen”变成“Gen”。

                          打开要嵌入或链接对象的数据库
                                      ↓
                          选择Record菜单中的Append选项
                                      ↓
                  双击通用字段中的gen标识,打开通用字段编辑窗口
                                      ↓
                         选择Edit菜单中的Insert Object选项
                                      ↓
                        单击Insert Object对话框中的File按钮
                                      ↓
                          选择.BMP或.PCX文件类型和文件名
                                      ↓
                    选定文件后单击Insert按钮将对象嵌入通用字段

                                图9.1 嵌入对象

    ②使用剪贴板嵌入或链接对象
    当对象是通过其它应用程序生成时(如由Excel产生的图表或由画图程序生成的位图),
就可以使用剪贴板来嵌入或链接对象。操作步骤如图9.2所示。
 
                     在应用程序中选择所需对象并复制到剪贴板
                                      ↓
                   进入Foxpro环境打开要嵌入或链接对象的数据库
                                      ↓
                          选择Record菜单中的Append选项
                                      ↓
                  双击通用字段中的gen标识,打开通用字段编辑窗口
                                      ↓
                  选择Edit菜单中的Paste选项或Paste special选项
                                      ↓
                            ┌────┴─────┐
                            ↓                    ↓
                   选择Paste为嵌入对象    选择Paste special选项
                                                  ↓
                                   在Paste special对话框中选择数据类型
                                                  ↓
                                  ┌───────────────┐
                                  ↓                              ↓
                       单击Paste按钮为嵌入对象      单击Paste Link按钮为链接对象

                            图9.2  利用剪贴板嵌入或链接对象

    ⑵  显示对象
    打开所需要的数据库,定位到相应的记录,双击通用字段中的“Gen ”标识即可显示该
对象。在显示过程中,通用字段编辑窗口的大小、位置都可以调整。
    ⑶  编辑对象
    打开通用字段编辑窗口后,双击该对象可以自动进入生成该对象的应用程序窗口,此时
可以修改对象。若要删除一个对象,可先显示该对象,然后从Foxpro的Edit菜单中选择 Cut
即可。删除对象后,通用字段的标识又变为“gen”。

9.3.2  Foxpro通用字段的编程命令  
    在Foxpro中,支持OLE操作的通用字段编程命令有三条,它们分别实现对象的输入、
编辑和输出。
    ⒈  嵌入或链接对象──APPEND GENERAL
    【格式】APPEND GENERAL <通用字段> FROM <文件> [LINK] [CLASS<OLE类>]
    其中,<通用字段>用来存储输入的OLE对象。<文件>为存有OLE对象的文件名称,
可以包含盘符和路径。使用LINK选项表示链接对象,省略时为嵌入对象。 CLASS<OLE类>
指定OLE对象的类型,省略时由对象文件名的扩展名决定。
    【功能】将<文件>指定的对象嵌入或链接到数据库当前记录的通用字段中。

    【例9.15】首先在Foxpro环境下建立数据库OLEDBF.DBF的结构,该数据库含有filename
(字符型、宽度为12)和object (通用型字段)两个字段。 根据如下要求编程:将C盘上
WINDOW目录下扩展名为.BMP的位图文件以对象方式嵌入到OLEDBF.DBF的通用字段中,并将该
对象的文件名填入filename字段中。

程序如下:
*PROG9_15.PRG数据库通用字段的输入
SET TALK OFF
CLEAR
USE OLEDBF
counter=ADIR(bmpfile,"C:\WINDOWS\*.BMP") &&将.BMP文件的各项信息拷贝到数组bmpfile
FOR i=1 TO counter &&将.BMP文件的文件名及对象存入数据库
  APPEND BLANK
  REPLACE filename WITH bmpfile[i,1]  &&将该对象的文件名填入filename字段
  APPEND GENERAL object FROM "C:\WINDOWS\"+bmpfile[i,1] &&对象嵌入
ENDFOR
USE
SET TALK ON
RETURN


    例9.15程序中,ADIR()为Foxpro的内部函数,其作用是将C盘WINDOWS目录下所有扩展名
为.BMP的位图文件的各项信息(包括文件名、文件大小、建立文件日期、建立文件时间以及
文件的属性)拷贝到一个5列二维数组(bmpfile)中,并返回拷贝的文件个数(counter)。
二维数组bmpfile的每一行分别用来存储一个文件的文件名(字符型)、文件大小(数值型)
、建立文件日期(日期型)、建立文件时间(字符型)和文件属性(字符型)五项信息。命
令APPEND GENERAL object FROM "C:\WINDOWS\"+bmpfile[i,1] 将指定的对象嵌入到通用字
段object中。

    ⒉  编辑对象──MODIFY GENERAL
    【格式】MODIFY GENERAL <通用字段1> [,<通用字段2>,…]
              [NOMODIFY]
              [NOWAIT]
              [WINDOW <窗口名1>]
              [IN [WINDOW] <窗口名2>|IN SCREEN]
    其中,<通用字段1>、<通用字段2>…为要编辑的通用字段。NOMODIFY选项限制通用字段
中的OLE对象只能显示,不能编辑修改。NOWAIT选项允许程序不等待对象编辑窗口的关闭
而继续执行后面的命令,只在程序方式下有效。使用WINDOW <窗口名1>可以指定对象的编辑
窗口,省略时使用系统缺省编辑窗口。使用IN[WINDOW] <窗口名2>可以指定对象编辑窗口的
父窗口,省略时为Foxpro的桌面窗口。使用IN SCREEN选项指定Foxpro 的桌面窗口作为对象
编辑窗口的父窗口。
    【功能】打开一个或多个编辑窗口来浏览或编辑一个或多个通用字段中的OLE对象。
    【说明】
    ⑴利用MODIFY GENERAL命令打开通用字段编辑窗口后,只需用鼠标双击该OLE对象就
可以启动该对象的应用程序窗口进行编辑。
    ⑵缺省情况下,Foxpro取当前工作区中数据库的通用字段进行浏览或编辑,如果要浏览
或编辑其它工作区中数据库的通用字段,只需在该字段名前加上所属数据库的别名即可。

    【例9.16】根据例9.15所建立的数据库OLEDBF.DBF,编写一个对通用字段(object)中
的OLE对象进行修改的程序。

程序如下:
*PROG9_16.PRG的功能是修改通用字段中的OLE对象
SET TALK OFF
CLEAR
USE OLEDBF
SCAN
  MODIFY GENERAL object   &&打开通用字段编辑窗口,编辑其中的OLE对象
  WAIT "是否继续修改对象(Y/N)?" window TO yesno
  IF UPPER(yesno)#"Y"
     EXIT
  ENDIF  
ENDSCAN
USE
SET TALK ON
RETURN

    ⒊  输出对象──@...SAY
    【格式】@ <行号>,<列号> SAY <通用字段>|<文件名> BITMAP
              [STYLE <字符表达式>]
              [SIZE <数值表达式1>,<数值表达式2>]
              [CENTER] [ISOMETRIC|STRETCH]
    其中,<通用字段>指定要显示的OLE对象。使用<文件名> BITMAP可以显示由<文件名>
指定的位图文件。STYLE <字符表达式>用来指定所显示位图的透明属性,<字符表达式>的值
为“T”时表示透明(即显示的位图不覆盖当前位置上原来的位图),为“Q”时表示不透明
(即显示的位图覆盖当前位置上原来的位图)。SIZE <数值表达式1>,<数值表达式2>指定对
象的显示区域(高度和宽度)。CENTER选项控制对象在编辑窗口中央显示。ISOMETRIC 选项
使对象在SIZE指定的区域按比例(即按对象各边等长放缩)显示而不变形。STRETCH 选项使
对象在SIZE指定的区域内显示(即按区域大小进行放缩)。
    【功能】在指定的行、列位置显示通用字段中的OLE对象或位图文件。

    【例9.17】编程显示通用字段中的OLE对象。

程序如下:
*PROG9_17.PRG的功能是显示通用字段中的OLE对象。
SET TALK OFF
CLEAR
USE OLEDBF
SCAN
  @2,5 SAY object SIZE 20,40 ISOMETRIC  &&显示object中的OLE对象
  WAIT "是否继续显示对象(Y/N)?" window TO yesno
  IF UPPER(yesno)<>"Y"
     EXIT
  ENDIF  
ENDSCAN
CLEAR
USE
SET TALK ON
RETURN

转贴:Foxpro实用编程技术(1)

第九章  Foxpro实用编程技术

    本章从应用的角度介绍一些Foxpro实用编程技术,其中包括数组程序设计、备注字段的
编程、通用字段的编程、网络环境下程序设计、事件驱动程序设计和串行通讯程序设计六个
专题。
    本章学习要点:在理解有关概念的基础上,重点掌握每个专题所涉及到的编程命令及函
数的使用,掌握每个专题的基本编程方法。


                              9.1  数组程序设计

    同其它高级程序设计语言一样,在Foxpro中也可以使用数组。数组是具有相同名字的有
序数据的集合,由若干个数组元素组成。数组中的每个元素相当于一个内存变量,因此所有
对内存变量进行操作的命令均可适用于数组元素,但数组元素使用起来要比普通的内存变量
方便得多。Foxpro支持一维数组和二维数组,与其它高级语言不同的是,Foxpro中的数组元
素可以具有不同的数据类型,并且允许重复定义数组。利用数组进行程序设计,可以简化程
序的编写,便于复杂问题的求解。

9.1.1  数组的定义
    在Foxpro中,使用数组之前通常需要先进行定义。也就是说,使用数组说明命令对数组
的名称、维数、大小(即数组元素的个数)进行说明。定义数组的命令如下:
    【格式】 DECLARE|DIMENSION|PUBLIC <数组名1>(<数值表达式1>[,<数值表达式2>])
             [,<数组名2>(<数值表达式3>[,<数值表达式4>]),…]
    其中,<数组名1>、<数组名2>…为要定义数组的名称,其命名规则同内存变量完全相同。
当同时定义多个数组时,数组名之间用逗号“,”分隔。 数组名后括号内的数值表达式用来
指定所定义数组的维数和大小。若数组名后面的括号内只用一个数值表达式,则表示要定义
一维数组,数值表达式的值代表该数组中元素的个数。若数组名后面的括号内同时使用两个
数值表达式,则表示要定义二维数组,这两个数值表达式的乘积为该二维数组所拥有元素的
个数。
    【功能】定义一个或多个一维或二维内存变量数组。
    【说明】
    ⑴这三条命令都可以用来定义数组。其中,DECLARE命令和DIMENSION命令的功能完全相
同,PUBLIC命令用来定义全局数组变量。
    ⑵使用这三条命令所定义的数组,数组元素的初值均初始化成逻辑值.F., 因此引用数
组元素值之前,必须先对其赋值。
    ⑶定义数组时,数组名后面的数值表达式必须使用一对小括号或中括号括起来。
    例如:
    DECLARE A[10],B[5]  &&定义两个一维数组变量A和B,元素个数分别为10和5
    DIMENSION C[4,5]    &&定义一个4行5列的二维数组C,元素个数为4*5=20
    PUBLIC PA(50)       &&定义一个全局一维数组PA,元素个数为50

    需要说明的是,有些Foxpro命令或函数也具有定义数组的功能,也就是说,如果指定的
数组不存在,将自动建立该数组。例如,COPY TO ARRAY <数组名>命令、SCATTER TO <数组
名>命令和ACOPY()函数都具有定义数组的功能。

9.1.2  数组元素的引用与赋值
    ⒈  数组元素的引用
    由于数组元素在内存中是按顺序存储的,所以引用数组元素时必须指定该元素在数组中
的位置。数组元素在数组中的位置可以用下标来表示,一旦下标确定,数组元素也就确定了。
    一维数组的引用格式如下:
              数组名称[下标表达式]
          或  数组名称(下标表达式)
其中,下标表达式用来指定所引用的元素在数组中的位置,可以是正的数值常量、赋过值的
变量或数值表达式,且下标表达式必须使用一对小括号或中括号括起来。
    例如,对于前面定义的数组A,若存取其中第三个元素可用A[3]或A(3)表示,存取数组A
中第I(I=1,2,…,10)个元素可用A[I]或A(I)表示。因此,使用一重循环可以访问数组A
中的所有元素。
    二维数组可以理解为由M行N列组成的一张二维表,因此引用二维数组中的某个元素,必
须指定其行、列位置。引用格式如下:
              数组名称[行下标表达式,列下标表达式]
          或  数组名称(行下标表达式,列下标表达式)
    例如,对于前面定义的二维数组C,若存取第二行第三列上的元素可用C[2,3]或C(2, 3)
表示,存取数组C中第I(I=1,2,3,4)行第J(J=1,2,3,4,5)列上的元素可用C[I,J]或C(I,J)
表示。因此,使用二重循环可以访问数组C中的所有元素。
    在引用数组元素时,如果指定的下标表达式的值超过定义数组时所规定的最大下标值,
将产生“下标超界”错误信息。例如,对于前面定义的数组A中元素的引用,若使用A[11],
Foxpro将报告“下标超界”错误信息。
    ⒉  数组元素的赋值
    在Foxpro中,一旦数组定义后其元素自动被初始化成逻辑假值.F., 因此在使用数组元
素的值之前,必须对其重新赋值。对数组元素的赋值既可以通过赋值命令实现,也可以使用
有关数组操作的命令实现。
    利用赋值命令为数组元素赋值有以下两种方式:
    ⑴用变量赋值命令为数组元素赋值。例如:
        DECLARE MA[10]
        STORE 1 TO MA[1], MA[3], MA[5], MA[7], MA[9]
        STORE 2 TO MA[2], MA[4], MA[6], MA[8], MA[10]
    以上赋值命令对数组MA中奇数下标元素分别赋值1,偶数下标元素分别赋值2。这两条赋
值命令也可以用下面的FOR循环代替。
        FOR I=1 TO 10
          IF I % 2<>0   &&I为奇数
            MA[I]=1
          ELSE
            MA[I]=2
          ENDIF
        ENDFOR
再如:
        DIMENSION MT[3,3]
        MT[1,1]=1
        MT[1,2]=2
        MT[1,3]=3
        MT[2,1]=4
        MT[2,2]=5
        MT[2,3]=6
        MT[3,1]=7
        MT[3,2]=8
        MT[3,3]=9
    以上赋值命令对二维数组MT中的元素按行顺序分别赋值1,2,3;4,5,6;7,8,9 。
上述赋值命令也可以用下面的二重FOR循环代替。
        FOR I=1 TO 3
          FOR J=1 TO 3
            MT[I,J]=(I-1)*3+J
          ENDFOR
        ENDFOR
    ⑵用赋值命令直接对整个数组赋值
    当对数组中的元素赋相同的值时,Foxpro允许使用赋值命令直接对数组名进行赋值。
    例如:
    DECLARE MB[20]
    MB=0   &&将MB数组中的20个元素均赋0值
    PUBLIC MC[10,20]
    MC=" " &&将MC数组中的200个元素均赋为空格。

9.1.3  数组操作函数
    为了便于数组操作,Foxpro提供了一些专门用于数组处理的标准函数。下面介绍几个常
用的函数。
    ⒈  确定数组元素的个数或行(列)数──ALEN()
    【格式】ALEN(<数组名>[,<数值表达式>])
    其中,<数组名>可以为一维数组或二维数组。<数值表达式>的取值可以为0、1、2,为0
时返回数组元素的个数,为1时返回数组的行数,为2时返回数组的列数。
    【功能】返回指定数组的元素个数或数组的行(列)数。
    【说明】如果省略<数值表达式>,则返回数组元素的个数。如果对一维数组指定的< 数
值表达式>的值为2,则返回0值。例如:
    DECLARE MA[10],MB[4,5]
    ?ALEN(MA),ALEN(MB,0)
        10       20
    ?ALEN(MA,1),ALEN(MB,1)
        10        4
    ?ALEN(MA,2),ALEN(MB,2)
         0        5
    ⒉  确定数组元素在内存中的序号──AELEMENT()
    【格式】AELEMENT(<数组名>,<数值表达式1>[,<数值表达式2>])
    其中,<数值表达式1>和<数值表达式2>分别为数组的行下标和列下标。若<数组名>为一
维数组,则不使用<数值表达式2>。
    【功能】根据指定的下标返回数组元素在内存中的排列序号。
    【说明】一维数组各元素在内存中的排列序号与其下标值相同,二维数组在内存中按行
顺序存储,即先存储第一行的每个元素,接着是第二行的每个元素,…。
    【例9.1】
    *PROG9_1.PRG
    SET TALK OFF
    DECLARE MA[10],MB[4,5]
    FOR I=1 TO 10
      MA[I]=I
    ENDIF
    FOR I=1 TO 4
      FOR J=1 TO 5
        MB[I,J]=I+J
      ENDFOR
    ENDFOR
    ?AELEMENT(MA,5),AELEMENT(MB,4,3)

运行结果为:
        5        18

    ⒊  确定数组元素的下标──ASUBSCRIPT()
    【格式】ASUBSCRIPT(<数组名>,<数值表达式1>,<数值表达式2>)
    其中,<数组名>为一维或二维数组。<数值表达式1>为数组元素在内存中的排列序号。<
数值表达式2>的取值可以为1或2,为1时返回数组元素的行下标,为2时返回数组元素的列下
标。
    【功能】根据数组元素在内存中的序号,确定该元素的行下标或列下标。
    【说明】
    ⑴对于一维数组,<数值表达式2>的值只能取1,取2将报告错误信息。
    ⑵ASUBSCRIPT()和AELEMENT()是功能相反的两个函数,前者是根据数组元素的序号确其
定下标值,后者是根据数组元素的下标值确定其序号。
    例如:
    DECLARE MB[4,5]
    ?ASUBSCRIPT(MB,18,1),ASUBSCRIPT(MB,18,2)  &&显示序号为18的元素的行列下标
         4          3
    ?AELEMENT(MB,ASUBSCRIPT(MB,18,1),ASUBSCRIPT(MB,18,2))
        18
 
    ⒋  数组的拷贝──ACOPY()
    【格式】ACOPY(<源数组>,<目标数组>[,<数值表达式1>[,<数值表达式2>
                 [,<数值表达式3>]]])
    其中,<数值表达式1>指定要拷贝元素的起始位置,省略时为1。<数值表达式2> 指定要
拷贝的元素个数,省略时为源数组中指定位置开始的全部元素。<数值表达式3> 指定拷贝到
目标数组的起始位置,省略时为1。
    【功能】将源数组中的元素拷贝到目标数组中。
    【说明】
    ⑴当目标数组中的元素多于拷贝过来的元素时,ACOPY()将返回成功拷贝的元素个数。
    ⑵当目标数组中的元素少于要拷贝的元素时,ACOPY() 将尽可能的拷贝元素到目标数组
,并显示“下标超界”出错信息。
    ⑶当目标数组不存在时,ACOPY()将自动建立该数组。
    【例9.2】
*PROG9_2.PRG
SET TALK OFF
DECLARE MA[10],MB[10],MC[10],MD[10]  &&定义四个一维数组,其初值均为.F.
CLEAR
FOR I=1 TO 10
   MA[I]=I          &&对数组MA赋值
ENDFOR
=ACOPY(MA,MB)       &&拷贝MA数组到MB
=ACOPY(MA,MC,2)     &&将MA数组第2个位置开始的元素拷贝到MC数组
=ACOPY(MA,MD,3,5,2) &&拷贝MA数组第3个位置开始的5个元素到MD(从第2个位置开始存放)
LIST MEMORY LIKE M*   &&显示所有以M开头的内存变量

运行结果如下:

MA          Priv   A          ma
       (   1)     N           1  (         1.00000000)
       (   2)     N           2  (         2.00000000)
       (   3)     N           3  (         3.00000000)
       (   4)     N           4  (         4.00000000)
       (   5)     N           5  (         5.00000000)
       (   6)     N           6  (         6.00000000)
       (   7)     N           7  (         7.00000000)
       (   8)     N           8  (         8.00000000)
       (   9)     N           9  (         9.00000000)
       (  10)     N          10  (        10.00000000)
MB          Priv   A          ma
       (   1)     N           1  (         1.00000000)
       (   2)     N           2  (         2.00000000)
       (   3)     N           3  (         3.00000000)
       (   4)     N           4  (         4.00000000)
       (   5)     N           5  (         5.00000000)
       (   6)     N           6  (         6.00000000)
       (   7)     N           7  (         7.00000000)
       (   8)     N           8  (         8.00000000)
       (   9)     N           9  (         9.00000000)
       (  10)     N          10  (        10.00000000)
MC          Priv   A          ma
       (   1)     N           2  (         2.00000000)
       (   2)     N           3  (         3.00000000)
       (   3)     N           4  (         4.00000000)
       (   4)     N           5  (         5.00000000)
       (   5)     N           6  (         6.00000000)
       (   6)     N           7  (         7.00000000)
       (   7)     N           8  (         8.00000000)
       (   8)     N           9  (         9.00000000)
       (   9)     N          10  (        10.00000000)
       (  10)     L          .F.
MD          Priv   A          ma
       (   1)     L          .F.
       (   2)     N           3  (         3.00000000)
       (   3)     N           4  (         4.00000000)
       (   4)     N           5  (         5.00000000)
       (   5)     N           6  (         6.00000000)
       (   6)     N           7  (         7.00000000)
       (   7)     L          .F.
       (   8)     L          .F.
       (   9)     L          .F.
       (  10)     L          .F.

    ⒌  数组元素的插入──AINS()
    【格式】AINS(<数组名>,<数值表达式>[,2])
    其中,<数组名>是待插入元素的一维数组或插入一行(或一列)元素的二维数组。< 数
值表达式>指定要插入元素的位置或行号(不带参数2)或列号(带参数2)。
    【功能】将一个元素插入到一维数组或将一行(或一列)元素插入到二维数组。
    【说明】
    ⑴向一维数组中插入一个新元素时,从插入位置开始的所有元素向后移动一个位置,最
后一个元素将丢掉,整个数组大小不变。
    ⑵向二维数组中插入一行(不带参数2)或一列(带参数2)元素时,从插入位置开始的
所有行(或列)均向后移动一行(或一列),最后一行(或一列)元素将丢掉,整个数组大
小不变。
    ⑶向数组中插入的新元素被设置为逻辑值.F.。
    ⑷插入成功时,AINS()返回值1。
    【例9.3】
*PROG9_3.PRG
SET TALK OFF
DECLARE MA[10],MB[4,5]
CLEAR
FOR I=1 TO 10
  MA[I]=I                &&对一维数组MA赋值
ENDFOR 
FOR I=1 TO 4
  FOR J=1 TO 5
    MB[I,J]=(I-1)*5+J    &&对二维数组MB赋值
  ENDFOR
ENDFOR
?"插入元素之前一维数组MA中各元素情况:"
?MA[1],MA[2],MA[3],MA[4],MA[5]
?MA[6],MA[7],MA[8],MA[9],MA[10]
?"插入元素之前二维数组MB中各元素情况:"
FOR I=1 TO 4
  ?
  FOR J=1 TO 5
    ?? MB[I,J]
  ENDFOR
ENDFOR
=AINS(MA,4) &&在MA数组第四个位置插入一个元素
=AINS(MB,2) &&在MB数组的第二行插入一行元素
?"插入元素之后一维数组MA中各元素情况:"
?MA[1],MA[2],MA[3],MA[4],MA[5]
?MA[6],MA[7],MA[8],MA[9],MA[10]
?"插入元素之后二维数组MB中各元素情况:"
FOR I=1 TO 4
  ?
  FOR J=1 TO 5
    ?? MB[I,J]
  ENDFOR
ENDFOR
     
运行结果如下:

插入元素之前一维数组MA中各元素情况:
         1         2          3          4          5
         6         7          8          9         10
插入元素之前二维数组MB中各元素情况:
         1         2          3          4          5
         6         7          8          9         10
        11        12         13         14         15
        16        17         18         19         20
插入元素之后一维数组MA中各元素情况:
         1         2          3         .F.         4
         5         6          7          8          9
插入元素之后二维数组MB中各元素情况:
         1         2          3          4          5
        .F.       .F.        .F.        .F.        .F.
         6         7          8          9         10
        11        12         13         14         15 

    ⒍  数组元素的删除──ADEL()
    【格式】ADEL(<数组名>,<数值表达式>[,2])
    其中,<数组名>是待删除元素的一维数组或删除一行(或一列)元素的二维数组。< 数
值表达式>指定要删除元素的位置或行号(不带参数2)或列号(带参数2)。
    【功能】删除一维数组中的指定元素或二维数组中指定的一行(或一列)元素。
    【说明】
    ⑴删除数组元素后,数组的大小不变。
    ⑵删除一维数组中的元素后,其后面的所有元素自动向前移动一个位置,并将最后一个
元素赋值为.F.。
    ⑶删除二维数组中的一行(或一列)后,其后面的行(或列)自动向前移动或一行(或
一列),并将最后一行(或一列)元素赋值为.F.。
    ⑷删除成功时,ADEL()返回1。
    【例9.4】
    *PROG9_4.PRG
    SET TALK OFF
    DECLARE MA[6],MB[3,3]
    CLEAR
    MA=0 &&将MA数组全部元素置0
    MB=1 &&将MB数组全部元素置1
    =ADEL(MA,4) &&删除MA数组中第4个元素
    =ADEL(MB,2) &&删除MB数组中第2行元素
    LIST MEMORY LIKE M*  &&显示删除元素后MA和MB数组的内容
    SET TALK ON

结果如下:
MA          Priv   A                                        
       (   1)     N           0  (         0.00000000)
       (   2)     N           0  (         0.00000000)
       (   3)     N           0  (         0.00000000)
       (   4)     N           0  (         0.00000000)
       (   5)     N           0  (         0.00000000)
       (   6)     L          .F.
MB          Priv   A                                        
  (   1,   1)     N           1  (         1.00000000)
  (   1,   2)     N           1  (         1.00000000)
  (   1,   3)     N           1  (         1.00000000)
  (   2,   1)     N           1  (         1.00000000)
  (   2,   2)     N           1  (         1.00000000)
  (   2,   3)     N           1  (         1.00000000)
  (   3,   1)     L          .F.
  (   3,   2)     L          .F.
  (   3,   3)     L          .F.

    ⒎  数组元素的查找──ASCAN()
    【格式】ASCAN(<数组名>,<表达式>[,<数值表达式1>[,<数值表达式2>]])
    其中,<数组名>为要查找的一维数组或二维数组。<表达式>指定要查找的数组元素,可
以是任何类型的表达式。<数值表达式1>指定查找的起始位置, 省略时从数组中第一个元素
开始查找。<数值表达式2>指定查找的范围,省略时为指定查找位置开始的全部元素。
    【功能】在数组中查找指定的数组元素。
    【说明】
    ⑴如果查找成功,则返回该元素在数组中的序号,否则返回0值。
    ⑵若查找的元素为字符类型,则查找结果将受SET EXACT的设置影响。
    【例9.5】
    *PROG9_5.PRG
    SET TALK OFF
    DECLARE MA[10]
    FOR I=1 TO 10
      MA[I]=I*I
    ENDFOR
    IP=ASCAN(MA,25) &&在MA数组中查找值为25的元素
    IF IP>0
      WAIT "查找成功!" WINDOW NOWAIT
    ELSE
      WAIT "查找失败!" WINDOW NOWAIT
    ENDIF
    SET TALK ON

    ⒏  数组的排序──ASORT()
    【格式】ASORT(<数组名>[,<数值表达式1>[,<数值表达式2>[,<数值表达式3>]]])
    其中,<数组名>为要排序的一维数组或二维数组。<数值表达式1>表示数组元素的序号,
指定排序的起始位置,省略时从第一个元素开始排序。<数值表达式2> 指定一维数组中要排
序的元素个数或二维数组中要排序的元素的行数,省略时将从指定的起始位置开始,一直到
数组结束。<数值表达式3>指定排序的方向,为0时表示升序(由小到大)排序,为1 时表示
降序(由大到小)排序,省略时按升序排序。
    【功能】按升序或降序对数组中的元素进行排序。
    【说明】
    ⑴参加排序的数组元素必须具有相同的数据类型,否则将产生“数据类型不匹配”错误
信息。
    ⑵一维数组是以元素为单位进行排序的,即从<数值表达式1>指定的元素开始,直到<数
值表达式2>指定的元素个数为止。
    【例9.6】
*PROG9_6.PRG
SET TALK OFF
DECLARE MA[10]
CLEAR
MA[1]=1
MA[2]=3
MA[3]=5
MA[4]=7
MA[5]=9
MA[6]=2
MA[7]=4
MA[8]=6
MA[9]=8
MA[10]=10
=ASORT(MA,1,0,1) &&对MA数组按降序排序
?MA[1],MA[2],MA[3],MA[4],MA[5]
?MA[6],MA[7],MA[8],MA[9],MA[10]
SET TALK ON

运行结果为:
        10          9          8          7          6
         5          4          3          2          1
 
    如果将例9.6程序中的排序命令改为“=ASORT( MA) ”, 结果将会如何? 如果改为“
=ASORT(MA,3,6,0),结果又将如何?请读者自己分析。

    ⑶二维数组是以某列为基准,以行为单位进行排序的,即从<数值表达式1> 指定的元素
所在的行开始,以该元素所在的列上的元素大小为基准,向下排<数值表达式2>行。 在整个
排序过程中,每行中各列元素的次序保持不变。
    【例9.7】
*PROG9_7.PRG
SET TALK OFF
DECLARE MB[4,3]
CLEAR
MB[1,1]=4
MB[1,2]=2
MB[1,3]=6
MB[2,1]=1
MB[2,2]=5
MB[2,3]=9
MB[3,1]=10
MB[3,2]=8
MB[3,3]=12
MB[4,1]=7
MB[4,2]=11
MB[4,3]=3
?"排序之前数组MB的各元素如下:"
DO DISPMB
=ASORT(MB,8,2,1)  &&从序号为8的元素开始,降序排2行元素
?"排序之后数组MB的各元素如下:"
DO DISPMB
SET TALK ON
PROCEDURE DISPMB &&显示数组元素过程
FOR I=1 TO 4
  ?
  FOR J=1 TO 3
    ?? MB[I,J]
  ENDFOR
ENDFOR
RETURN   

运行结果如下:
排序之前数组MB的各元素如下:
         4         2         6
         1         5         9
        10         8        12
         7        11         3
排序之后数组MB的各元素如下:
         4         2         6
         1         5         9
         7        11         3
        10         8        12

    如果将例9.7程序中的排序命令改为“=ASORT( MB) ”, 结果将会如何? 如果改为“
=ASORT(MB,6),结果又将如何?请读者自己分析。

    ⒐  将数据库的结构复制到数组──AFIELDS()
    【格式】AFIELDS(<数组名>)
    【功能】将当前打开的数据库的结构信息复制到指定的数组。
    【说明】
    ⑴该函数将当前工作区中打开的数据库的“字段名”、“字段类型”、“字段长度”和
“小数位数”四项信息存入一个n(字段个数)行4列的二维数组数组中。
    ⑵如果事先没有定义数组,AFIELDS()将自动建立该数组。若定义的数组不够大, 则自
动调整该数组的大小,以便容纳所有的字段信息。
    【例9.8】
*PROG9_8.PRG
SET TALK OFF
CLEAR
USE TXLK
=AFIELDS(DBFSTRU) &&取通讯录数据库的结构信息
FOR I=1 TO FCOUNT()
  ?
  FOR J=1 TO 4
    ?? DBFSTRU[I,J],"  "
  ENDFOR
ENDFOR
SET TALK ON

运行结果如下:

姓名       C     8     0
性别       C     2     0
职务       C     6     0
工作单位   C    30     0
邮政编码   C     6     0
通讯地址   C    30     0   

9.1.4  数组程序设计举例

    通过上述介绍可以看出,Foxpro中的数组在操作上非常灵活,这是其它程序设计语言所
无法相比的。另外,Foxpro中的数组还可以与数据库之间进行数据交换,大大方便了数据库
的操作。数组是一种非常有用的数据结构,利用数组进行程序设计,可以简化问题的求解。

    【例9.9】假设有两个数据库结构完全相同的数据库RSDA1.DBF和RSDA2.DBF, 为了保证
输入数据的可靠性,这两个数据库中的记录分别由两个操作员按照同样的数据建立。要求编
程检查两个数据库中的记录是否完全相同,如果发现不相同的记录,需要将RSDA2.DBF 中不
一致记录的记录号和记录内容存入另外一个数据库RSDA3.DBF中。

数据库结构如下:
Structure for database: C:\PRGDIR\RSDA1.DBF
Field  Field Name  Type       Width    Dec   
    1  姓名        Character      8                 
    2  性别        Character      2                 
    3  职称        Character      6                 
    4  出生日期    Date           8                 
    5  工龄        Numeric        2                 
    6  基本工资    Float          6      2          
    7  奖金        Float          6      2          
    8  婚否        Logical        1                 
** Total **                      40

Structure for database: C:\PRGDIR\RSDA3.DBF
Field  Field Name  Type       Width    Dec   
    1  记录号      Numeric       10                 
    2  姓名        Character      8                 
    3  性别        Character      2                 
    4  职称        Character      6                 
    5  出生日期    Date           8                 
    6  工龄        Numeric        2                 
    7  基本工资    Float          6      2          
    8  奖金        Float          6      2          
    9  婚否        Logical        1                 
** Total **                      50

程序如下:
*PROG9_9.PRG的功能是比较两个数据库中的记录是否完全相同
SET TALK OFF
DECLARE MA[8],MB[8]
SELE 3
USE RSDA3
SELECT 2
USE RSDA2
SELECT 1
USE RSDA1
SET RELATION TO RECNO() INTO RSDA2  &&将RSDA1.DBF与RSDA2.DBF按记录号关联
SCAN
  SELECT 1
  SCATTER TO MA &&将RSDA1.DBF中当前记录中各字段的值存入数组MA
  SELECT 2
  SCATTER TO MB &&将RSDA2.DBF中当前记录中各字段的值存入数组MB
  IF NOT EQUAL() &&将RSDA2.DBF中不一致记录的记录号和记录存入RSDA3.DBF
     SELECT 3
     APPEND BLANK
     REPLACE 记录号 WITH RECNO(2)
     GATHER FROM MB FIELDS 姓名,性别,职称,出生日期,工龄,基本工资,奖金,婚否
  ENDIF
ENDSCAN
CLOSE DATABASE
SET TALK ON
RETURN
FUNCTION EQUAL &&比较数组MA和MB对应元素是否相同,如果相同返回.T.,否则返回.F.
PRIVATE I,TRUE
TRUE=.T.
FOR I=1 TO 8    
    IF MA[I]<>MB[I]
      TRUE=.F.
    ENDIF
ENDFOR
RETURN TRUE      

    例9.9程序由一个主程序和一个用户自定义函数EQUAL组成。为了便于处理, 将 RSDA1
.DBF和RSDA2.DBF两个数据库按记录号进行关联,这样就保证了数据库RSDA1.DBF的记录指针
移动时,与之关联的RSDA2.DBF的记录指针也自动指向具有相同记录号的记录。使用SCATTER
命令将数据库的当前记录存入数组,利用GATHER命令把数组的内容写入数据库的当前记录。
使用数组MA和MB,将两个数据库记录的比较,转化为对两个数组对应元素的比较,简化了操
作。

    【例9.10】输入一个字符串,统计其中每个字母出现的次数,并按照字母顺序输出统计
的结果。统计时,大小写字母按一个字母计算。
   
程序如下:
*PROG9_10.PRG的功能是统计一个字符串中每个字母出现的次数。
SET TALK OFF
CLEAR
ACCEPT "输入一个字符串:" TO STRING
STRING=UPPER(STRING)  &&将字符串中的小写字母转换为大写字母
LENGTH=LEN(STRING)    &&计算字符串的长度
DECLARE MA[LENGTH]    &&MA数组用来保存字符串中不同的字母,重复出现的字母只存一个
NUM=0                 &&保存不同字母的个数
FOR I=1 TO LENGTH
  CH=SUBSTR(STRING,I,1)  &&取字符串中的一个字符
  IF ISALPHA(CH)         &&判别CH是否为字母,ISALPHA()为Foxpro内部函数
    IP=ASCAN(MA,CH)      &&查找该字母是否在数组MA中出现过
    IF IP=0              &&如果是第一次出现,则将其存入数组MA
      NUM=NUM+1
      MA[NUM]=CH        
    ENDIF
  ENDIF 
ENDFOR
IF NUM>0                 &&如果输入的字符串中含有字母,则统计
  DECLARE MB[NUM]        &&MB数组用来保存字母的统计结果
  MB=0                   &&将MB数组全部元素赋0值
  =ASORT(MA,1,NUM)       &&按照字母顺序排序
  FOR I=1 TO LENGTH      &&该FOR循环统计字符串中字母的个数
    CH=SUBSTR(STRING,I,1)
    IP=ASCAN(MA,CH)
    IF IP>0
      MB[IP]=MB[IP]+1    &&将该字母的个数加1
    ENDIF
  ENDFOR
  ?"字母:"+SPACE(10)+"个数:"
  FOR I=1 TO NUM
    ?MA[I],MB[I]
  ENDFOR
ELSE
  ?"输入的字符串中没有字母,无法统计"
ENDIF   
SET TALK ON
RETURN      




运行情况如下:

输入一个字符串:You have to look before you leap,as the saying goes.
字母:      个数:
A             4
B             1
E             6
F             1
G             2
H             2
I             1
K             1
L             2
N             1
O             7
P             1
R             1
S             3
T             2
U             2
V             1
Y             3

    【例9.11】编写一个消除指定数据库中相同记录的程序,并且将消除重复记录后剩余的
记录复制到结构相同的TEMPDBF.DBF中。假设数据库中没有备注字段和通用字段。

    基本思路:首先打开指定的数据库文件(例如RSDA.DBF),利用COPY TO ARRAY 命令将
数据库中全部记录拷贝到二维数组(如TAB)中。然后在二维数组TAB中逐行检查是否存在完
全相同的两行(或多行)元素,若存在,则保留一行,并删除其余相同的行。最后将TAB 数
组中剩余的元素(不包含元素值全部为.F.的行)拷贝到另外一个二维数组(如MA)中, 并
利用APPEND FROM ARRAY命令将数组MA中的全部元素追加到目标数据库TEMPDBF.DBF中。

程序如下:
*PROG9_11.PRG的功能是消除指定数据库中的相同记录
SET TALK OFF
USE RSDA
COPY TO ARRAY TAB      &&将数据库记录拷贝到二维数组TAB
M=ALEN(TAB,1)          &&计算行数
N=ALEN(TAB,2)          &&计算列数
I=1
DO WHILE I<=M AND NOT ALLFALSE(I)  &&消除二维数组TAB中完全相同的行
   J=I+1
   DO WHILE J<=M AND NOT ALLFALSE(J)
       TRUE=CHECKTAB(I,J)
       IF TRUE
          =ADEL(TAB,J)             &&删除TAB数组中的第J行
       ENDIF
       J=J+1
   ENDDO
   I=I+1
ENDDO
I=1
NUM=0
DO WHILE I<=M AND NOT ALLFALSE(I)  &&统计TAB数组中有效数据行数
   NUM=NUM+1
   I=I+1
ENDDO  
DECLARE MA[NUM,N]
=ACOPY(TAB,MA,1,NUM*N)    &&将消除重复后的数据拷贝到二维数组MA
COPY STRUCTURE TO TEMPDBF 
USE TEMPDBF       
APPEND FROM ARRAY MA      &&将二维数组MA中的元素追加到当前数据库
USE
SET TALK ON
RETURN
*函数ALLFALSE判断TAB数组中某行元素是否全部为.F.,若是返回.T.,否则返回.F.
FUNCTION ALLFALSE 
PARAMETERS IP
PRIVATE J,TRUE
TRUE=.T.
FOR J=1 TO N
  IF NOT EMPTY(TAB[IP,J]) &&不为空值
     TRUE=.F.
  ENDIF
ENDFOR
RETURN TRUE
*函数CHECKTAB检查TAB数组中是否存在两行完全相同的元素,若是返回.T.,否则返回.F.
FUNCTION CHECKTAB 
PARAMETERS X,Y    
PRIVATE K,TRUE
TRUE=.T.
FOR K=1 TO N
  IF TAB[X,K]<>TAB[Y,K]  &&数组元素不同
     TRUE=.F.
  ENDIF
ENDFOR
RETURN TRUE    

    例9.11程序由一个主程序和两个用户自定义函数组成。自定义函数ALLFALSE()用来判断
TAB数组中某行元素是否全为.F.(删除数组中某行后产生元素值全为.F.的行),若是, 该
行不参与比较。自定义函数CHECKTAB()用来检查TAB中是否存在完全相同的两行, 若存在,
该函数返回逻辑值.T.,否则返回. F. 。 ALLFALSE( ) 的调用出现在 DO  WHILE 命令中,
CHECKTAB()的调用出现在赋值命令中(TRUE=CHECKTAB(I,J))。它们都属于有参调用,参数
传递情况请读者自己分析。

    【例9.12】在港口煤炭货运业务管理中,需要按照收货单位、到达港和煤种统计国内出
口煤炭进出结存日报表。其中,当日调进煤炭吨数可根据当天的水陆联运货票,按照收货单
位、到达港和煤种统计出来。现有一名为HPK.DBF的水陆联运货票数据库, 其数据库结构如
下:
Field  Field Name  Type       Width    Dec   
    1  到港日期    Character     10                 
    2  发货日期    Character     10                 
    3  运单号      Character      6                 
    4  车号        Character      8                 
    5  吨数        Numeric       10                 
    6  发站        Character     12                 
    7  到达港      Character      6                 
    8  煤种        Character     10                 
    9  发货单位    Character     30                 
   10  收货单位    Character     30                 
   11  铁路局      Character     12                 
   12  备注        Memo          10                 
** Total **                     155

要求按照收货单位、到达港和煤种统计吨数(即把收货单位、到达港和煤种都相同的记录的
吨数字段相加)。
    分析:本例属于多个关键字段(即收货单位、到达港和煤种三个字段)的分类求和,显
然,无法利用Foxpro的TOTAL命令实现。 我们可以按照下面的思路求解:首先根据要分类求
和的数据库(HPK.DBF),建立一个用于累加求和目的的数据库(简称累加库), 在累加库
中应包括要分类求和的所有关键字段和数值字段。然后从HPK.DBF中取记录, 并按关键字段
的值查找累加库。若找到,则取出上次求和的结果与本次的吨数相加,结果再写回累加库。
若没有找到,则将关键字段的值和对应的吨数追加到累加库。这样对HPK.DBF 扫描一遍就可
以统计出各类的求和结果。

程序如下:
*PROG9_12.PRG按照收货单位、到达港和煤种统计吨数,结果存入COUNTHP.DBF
SET TALK OFF
SLEECT 1
USE HPK     &&打开货票库
COPY STRU TO COUNTHP FIELDS 收货单位,到达港,煤种,吨数
SELECT 2
USE COUNTHP &&打开累加库
INDEX ON 收货单位+到达港+煤种 TO COUNTHP &&按关键字段的组和建立索引
DECLARE OP1[4],OP2[4]
SELECT 1
SCAN
  SCATTER TO OP1 FIELDS 收货单位,到达港,煤种,吨数
  SELECT 2
  SEEK OP1[1]+OP1[2]+OP1[3]  &&按组和关键字查找累加库
  IF FOUND()  &&若存在,则累加吨数
     SCATTER TO OP2
     OP2[4]=OP2[4]+OP1[4]  &&累加吨数
     GATHER FROM OP2
  ELSE        &&第一次出现追加到累加库
     APPEND BLANK
     GATHER FROM OP1
  ENDIF
  SELECT 1
ENDSCAN
CLOSE ALL
SET TALK ON
RETURN
       
    例9.12程序中,使用了数据库记录与数组之间交换数据的命令SCATTER TO  < 数组> 和
GATHER FROM <数组>。本例介绍的方法弥补了Foxpro的TOTAL 命令在多关键字段分类求和方
面的缺陷,为编写通用多关键字段分类求和程序提供了一种有效方法。