COBOL:简单的文件读取问题

COBOL: Simple file reading issue

我有一个非常基本的 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 子句的误解,但我无法绕过它。

编辑: 按照建议更新了编译器。结果还是一样。 这是我的 input file 的 link,如果有帮助的话

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

我也粘贴了你的问题数据,它以 40 字节的记录形式到达我这里包括空记录分隔符

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

你有 5 个 41 字节的记录,而不是 5 个 40 字节的记录。如果你认为这是一个数据块,COBOL 程序将一次读取 40 字节,那么你会得到 5 个 40 字节的记录, 和五分之一。

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

这次有 "long" 条记录,除了第一条记录外,其他所有记录都有 leading 空记录分隔符。

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

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

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

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

DISPLAYs 中的“>”和“<”的作用是限定字段。然后您可以确定空值的位置,因为它们会导致中断。

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
      .

我会尝试理解为什么最后一个 "line" 会像您原来的那样出现,并在 GnuCOBOL 讨论区讨论它。

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


好的,你会喜欢的。

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

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

打开和关闭给出的文件状态为“00”。预期。

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

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

第五次读取给出了文件状态“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 ENDNOT AT END 都不关心这个。

如果您使用了 FILE STATUS,您的程序 可以 知道您读取了一条短记录或长记录。

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

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

我不知道为什么你几乎看到了整个 "sixth record",但它具有历史意义。如果您想查看 OpenCOBOL 源代码以尝试找出原因,可以在 GnuCOBOL 站点的文件部分找到它。

使用 GnuCOBOL,您可以显示或写入带有嵌入空白的 40 字节字段。 DISPLAY 将始终对嵌入的 "null" 值进行换行,这给了我明显的 35 字节后跟四字节记录,第 40 个字节(实际上是第 36 个)是 "invisible" null .

WRITE 不会导致嵌入 null 的换行符,直到您使用某些东西 "look at" 期望数据为文本而不是二进制的文件。

GnuCOBOL 中的 "problem" 不是问题,它是 DISPLAY 的工作方式(需要文本数据,而不是二进制数据),或者,如果使用 WRITE,那么 "view"文件。

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

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

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

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

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

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

的纠结

如果您使用了吉尔伯特的建议,并进行了初步阅读,而没有使用 INTO,我认为您会得到不同的结果,这将有助于解决问题。启动读取(在进入读取循环之前始终有一个当前记录,然后读取下一条记录(或获取文件结尾)作为循环中的最后一个逻辑事物)是一种更 "COBOL" 的做事方式.文件状态和文件状态字段上的 88s,以及每次检查。

您还可以考虑在 SELECT 上使用 LINE SEQUENTIAL。对于 Linux/Unix/Windows,这是一种更自然的文件类型,然后已知 "records" 被分隔,并且您将获得 40、40、40、40 和 35 字节的有效记录。

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


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

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

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

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

你的代码有几点。

Gilbert LeBlanc 之前对这个问题的回答中建议的结构在 COBOL 程序中要好得多。

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

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

如果您将代码修改为 读取输入文件 最后移动 1 到文件状态 别的 显示输入记录 结束阅读。