在SQR中使用input/output操作
像大多数编程语言一样,SQR使用Open、Read、Write 和 Close命令处理文件。
1.打开文件
访问一个文件之前,必须先打开它。可以将一个已经存在的文件作为input文件打开以读取其中的数据,或者将文件作为output文件打开以向其写数据。打开文件的数目最多只能是256个。
当打开输入文件时,Open命令中指定的Record长度必须 >= 文件中使用的最长记录的长度。最大Record长度是(32K - 1)字节(32767)。
当打开输出文件时,除非特别指定了For-Append 参数,否则就会创建一个新文件。如果相同的文件已经存在 且 没有指定For-Append参数,那么已经存在的文件就会被覆盖掉。
下面的例子中使用了Opend命令:
- Open 'My_File_1' as 1 For-Reading Record=80:Vary
- Open 'c:\temp\My_File_2' as 2 For-Reading Record=88:Fixed
- Open $File_1 as 3 For-Reading
- Open $File_2 as #k For-Writing Record=100
- Open $File_3 as 4 For-Append Record=70
- Open &A.File_Name as 5 For-Reading Record=120:Fixed_Nolf
- Let $File_4='c:\files\input\My_File_3'
Open $File_4 as 6 For-Reading Record=150:Vary
Status=#FileStatus
If #FileStatus != 0
Show 'Error Opening File: ' $File_4
Else
Show 'File ' $File_4 'Opened Successfully'
End-If
第1行:打开了一个输入文件“My_File_1”,用于读取操作。文件名可以是文本,也可以保存在字符串变量或者列变量中。文件名可以是绝对路径,包括磁盘驱动名和目录,也可以只是一个文件名。如果不指定磁盘驱动名和目录,SQR默认使用当前目录。文件名旁边的数字1叫作文件号(常称为文件句柄),用于程序中其他input/output命令引用该文件。在Read、Write 和 Close命令中使用文件号操作文件,而不是使用文件名。文件号可以是任意 < 64000 的整数。该例中Record最大长度为80,意味着所有超过80字节的input record都将被忽略掉。因为此例中Record类型为Vary,所以文件中所有记录必须以一个行分隔符结束(依赖于具体的平台)。特别需要注意的是Record Type = Vary的文件不能包含二进制数据。
第2行: 'c:\temp\My_File_2' 文件的Record Type = Fixed,意味着文件中所有记录的长度都等于88。每个记录都以行分隔符结束。可以使用这种类型的文件读取二进制数据。需要注意的是Type = Fixed 的文件,其记录长度不包含行分隔符。
第3行:文件名保存在$File_1变量中。建议将文件名保存在变量中,而不是硬编码。
第4行:文件名保存在变量$File_2中,用于Writing操作。该文件的句柄(即文件号)保存在数字变量 #k 中。记录最大长度为100。当没有指定Record Type时,默认为Vary。
第5行:文件以Append方式打开。这种方式意味着所有的输出信息都会被放置到文件的末尾。如果文件不存在,就创建一个文件,且该文件的Record Type = Vary、Record Length = 70。
第6行:文件名保存在列变量&A.File_Name中,用于读取数据。文件类型指定为Fixed_Nolf,意味着文件中所有记录都是120字节且每条记录末尾都没有行分隔符。这种文件类型对于读或者写二进制数据来说非常有用。
第7行:通过#FileStatus检验Open命令是否成功执行。
2.关闭文件
SQR中的文件会一直处于打开状态,直到使用Close命令或者程序正常结束。
例如:
- Close 1
- Close #k
Close命令只有一个参数:文件句柄。
3.读取文件
如果文件是以For-Reading方式打开的,就可以读取该文件。Read命令将文件中的下一跳记录读取到Read命令指定的一个变量或者多个变量中。
Read Into
有几种方式可以读取文件中的一条记录,具体情况取决于程序需要。每条记录既可以读取到一个SQR字符串变量中,也可以读取到几个变量中。如下例:
Read 1 Into $Input_Record:80
该例将整条记录都读取到一个字符串变量$Input_Record中。在处理包含可变长度字段的记录时,这种方式特别有用,下面是解析该字符串的一些例子:
Unstring $Input_Record By ',' Into $Last_Name $First_Name $Address
Extract $First_Name from $Input_Record 0 20
Let $Last_Name=Substr($Input_Record,1,20)
Extract $First_Name from $Input_Record 0 20
Let $Last_Name=Substr($Input_Record,1,20)
Read命令的另一种方式就是将记录直接读取到指定的字段中。当清楚每个字段的确切长度时,常使用这种方式:
Read 1 into $Last_Name:20 $First_Name:10 $Address:50
检查文件结尾
读取文件的时候,要经常检查是否满足End-Of-File条件。当不再有记录可读取的时候,SQR内部变量#end-file会被设置成1。在读取每条记录后,应该检查该变量。通常做法是将Read命令放进一个循环中,当探测到End-Of-File条件时,就从循环中跳出:
!**********************************
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 Into $Record:80
If #end-file
Break
End-If
Do Process-Input-Record
End-While
!At this point all the records are read from the file
Close 1
End-Procedure
查看读取状态
想要从操作系统获取文件的读取状态,可以在Read 命令中指定一个数字变量,该变量的名称在Status参数中指定。如果读取成功,就返回0到该变量中,否则就会返回特定于系统的错误号。如下例:
!**********************************
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 into $Record:80 Status=#Read_Stat
If #end-file
Break
End-If
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 into $Record:80 Status=#Read_Stat
If #end-file
Break
End-If
If #Read_Stat <> 0
Show 'Bad return from the Read command, errno=' #Read_Stat ' Record # = ' #Rcds
Else
Add 1 to #Rcds
Do Process-Input-Record
End-If
End-While
!At this point all the records are read from the file
Close 1
End-Procedure
Show 'Bad return from the Read command, errno=' #Read_Stat ' Record # = ' #Rcds
Else
Add 1 to #Rcds
Do Process-Input-Record
End-If
End-While
!At this point all the records are read from the file
Close 1
End-Procedure
读取文本数据
当读取文本数据(任何字符串)时,应该指定变量名和程序要读取的字节数。记住:当读取记录时,末尾的空格会被删除掉。
所有的变量总体的长度必须 <= 读取的整条记录的长度
读取二进制数据
文件记录类型为Fixed或者Fixed_Nolf。二进制字段的长度可以是1、2、4字节。读取二进制数字时,必须将其放到数字变量中。二进制数字只包含整数,如果想要包含数字的小数部分,就将该数字转化成字符串变量。如下为读取二进制数字的一个简单例子:
Read 1 Into #Amount:2 #Hours:1
读取日期字段
如果日期字段是以SQR日期变量格式写到文件中的话,可以将该字段读取到日期变量或者字符串变量。日期变量必须属于以下格式之一:
- SQR_DB_DATE_FORMAT环境变量指定的格式
- 特定于数据的格式
- 独立于数据的格式:'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'
下例将日期字段读取到日期变量中:
Declare-Variable
Date $Input_Date
End-Declare
!**********************************
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 Into $Input_Date:18 $Record
If #end-file
Break
End-If
End-While
End-Procedure
….
Date $Input_Date
End-Declare
!**********************************
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 Into $Input_Date:18 $Record
If #end-file
Break
End-If
End-While
End-Procedure
….
当日期字段被读取到一个文本变量时,可以用strtodate()函数将该字符串文本转化成日期:
Declare-Variable
Date $Date1
End-Declare
!********************************* *
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 Into $String_Date:18 $Record
If #end-file
Break
End-If
Let $Date1 = strtodate($String_Date)
End-While
End-Procedure
Date $Date1
End-Declare
!********************************* *
Begin-Procedure Read-File
!**********************************
While Not #end-file
Read 1 Into $String_Date:18 $Record
If #end-file
Break
End-If
Let $Date1 = strtodate($String_Date)
End-While
End-Procedure
4.写入文件
SQR使你能够将记录写入到文件,只要文件是以For-Writing模式或者For-Append模式打开就行。Write命令将命令中指定变量写入到文件。Write命令可以将单个的文本值、变量、列或者一列文本值、变量、列写入到文件中。可以指定文件句柄、source fields、source fields的长度以及保存Write命令状态的变量。
1 Write 1 From $Record:80 Status=#Write_Stat
2 Write #H From $Record_2
3 Write 2 From $Last_Name:20 ',' $First_Name:10 ',' &Address:50
4 Write 3 From #Amount:2 #Number:1
5 Write 4 From $Date:18
2 Write #H From $Record_2
3 Write 2 From $Last_Name:20 ',' $First_Name:10 ',' &Address:50
4 Write 3 From #Amount:2 #Number:1
5 Write 4 From $Date:18
第1行:将字符串变量作为整条记录写入文件中。因为指定了记录长度,所以只会写字符串前80个字节。如果$Record长度 > 80字节,就会截断;如果 < 80字节,记录的剩余部分就会填充空格。如果成功写入文件,就 将0 赋值给#Write_Stat变量。
第2行:由于没有指定记录长度,所以就是用变量$Record_2的当前长度。
第3行:将三个指定的字段写入到文件中。因为想要使字段以逗号分割,所以在字段中间加入了逗号。
第4行:将数字变量作为source fields。这种情况下,必须指定长度参数。SQR只允许1、2、4字节的二进制整数。
第5行:将日期变量写入到文件。写入到文件之前,SQR会使用SQR_DB_DATE_FORMAT(如果没有设置,就使用数据库的格式)将该日期变量会被转换成字符串。
如果 Open命令中指定的记录长度 < Write命令中指定的长度,就会出现错误;
如果 Open命令中指定的记录长度 > Write命令中指定的长度,就使用Write中指定的长度。
如果不指定记录中每个字段的长度,SQR就会将文件当作拥有可变长度的记录。
不要将NULL写入到ASCII文件中,反之亦然。因为SQR字符串是以NULL结尾的。
5.创建Flat文件
使用 input/output 命令
下面的例子找到有效的员工以及他们的电话号码,然后将这些数据输出到一个文件:! The E-Mail Interface program
!********************
Begin-Program
!********************
If $sqr-platform = 'WINDOWS-NT '
Let $FileName='c:\appldir\Employee.dat'
Else ! UNIX
Let $FileName='/tmp/appldir/Employee.dat'
End-If
Open $FileName as 1 For-Writing Record=100 Status=#OpenStat
If #OpenStat != 0
Show 'Error Opening ' $FileName
Else
Do Process_Employees
Close 1
End-If
Display 'Total records exported: ' Noline
Display #Tot_Recs 999,999,999
End-Program
!*********************************************
Begin-Procedure Process_Employees
!*********************************************
Move 0 To #Tot_Recs
Begin-Select
A.Emplid
A.Deptid
B.Name
B.Phone
Do Write-Output-Record
From PS_Job A, PS_Personal_Data B
Where A.Emplid=B.Emplid
And A.Empl_Rcd=0
And A.Empl_Status = 'A'
And A.EFFDT = (Select MAX(Effdt)
From PS_Job
Where Emplid = A.Emplid
And Empl_Rcd = A.Empl_Rcd
And Effdt <= Sysdate)
And A.Effseq = (Select MAX(Effseq)
From Ps_Job
Where Emplid = A.Emplid
And Empl_Rcd = A.Empl_Rcd
And Effdt = A.Effdt)
End-Select
End-Procedure
!**********************************************
Begin-Procedure Write-Output-Record
!**********************************************
Write 1 From
&A.Emplid:11
&A.Deptid:10
&B.Name:30
&B.Phone:10
Add 1 to #Tot_Recs
End-Procedure
上例中虽然没有在Open命令中指定Record = Fixed,但是产生的输出文件中的记录都是固定长度的。因为在输出记录的时候指定了每个变量确切的长度。记录总长度 = 所有变量长度的和。
创建逗号分隔的输出文件
可以将上面代码中的Write_Output_Record方法改成如下代码:
!************************************************
Begin-Procedure Write_Output_Record
!************************************************
Write 1 from
&A.Emplid ','
&A.Deptid ','
&B.Name ','
&B.Phone
Add 1 To #Tot_Recs
End-Procedure
Begin-Procedure Write_Output_Record
!************************************************
Write 1 from
&A.Emplid ','
&A.Deptid ','
&B.Name ','
&B.Phone
Add 1 To #Tot_Recs
End-Procedure
但是在结果中发现&B.Name的一些值包含了逗号,所以需要改用其他分隔符号。