4

我有一个非常基本的 COBOL 程序,它读取文件input.dat并简单地将其输出到控制台中。该input.dat文件如下所示:

John                Johnson             
Peter               Peterson            
Juliette            Julietteson         
Natasha             Natashason          
Justin              Justinson           

它没有正确显示在这里,但我很肯定有20 chars名字和20 chars姓氏。

这是我的 COBOL 程序:

    IDENTIFICATION DIVISION.
    PROGRAM-ID. ATEST4.
    ENVIRONMENT DIVISION.
    INPUT-OUTPUT SECTION.
    FILE-CONTROL.
        SELECT INPUTFILE ASSIGN TO "files/input.dat".
    DATA DIVISION.
    FILE SECTION.
    FD  INPUTFILE LABEL RECORDS ARE OMITTED.
    01  INPUTRECORD              PIC X(40).    
    WORKING-STORAGE SECTION.
    01  FILE-STATUS              PIC 9 VALUE 0.
    PROCEDURE DIVISION.
    001-MAIN.
        OPEN INPUT INPUTFILE.
        PERFORM 002-READ UNTIL FILE-STATUS = 1.
        CLOSE INPUTFILE.
        STOP RUN.
            
    002-READ.
        READ INPUTFILE
            AT END MOVE 1 TO FILE-STATUS
            NOT AT END DISPLAY INPUTRECORD
        END-READ.  

相反,输出如下所示:

John                Johnson             
Peter               Peterson            
Juliette            Julietteson         
Natasha             Natashason          
Justin              Justinson           
ustin              Justinson       

最后一行似乎是前一行的副本,缺少第一个字符和几个较少的尾随空格(总计为35 chars)。

为什么会这样?这似乎是对AT END条款的误解,但我无法绕过它。

编辑:按照建议更新编译器。结果还是一样。这是我的输入文件的链接,如果有帮助的话

4

2 回答 2

6

好吧,错过了一个技巧。或者两个。您正在使用 40 字节的固定长度记录。当您使用固定长度记录时,与行顺序不同,READ 上没有单尾空剥离,而 WRITE 上没有空附加。

我还从问题中粘贴了您的数据,它以 40 字节记录的形式到达我这里,包括 null record-delimiter

现在我有你的真实数据...

不是 5 条 40 字节的记录,而是 41 条中的 5 条。如果考虑到一块数据,COBOL 程序将一次读取 40 字节,这会给您 5 条 40 字节的记录,以及 5 条中的一条.

如果没有将空值附加到记录中,我应该将所有输出数据视为一长行。但我没有。为什么?

这一次,对于“长”记录,除了第一条记录外,其他所有记录都有 null 的前导记录分隔符。

这里有一些数据供您测试:

John                Johnson   0123456789
Peter               Peterson  0123456789
Juliette            Julietteson123456789
Natasha             Natashason0123456789
Justin              Justinson 0123456789
1234511111111111111111111111111111111110

这意味着每条记录有 40 个字节的数据,后跟一个空记录终止符。

这是您修改后的程序,用于编译和运行数据。我没有在粘贴您的问题后修复列(从 SO 粘贴对 COBOL 来说不是很好),而是使用了 `cobc -x -free prog.cob。而且由于它被破坏了,所以并没有太注意我将新东西塞进了哪里。

DISPLAY 中的“>”和“<”的点是绑定字段。然后您可以识别空值的位置,因为它们会导致中断。

IDENTIFICATION DIVISION.
PROGRAM-ID. FILE-TEST.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INPUTFILE ASSIGN TO "files/input.dat"
file status is fs1.
SELECT OUTPUTFILE ASSIGN TO "files/output.dat"
file status is fs2.
DATA DIVISION.
FILE SECTION.
FD INPUTFILE
    LABEL RECORDS ARE OMITTED
    record is varying depending on record-length.
01 INPUTRECORD PIC X(40).
FD OUTPUTFILE
    LABEL RECORDS ARE OMITTED.
01 OUTPUTRECORD PIC X(40).
WORKING-STORAGE SECTION.
    01 EOF PIC 9 VALUE 0.
    01  fs1 pic xx.
        88  fs1-ok value zero.
        88  fs1-eof value "10".
    01  fs2 pic xx.
        88  fs2-ok value zero.
    01 CUSTOMER.
        02 FIRST-NAME PIC X(20).
        02 LAST-NAME PIC X(20).
    01  rec-count comp-3 pic 999 value zero.
PROCEDURE DIVISION.
    001-MAIN.
        OPEN INPUT INPUTFILE OUTPUT OUTPUTFILE.
        if not fs1-ok
            display "bad fs1 O>" fs1 "<"
        end-if
        if not fs2-ok
           display "bad fs2 O>" fs2 "<"
        end-if
        PERFORM 002-READWRITELOOP UNTIL EOF = 1.
        CLOSE INPUTFILE. 
       if not fs1-ok
           display "bad fs1 C>" fs1 "<"
       end-if
       CLOSE OUTPUTFILE.
       if not fs2-ok
           display "bad fs2 C>" fs2 "<"
       end-if
       STOP RUN.

   002-READWRITELOOP.
       READ INPUTFILE INTO CUSTOMER
           AT END MOVE 1 TO EOF
              display "at end"
       if not fs1-ok
           display "bad fs1 R>" fs1 "< " rec-count
       end-if
           NOT AT END WRITE OUTPUTRECORD FROM CUSTOMER
      DISPLAY ">" CUSTOMER "<"
      DISPLAY ">" inputrecord "<"
      add 1 to rec-count
       display rec-count
       if not fs1-ok
           display "bad fs1 R>" fs1 "< " rec-count
       end-if
       if not fs2-ok
           display "bad fs2 W>" fs2 "<"
       end-if
       END-READ
      .

我将尝试理解为什么最后一个“行”会像你原来的那样出现,并在 GnuCOBOL 讨论区中讨论它。

解决方法:在 SELECT 上使用 LINE SEQUENTIAL 并保持数据不变;或者,从您的数据中删除所有空/新行。这些中的任何一个都会为输入中的(现在)六个记录提供 40 个字节的正确排列的数据。


好的,你会喜欢这个的。

我通过将 FILE STATUS 子句添加到两个 SELECT 语句来更改您的原始程序。

我测试了在每个 IO(打开、关闭、读取和写入)之后定义的文件状态字段。

OPEN 和 CLOSE 给出的文件状态为“00”。预期的。

前四个 READ 给出的文件状态为“00”。预期的。

五个 WRITE 给出的文件状态为“00”。预期的。

第五个 READ 给出“04”的文件状态。意思是:

A READ statement was successfully executed, but the length of the record being processed did not conform to the fixed file attributes for that file.

所以,意料之中。既不AT END也不NOT AT END关心这一点。

如果您使用了 FILE STATUS,您的程序可能会知道您已经阅读了一条短或长的记录。

如果只执行了 5 条 WRITE 语句,怎么会有 6 条输出记录?

好吧,因为你有 35 个字节的数据加上一个“新行”,因为新行只会在第 40 个字节之后被剥离,当你 DISPLAY 数据时,你会得到两行。在文件上,有一个“记录”,但它有一个嵌入的换行符。我没有使用会显示十六进制值的编辑器,而是使用cat, 所以看到了“第六条记录”,然后是文本编辑器,再次看到了第六条记录。

我不知道为什么你会看到几乎完整的“第六张唱片”,但它具有历史意义。如果您想查看 OpenCOBOL 源代码以尝试找出原因,您可以在 GnuCOBOL 站点的文件部分找到它。

使用 GnuCOBOL,您可以显示或写入 40 字节的字段,并嵌入一个空白。DISPLAY 将始终对嵌入的“null”值进行换行,这给了我明显的 35 字节后跟 4 字节记录,第 40 个字节(实际上是第 36 个)是“不可见的”null。

WRITE 不会导致嵌入空值的换行,直到您使用某些东西“查看”期望数据是文本而不是二进制的文件。

GnuCOBOL 中的“问题”不是问题,它是 DISPLAY 的工作方式(期待文本数据,而不是二进制数据),或者,如果使用 WRITE,则是您“查看”文件的方式。

您得到的实际 OpenCOBOL 输出实际上是一个错误,但它无法在 GnuCOBOL 中重现。

可以解释程序的 GnuCOBOL 输出以及您的数据(最后记录 35 个字节的数据)。当您将较短的字段移动到较长的字段时,由于 COBOL 的“填充”,我得到了我们的空格。READ ... INTO ...包含一个隐式 MOVE,所以你得到了填充。

如果您刚刚使用了 FD 下数据区的记录,您会得到更多不可预测的结果。

所以它确实使问题成为话题。我认为。所以这个问题应该保留,因为其他人几乎肯定会在某个时候遇到类似的问题。问题在于将 DISPLAY 用于非文本数据,或使用纯文本工具查看输出文件。或者这是否意味着它会与 WRITE 无关?:-)

分辨率是双重的。升级到 GnuCOBOL。始终使用 FILE STATUS,并始终在每次 IO 后检查(正确的)文件状态字段(最好每个文件一个),并在发生意外情况时采取一些明智的措施。

文件结尾的文件状态值为“10”。我将 88s 添加到文件状态字段,并始终使用“10”进行文件结束检查。我从不使用 AT END/NOT AT END 的纠结。

如果您使用 Gilbert 的建议,使用启动阅读,并且没有 INTO,我认为您会得到不同的结果,这将有助于解决问题。启动读取(在进入读取循环之前始终具有当前记录,然后读取下一条记录(或获取文件结尾)作为循环中的最后一个逻辑内容)是一种更“COBOL”的做事方式。文件状态和文件状态字段上的 88 也是如此,并且每次都进行检查。

您还可以查看在 SELECT 上使用 LINE SEQUENTIAL。对于 Linux/Unix/Windows,这是一种更自然的文件类型,“记录”被称为是分隔的,并且您将获得 40、40、40、40 和 35 字节的有效记录。

然后你有可变长度的记录,你需要知道如何处理这些记录。


这种类型的问题,即数据问题,通常在 Stack Overflow 上是题外话。

但是,这种行为是不正确的,有理由期望您不会获得最后一条记录的最后五个字节(看起来就像您最初所做的那样,因为您使用了 READ INTO)。但是,这样的数据错误不应让您的程序认为有额外的记录。

我将在 GnuCOBOL 讨论页面上提出这个问题,其中(披露)我是版主。

我建议您升级到 GnuCOBOL 1.1.0,它比 OpenCOBOL 1.1 修复了许多错误,并且正在积极开发中(GnuCOBOL 是 OpenCOBOL 的新名称,因此不再开发 OpenCOBOL 本身)。

您的代码有几点。

Gilbert LeBlanc 在之前的回答中建议的结构,对于这个问题,不公正地被否决,在 COBOL 程序中要好得多。

FILE STATUS对文件的语句使用实际比使用及其亲属SELECT要好得多。AT END您在每次 IO 之后测试然后测试(最好使用 88)文件状态,这样您就可以在问题发生后立即识别问题。无论如何,我将测试这是否会在这种情况下有所帮助。

于 2015-10-14T21:15:20.730 回答
0

在 002-Read 中,文件状态设置在文件末尾。直到段落结尾才检查它,因此缓冲区中的任何内容都将被打印,因此歪斜的最后记录两次。

如果您将代码修改为 READ INPUTFILE AT END MOVE 1 TO FILE-STATUS ELSE DISPLAY INPUTRECORD END-READ。

于 2021-07-30T18:41:06.300 回答