1、第七章 集合和记录类型,第一节集合类型第二节记录类型第三节 文件操作,第一节集合类型第二节记录类型第三节 文件操作,第一节集合类型,集合是由具有某些共同特征的元素构成的一个整体。在Pascal中,一个集合是由具有同一有序类型的一组数据元素所组成,这一有序类型称为该集合的基类型。,集合类型的定义和变量的说明,集合类型的一般形式为: set of ;说明: 基类型可以是任意顺序类型, 而不能是实型或其它构造类型。同时,基类型的数据的序号不得超过255。 例如下列说明是合法的: type letters=set of A.Z; numbers=set of 0.9; s1=set of char;
2、ss=(sun,mon,tue,wed,thu,fri,sat); s2=set of ss;与其它自定义类型一样, 可以将类型说明与变量说明合并在一起.如: type numbers=set of 0.9; var s:numbers; 与 var s:set of 0.9;等价。,集合的值,集合的值是用“”和“”括起来,中间为用逗号隔开的若干个集合的元素。如: 空集 1,2,3 a,e,i,o,u 都是集合。说明:集合的值放在一对方括号中,各元素之间用逗号隔开。在集合中可以没有任何元素,这样的集合称为空集。在集合中,如果元素的值是连续的,则可用子界型的表示方法表示。 例如: 1,2,3,4
3、,5,7,8,9,10,15 可以表示成: 1.5,7.10,15集合的值与方括号内元素出现的次序无关。例如,1,5,8 和5,1,8的值相等。在集合中同一元素的重复出现对集合的值没有影响。例如,1,8,5,1,8与1,5,8的值相等。每个元素可用基类型所允许的表达式来表示。如1,1+2,4、ch、succ(ch)。,集合的运算,赋值运算 只能通过赋值语句给集合变量赋值,不能通过读语句赋值,也不能通过write(或writeln)语句直接输出集合变量的值。集合的并、交、差运算 可以对集合进行并、交、差三种运算,每种运算都只能有一个运算符、两个运算对象,所得结果仍为集合。三种运算符分别用“”、“
4、”、“”表示。注意它们与算术运算的区别。集合的关系运算 集合可以进行相等或不相等、包含或被包含的关系运算,还能测试一个元素是否在集合中。所用的运算符分别是:、 它们都是二目运算,且前个运算符的运算对象都是相容的集合类型,最后一个运算符的右边为集合,左边为与集合基类型相同的表达式。,例7.1 设有如下说明: type weekday=(sun,mon,tue,wed,thu,fri,sat); week=set of weekday; subnum=set of 1.50;写出下列表达式的值:sun,sat+sun,tue,frisun,fri*mon,tuesun,sat*sun.satsun
5、-mon,tuemon-mon,tuesun.sat-mon,sun,sat1,2,3,5=1,5,3,21,2,3,41.41,2,3,5=1.31.5=1.41,2,3=1.3 2 in1.10,答: 表达式的值分别是: sun,sat,tue,fri sun,sat sun tue.fri TRUE FALSE TRUE FALSE TRUE TRUE,例7.3 调用随机函数产生10个互不相同的随机整数(0=x=40),放入集合中并一起输出(5个一行)。program ex7_3;var a:set of 0.40; I,m,n:integer;Begin a:=; n:=0; Rand
6、omize; Repeat M:=random(41); If not(m in a) then begin a:=a+m; n:=n+1; End; Until n=10; N:=0; For I:=0 to 40 do If I in a then begin write(I:4); n:=n+1; If (n mod 5=0) then writeln; End;End.,例7.4 编制用筛选法求1-n(n200)以内素数的程序。【分析】由希腊著名数学家埃拉托色尼提出的所谓“筛选法”,步骤如下:将所有候选数放入筛中;找筛中最小数(必为素数)next,放入集合primes中;将next的所
7、有倍数从筛中筛去;重复直到筛空。编程时,用集合变量sieve表示筛子,用集合primes存放所有素数。,program ex7_4;const n=200;var sieve,primes:set of 2.n; next,j:integer;begin sieve:=2.n; /将所有候选数放入筛中 primes:=; /素数集合置空 next:=2; repeat /找筛sieve中最小一个数 while not(next in sieve) and(next=n)do next:=succ(next); primes:=primes+next; /将最小数放入素数集合中 j:=next;
8、,while j=n do /将这个素数的倍数从筛中删去 begin sieve:=sieve-j; j:=j+next; end until sieve=; j:=0; for next:=2 to n do /打印出所有素数 if next in primes then begin write(next:5); j:=j+1; if j mod 10=0 then writeln; end; writeln;end.,源程序如下:,【上机练习】,1、调用随机函数产生10个互不相同的随机整数(0x40),放入集合中并一起输出(5个一行)。提示:随机函数使用randomize;初始化m:=ra
9、ndom(n);n,m都是整数,那么0mn-12、输入一个大写字母字符串,找出未在此串中出现的所有大写字母。3、编写一个译码程序,将输入的一串字符,(只有小写字母、数字和空格,输入时以句号结束)翻译成原码。译码规则如下:数字0,1,2,3,9分别和字母a,b,c,j互换;字母k,m,p,t,y分别和它们的后继互换;其他字母和空格保持不变。,第二节记录类型,在程序中对于组织和处理大批量的数据来说,数组是一种十分方便而又灵活的工具,但是数组在使用中有一个基本限制,这就是:一个数组中的所有元素都必须具有相同的类型。但在实际问题中可能会遇到另一类数据,它是由性质各不相同的成份组成的,即它的各个成份可能
10、具有不同的类型。例如,有关一个学生的数据包含下列项目:学号字符串类型姓名字符串类型年龄整型性别字符型成绩实型数组 Pascal给我们提供了一种叫做记录的结构类型。在一个记录中,可以包含不同类型的并且互相相关的一些数据。,记录类型的定义,在pascal中,记录由一组称为“域”的分量组成,每个域可以具有不同的类型。记录类型定义的一般形式: record :; :; : : :; end;,说明:域名也称域变量标识符, 应符合标识符的语法规则。在同一个记录中类型中,各个域不能取相同的名,但在不同的记录类型中,两个类型中的域名可以相同。记录类型的定义和记录变量可以合并为一个定义,如: type dat
11、e=record year:1900.1999; month:1.12; day:1.31 end; var x:date; 可以合并成: var x: record year:1900.1999; month:1.12; day:1.31 end;对记录的操作,除了可以进行整体赋值, 还能对记录的分量域变量进行。域变量的表示方法如下: 记录变量名.域名 如前面定义的记录X,其3个分量分别为:x.year ,x.month ,x.day。域变量的使用和一般的变量一样, 即域变量是属于什么数据类型,便可以进行那种数据类型所允许的操作。,记录的嵌套,当一个记录类型的某一个域类型也是记录类型的时候,
12、我们说发生了记录的嵌套,看下面的例子:例7.5 某人事登记表可用一个记录表示, 其中各项数据具有不同的类型,分别命名一个标识符。而其中的“出生年月日”又包括三项数据,还可以用一个嵌套在内层的记录表示。具体定义如下:type sexs=(male,female); date=record year:1900.1999; month:1.12; day:1.31; end; personal=record name:string15; sex:sexs; birthdate:date; home:string40; end;,例7.6 设计一个函数比较两个dates日期类型记录变量的迟早。设函数名
13、、形参及函数类型定义为: AearlyB(A,B:dates):boolean; 函数的形参为两个dates类型的值参数。当函数值为true 时表示日期A早于日期B,否则日期A迟于日期B或等于日期B。显然不能对、两个记录变量直接进行比较,而要依具体的意义逐域处理。,程序如下:program ex7_6;type dates=record year:1900.1999; month:1.12; day:1.31 end;var x,y:dates;function AearlyB(A,B:dates):boolean;var earln:boolean;begin early:=false; i
14、f (A.yearB.year) then early:=true; if (A.year=B.year)and(A.monthB.month) then early:=true; if (A.year=B.year)and(A.month=B.month)and(A.dayB.day) then early:=true; AearlyB:=early;end; of AearlyBBEGIN write(Input DATE X(mm-dd-yy):)readln(X.month,X.day,X.year); write(Input DATE Y(mm-dd-yy):)readln(Y.mo
15、nth,Y.day,Y.year); if AearlyB(X,Y) then writeln(Date X early!) else writeln(Date X not early!);END.,开域语句,在程序中对记录进行处理时,经常要引用同一记录中不同的域,每次都按例7.6的格式引用,非常乏味。为此Pascal提供了一个with语句,可以提供引用域的简单形式。开域语句一般形式: with do 功能: 在do后的语句中使用with后的记录的域时, 只要直接写出域名即可,即可以省略例7.6的记录变量名和“.”。,说明: 一般在with后只使用一个记录变量名。如: write(Input
16、year:); readln(x.year); write(Input month:); readln(x.month); write(Input day:); readln(x.day); 可以改写成: with x do begin write(Input year:);readln(year); write(Input month:);readln(month); write(Input day:);readln(day); end;设x,y是相同类型的记录变量,下列语句是非法的:with x,y do.;,with后接若干个记录名时,应是嵌套的关系。如有记录说明:var x:recor
17、d i:integer; y:record j:0.5; k:real; end; m:real end;可以使用:with x do begin read(i); with y do read(j,k); readln(m); end;或简写为: with x,y do readln(i,j,k,m);,例7.7 输入40个学生记录(每个学生记录包括学号、姓名、年龄、成绩),组成记录数组,然后按成绩由高到低的次序排序,输出排序后的全部学生记录。程序如下:Program ex7_7;const n=40;type student=record num : integer; name : str
18、ing8; sex : char; age : 5.100; score : real; end;var stu : student; temp;student; i,j : integer;,begin for i : =1 to n do with stui do readln(sex,num,age,score,name); for i : =1 to n-1 do/选择排序 for j : =i+1 to n do if stui.scorestuj.score then begin temp : =stui; stui : =stuj; stuj : =temp; end; writ
19、eln(num : 8,name : 10,sex : 14,age : 10,score : 8); /输出 for i : =1 to n do with stui do begin write(num : 8,name : 18); if sex=m then write(maLe : 8) else write(femaLe : 8); writeln(age : 8,score : 8 : 1); end;end.,例7.8 读入10个日期,再对每个日期输出第二天的日期。输入日期的格式是月、日、年。例如:9301993,输出的格式为10/1/1993。【分析】可用一个记录变量toda
20、y表示日期。 知道一个日期后要更新为第二天的日期,应判断输入的日期是否为当月的最后一天,或当年的最后一天。program ex7_8;type date=record month:1.12; day:1.31; year:1900.1999; end;var today:array1.10of date; i:integer; maxdays:28.31;begin for i:=1 to 10 do with todayi do readln(month,day,year);,if day=maxdays then begin day:=1; if month=12 then begin m
21、onth:=1;year:=year+1; end else month:=month+1; end else day:=day+1; writeln(month,/,day,/,year); end;end.,for i:=1 to 10 do with todayi do /求第i个日期中月份最后一天maxdays begin case month of 1,3,5,7,8,10,12:maxdays:=31; 4,6,9,11 :maxdays:=30; 2:if(year mod 400=0) or( year mod 4=0)and(year mod 1000) then maxda
22、ys:=29 else maxdays:=28; end;,第三节 文件操作,在程序设计中,常常需要从键盘输入大量数据,操作相当麻烦、也很容易出错;同时,在程序运行后也往往会产生大量的输出数据(结果),这给验证结果的正确性和测试程序的对错也带来了很大的麻烦。能不能有一种方法,让程序自动从某个地方读取数据运行,再将程序的运行结果保存到指定的地方呢?当然可以,这就是Pascal中的“文件”类型。文件在使用时必须在程序内部以一定的语句与实际文件联系起来,建立一一对应的关系,用内部文件的逻辑名对实际文件进行操作。使用文件大致有以下几个步骤;()在使用文件前,必须对文件类型和变量进行说明;()建立内部文
23、件(程序中的文件)与外部文件(磁盘上的实际文件)的联系;()打开文件,为文件读写作准备;()对文件进行读、写操作;()在使用完文件后,一定要记住关闭文件,确保文件的完整性和可靠性,否则会引起文件处理错误。切记切记!Free Pascal将文件分为三类:文本文件(顺序)、有类型文件(顺序或随机)和无类型文件(顺序或随机)。下面只对竞赛中用到的文本文件及其操作进行介绍。 文本文件又称为正文文件或行文文件,可供人们直接阅读,是人机通信的基本数据形式之一。文本文件可用文字编辑程序(如记事本、word等)直接建立、阅读和编辑, 也可以由Pascal程序在运行过程中建立。,文本文件的定义:,文本文件的类型
24、为TEXT,它是由ASCII字符组成的,是Pascal提供的标准文件之一。标准文件 TEXT已由Pascal说明如下: type text=file of char;因此,TEXT同标准类型Integer、real、char等一样可以直接用于变量说明之中,无需再由用户说明。 例如: var f1,f2:text;这里定义了两个文本文件变量F1和F2。,写入文本文件,文本文件内容写入操作步骤为:定义文本文件变量;把一外部文件名赋于文本文件变量,使该文本文件与一相应外部文件相关联; 命令格式:assign(f,name) f为定义的文本文件变量 name为实际文件文件名 如:assign(f1,f
25、ile1.in) 或:assign(f1, pas/file1.out) 这样在程序中对文本文件变量F1的操作,也就是对外部实际文件 File1.in或 File1.out的操作。上例中文件 File1.in是存贮在当前目录中,而文件File1.out则是存贮在PAS子目录中。打开文本文件,准备写; 命令格式:Rewrite(f) 功能:创建并打开新文件准备写,若已有同名文件则删除再创建 命令格式:Append(f) 功能:打开已存在的文件并追加(即文件中的原有信息不丢失),这在竞赛中不会用到。对文件进行写操作; 命令格式:Write(f,) 或:Writeln(f,) 功能:将项目内容写入文
26、件f中,Write和Writeln的用法与写在屏幕上一样。文件操作完毕后,关闭文件。 命令格式:Close(f),例7.9 从键盘上读入如下的数据,把它们写入名为b.txt的文件中。 3 429 30 50 6080 90 70 7560 50 70 45Program ex7_9;var i,j,n,m,x : integer; f : text;begin readln(n,m); assign(f,b.txt); rewrite(f); /准备写操作 writeln(f,n:4,m:4); for i : =1 to n do /共n行内容 begin for j : =1 to m d
27、o /每行有m个值 begin read(x); write(f,x:4); end; readln; /输入时屏幕换行 writeln(f); /把回车符写入输出文件中 end; close(f);end.,读取文本文件,文本文件内容读出操作步骤:定义文本文件变量;用Assign(f,name)命令,将内部文件f与实际文件name联系起来; 当需要从文件中读取数据(输入)到内存时,应先调用reset过程打开该文件,再用read或readln将数据读入到内存变量中,且只能从文件的开头读数据; 命令格式:reset(f);read(f,)或readln(f,)文件操作完毕,用Close(f)命令
28、关闭文件。,例7.10 读出例7.9建立的文本文件,并输出在屏幕。Program ex7_10;var i,j,n,m,x : integer; f : text;begin assign(f,b.txt); reset(f); /关联并准备读操作 readln(f,n,m); writeln(n:4,m:4); for i : =1 to n do /共有n行内容 begin for j : =1 to m do /每行有m个值 begin read(f,x); write(x:4); end; writeln; end; close(f); readln; /起暂停作用end. 由于文本文
29、件是以ASCII码的方式存储,故查看文本文件的内容是极为方便。,行结束和文件结束函数,除了上述几个重要的过程外,在文本文件的使用过程中还经常用到两个重要的函数。 Eoln函数:行结束函数,函数值为布尔型,当文件指针指向回车换行符时,函数值为真(true),否则为假(false)。 Eoln函数一般用在从一个已打开的文件中读取数据时判断一行的数据是否读完。如果文件是以写状态打开的,则Eoln函数的值总是假。Eoln函数的调用形式为:Eoln(f);一般可以省略参数(包括括号)。 Eof函数:文件结束函数,函数值为布尔型,当文件指针指向文件结束标志(ctrl+z)时,函数值为真(true),否则为
30、假(false)。Eof函数一般用在从一个已打开的文件中读取数据时判断文件是否结束。Eof函数的调用形式为:Eof(f);也可省略参数。,文本文件的特点,文本文件的每一个元素均为字符型,但在将文件元素读入到一个变量(整型,实型或字符串型)中时,Pascal会自动将其转换为与变量相同的数据类型。与此相反在将一个变量写入文本文件时,也会自动转换为字符型。 文件的读写操作,有一个非常实用的技巧,就是利用input和output这两个变量,input是Free Pascal默认的输入设备,事先不更改,指的是键盘。output是Free Pascal默认的输出设备,事先不更改,指的是显示器。这两个设备变
31、量,我们可以更改它们的值,让它们和我们所要求的文件相关联,利用这一特性,就可以达到更加灵活的编程风格。下面几个例子,读写操作全用input和output这两个变量,同学们仔细体会。,例7.11 将文本文件in.txt中的内容复制到一个新的文本文件out.txt中。程序如下:program ex7_11;var ch:char;begin assign(input,in.txt); assign(output,out.txt); reset(input); rewrite(output); while not eof do begin while not eoln do begin read(ch); write(ch); end; readln; writeln; end; close(input); close(output);end.,请维护编者版权,请勿将课件在网络上传播。,福建省长乐一中 董永建浙江省温州中学 舒春平 2009年9月,