如何处理包含两种不同类型记录的顺序文件?

How do I process a sequential file with two different types of records?

对于作为 COBOL 课程一部分的作业,我的输入文件是一个名为 BRANCHTOT.SEQ 的顺序文件,其中包含两种类型的记录:

第一个是我所说的 header 记录,它说明了文件中记录的总数(不包括 header 记录本身)。这只是一条记录。

如果这是文件中的唯一记录,我会将其定义如下:

01 header-record.
   03 record-count  PIC 9(6).
   03 FILLER        PIC X(13). 

第二种记录是我必须读取和处理输出的记录。其结构如下:

01 sales-record.
   03 branch-nr    PIC X(5).
   03 office-nr    PIC 9(2).
   03 count        PIC 9(5).
   03 sum          PIC 9(5)V99.
  1. 如何在我的 FILE SECTION 和 WORKING-STORAGE SECTION 中定义 BRANCHTOT.SEQ 文件?我是否使用重新定义?

我习惯于使用一种记录类型的文件。

  1. 如何在不让 header-record 妨碍的情况下读取和处理 sales-record 类型的记录?

作业比我在这里描述的要复杂得多,但是 对于这部分问题,我的程序必须读取和处理 sales-record 记录并打印它已读取的这些记录的总数,并将其与 header-record 中列出的数量进行比较。如果两者不匹配,我需要在我的输出列表中有一个声明来记录这个错误。

  1. 我该怎么做?

好的,感谢您的评论,这是一个课程,所以它在风格上是比较自由的。 Here is an excellent beginning for reading a file

通常,如果您谈到 "redefines",这意味着您的老师希望您使用一个。所以你的记录会被定义成这样:

01  my-record.
    05  my-header.
        10  record-count  PIC 9(6).
        10  FILLER        PIC X(13).
    05  my-sales-record redefines my-header.
        10 branch-nr    PIC X(5).
        10 office-nr    PIC 9(2).
        10 count        PIC 9(5).
        10 sum          PIC 9(5)V99.

有效,因为两者都是 19 个字符长。不需要额外的填充物或复杂的东西。

然后,稍后,您将有一个

READ myfile INTO my-record

使用计数器(我希望你知道如何使用一个简单的数字变量)来计算记录。并用它来知道它是否是 header :

IF current-record = 1
    (do something with the header)
ELSE
    (do something with the sales record)
END-IF

确保所有这些都嵌入到一个循环中,就像我的示例 link 中描述的那样,它应该可以工作。

您定义了您的两个记录,您可以完全按照您在单个 FD 下显示的那样进行操作。这会给你一个隐含的重新定义。

在文件的 SELECT 声明中(始终)使用 FILE STATUS。始终检查 file-status 字段是否获得预期值。使用 88 condition-name 作为值“10”来标识 end-of-file。不要使用 AT END/NOT AT END.

请注意,您无法完美地完成练习,因为该文件尚未正确定义。没有可用于识别 header 记录或数据记录的指示。唯一的迹象是 "the header is first, all the rest are data"。这可能看起来不错,但是当有人腌制最初编写文件的程序并且没有给你 header 或 two-or-more headers.

如果一个文件有一个结构,这个结构应该在数据中,因为它可以被检查。像处理好文件一样处理坏文件可能会非常昂贵。和尴尬。

你还需要知道文件是否允许"empty"。因为它有一个 header 记录,一个 "empty" 文件应该由一个 header 记录和一个 data-record 计数组成。您的情况可能有所不同,因为该文件不是设计的。

在您的初始处理中(在您的输入文件 OPENed 之后),您读取了第一条记录。如果是这样,请处理 "empty file"。检查它是 header。存储 record-count.

然后您阅读了下一条记录。你检查它是不是一个header。

然后处理您的数据,记住您有第一个 data-record 可用。

Loop until end-of-input
  process record
  read next record
End-Loop

在 end-of-file(当循环结束时)你检查 header 上的记录数(从存储的值)到你读取的 data-records 的数量(计数在记录的处理范围内)。

一旦你有了你的程序,你就有了一个 "model" 来作为其他程序的基础。您只需将一般 "this is the way I process a file" 正确一次,然后将其用作下一个 file-processing 程序的 starting-point。下一个程序会更复杂,所以你最终会得到另一个模型。

一段时间后,您将拥有大约五个模型,每个模型都基于一个更简单任务的工作代码。

我反对使用 AT END/NOT AT END 读取文件有几个原因:

复杂性和可理解性

a-pargraph-for-SO-formatting.
   PERFORM priming-read
   PERFORM 
     UNTIL end-of-infile
       PERFORM process-data
       PEFORM read-next
   END-PERFORM
   .
priming-read.
   PERFORM read-next
   .
read-next.
   READ IN-FILE
   IF NOT IN-FILE-STATUS-OK
       PERFORM diagnostic-message-and-fail
   END-IF
   .

  PERFORM UNTIL WS-EOF='Y'
     READ STUDENT INTO WS-STUDENT
        AT END MOVE 'Y' TO WS-EOF
        NOT AT END DISPLAY WS-STUDENT
     END-READ
  END-PERFORM

如果不测试 WS-EOF,就不能在 END-READ 之后放置任何代码。然而人们确实如此。

可靠性和易操作性

如果在 SELECT 中为文件指定了文件状态,则由程序员对其进行测试。如果出现问题,AT END 显然不是真的,但是没有新的记录。随后的 READ 将得到相同的情况,并且随之而来的是大循环。所以应该测试 file-status-字段(如果使用 FILE STATUS),那么,为什么不使用 file-status-字段来测试 end-of-file,因为它更简单,而不是NOT AT END 中的进一步条件。

当然,如果您不使用 FILE STATUS,run-time 会处理事情,但会以 broad-and-blunt 方式处理,没有机会提供额外的诊断信息,

当然,您也可以使用 USE AFTER... 但这会进一步复杂化,因为很多人不习惯。

它鼓励使用 GO TO "get out of a mess"

READ IN-FILE
  AT END GO TO no-more-records
END-READ

为什么在FD下定义记录,为什么不在WORKING-STORAGE

或者说,READ ...READ ... INTO ...有什么区别?

FILE SECTION中的FD允许描述"record area"中的记录。

对于输入文件,这将是成功读取的最后一条记录。如果有的话。打开文件将使 record-area 可用。一旦遇到end-of-file,就没有当前记录了。文件关闭时也不会有当前记录(无论是否达到end-of-file)。

这意味着您不应在打开文件之前、关闭文件之后或达到 end-of-file 之后访问 record-area。在 IBM 大型机上,不应该通常是 can't,因为它很容易导致 S0C4 异常终止,即保护异常。输入区域实际上是在处理文件的 IO-routines 中定义的,而不是在您的 COBOL 程序中定义的。 FD 只是将您的定义映射到 record-area 的地址。如果当时 record-area 不存在,则无法访问。

对于简单的file-structure,您不需要同时访问来自不同记录的数据,您始终可以使用 FD。

对于更复杂的结构,需要存储来自不同record-types的数据,因为FD下只有当前记录可用

您可以存储整个记录,也可以只存储您需要的部分。

您可以在 READ 之后的某个时间点使用 MOVE 为各个字段存储您需要的部分。

你可以在fd下通过MOVE整条记录来存储整条记录READ,或使用 READ ... INTO ... 自动执行。

READ ... INTO ... 对输入文件中的每条记录执行 MOVE(隐式)。如果你不需要它,那就是浪费资源,而且由于人们在大型机上为资源付费(比如 CPU 使用),除非你迫切需要它,否则值得避免。

网站通常有当地标准。你遵循标准,即使它们不好(你试图改变它们,并不总是成功)。如果你被告知使用 READ ... INTO ... 你就使用它。

但是,作为参考,我不使用 READ ... INTO ...(除非在上述情况下)并且在使用 FD 和移动我想要的数据时从未遇到过问题(无论是单个字段,或整个记录)。

使用FD是"best"。除非当地标准另有规定。那么就是"best"按照当地标准

需要注意的是,有些东西会修改上面的内容,并在您的程序中为记录创建特定区域。如果 INTO(和 WRITE 上的 FROM)可以使您的整个记录​​隐式移动两次。