COBOL:简单的文件读取问题

Alv*_*aro 4 cobol gnucobol

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

John                Johnson             
Peter               Peterson            
Juliette            Julietteson         
Natasha             Natashason          
Justin              Justinson           
Run Code Online (Sandbox Code Playgroud)

它没有正确显示在这里,但我肯定有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.  
Run Code Online (Sandbox Code Playgroud)

相反,输出如下所示:

John                Johnson             
Peter               Peterson            
Juliette            Julietteson         
Natasha             Natashason          
Justin              Justinson           
ustin              Justinson       
Run Code Online (Sandbox Code Playgroud)

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

为什么会发生这种情况?这似乎是对该AT END条款的误解,但我无法绕过它。

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

Bil*_*ger 6

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

我还从问题中粘贴了您的数据,它以 40 字节记录的形式出现在我面前,包括空记录分隔符

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

不是 5 个 40 字节的记录,而是 41 个中的 5 个。 如果考虑到 COBOL 程序将一次读取 40 个字节的数据块,那么您将获得 5 个 40 字节的记录,以及 5 个中的一个.

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

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

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

John                Johnson   0123456789
Peter               Peterson  0123456789
Juliette            Julietteson123456789
Natasha             Natashason0123456789
Justin              Justinson 0123456789
1234511111111111111111111111111111111110
Run Code Online (Sandbox Code Playgroud)

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

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

显示中 ">" 和 "<" 的点是限制字段。然后您可以确定空值的位置,因为它们会导致中断。

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
      .
Run Code Online (Sandbox Code Playgroud)

我将尝试理解为什么最后一个“行”看起来像你原来的那样,并在 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.
Run Code Online (Sandbox Code Playgroud)

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

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

如果只执行了五个 WRITE 语句,那么如何有六个输出记录?

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

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

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

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 的纠结。

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

您还可以查看在 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 STATUSSELECT文件的语句上使用实际比 usingAT END及其亲属要好得多。您在每次 IO 之后测试然后测试(最好使用 88)文件状态,这样您就可以在问题发生后立即识别。无论如何,我将测试这是否会在这种情况下有所帮助。