COBOL:简单的文件读取问题

时间:2015-10-14 17:52:47

标签: cobol gnucobol

我有一个非常基本的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的链接,如果它有帮助

2 个答案:

答案 0 :(得分:4)

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

我还粘贴了问题中的数据,它以40字节的记录包含空记录分隔符来到我这里。

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

而不是5个40字节的记录,而不是41个中的5个。如果你认为COBOL程序一次读取40个字节的数据块,则会给你5个40字节的记录,以及五分之一。

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

这一次," long"除了第一条记录外,所有记录都有前导记录分隔符。

以下是您要测试的一些数据:

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
      .

我会试着理解为什么最后一行&#34;&#34;看起来就像你原来一样,并把它放在GnuCOBOL讨论区。

修复:在SELECT上使用LINE SEQUENTIAL并保持原样;或者,从数据中删除所有空行/换行符。这些中的任何一个都会为输入中的(现在)六个记录提供40字节的正确排队数据。

好的,你会喜欢这个。

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

我测试了每个IO(OPEN,CLOSE,READ和WRITE)之后定义的文件状态字段。

OPEN和CLOSE的文件状态为&#34; 00&#34;。预期

前四个READ的文件状态为&#34; 00&#34;。预期

五个WRITE的文件状态为&#34; 00&#34;。预期

第五个READ给出文件状态为&#34; 04&#34;。这意味着:

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都不关心这一点。

如果您使用了文件状态,那么您的程序可以知道您已经阅读了短记录或长记录。

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

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

正是为什么你看到了几乎全部的第六个记录&#34;我不知道,但它具有历史意义。如果您想查看OpenCOBOL源以尝试发现原因,可以在GnuCOBOL站点的Files部分找到它。

使用GnuCOBOL,您可以使用嵌入的空白显示或写入40字节字段。 DISPLAY将始终在嵌入式&#34; null&#34;上执行换行符。值,这给了我明显的35字节后跟四字节记录,第40个字节(实际上是第36个)是&#34;不可见&#34;空。

WRITE不会导致嵌入null的换行符,直到你使用某些东西来&#34;看看&#34;期望数据是文本而不是二进制的文件。

&#34;问题&#34;在GnuCOBOL中不是问题,它是DISPLAY的工作方式(期望文本数据,而不是二进制),或者,如果使用WRITE,那么你的方式&#34;查看&#34;文件。

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

可以解释程序中GnuCOBOL输出的数据(最后记录35个字节的数据)。我得到的空间因为COBOL&#34;填充&#34;当您将较短的字段移动到较长的字段时。 READ ... INTO ...包含隐式MOVE,因此您可以获得填充。

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

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

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

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

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

您还可以在SELECT上查看使用LINE SEQUENTIAL。对于Linux / Unix / Windows,这是一种更自然的文件类型,&#34;记录&#34;然后知道它们是分隔的,并且你有40,40,40,40和35字节的有效记录。

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

此类问题是数据的问题,通常是Stack Overflow的主题。

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

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

我建议你升级到GnuCOBOL 1.1.0,它有很多针对OpenCOBOL 1.1的错误修复,并且正在积极开发(GnuCOBOL是OpenCOBOL的新名称,因此OpenCOBOL本身不再开发)。 / p>

您的代码有几点。

在Gilbert LeBlanc之前的回答中提出的结构,对于这个问题,不公平地投票,在COBOL计划中要好得多。

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

答案 1 :(得分:0)

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

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