好的,错过了一招或两招。您正在使用40字节的定长记录。当您使用定长记录时,与行顺序不同,在读取时没有单个尾随空值剥离和在写入时没有空值附加。
我还从问题中复制了您的数据,它以包括空记录分隔符的40字节记录形式到达我这里。
现在我有了您的真实数据...
您有五条长度为41的记录,而不是五条长度为40的记录。如果您将其视为数据块,COBOL程序将每次读取40个字节,这将给您五条长度为40的记录和一条长度为5的记录。
在没有将空值附加到记录的情况下,我应该将所有输出数据看作一个长行。但是我没有。为什么?
这次,随着“长”记录,除第一条记录外,所有记录都有前导记录分隔符为空。
以下是一些供您测试的数据:
John Johnson 0123456789
Peter Peterson 0123456789
Juliette Julietteson123456789
Natasha Natashason0123456789
Justin Justinson 0123456789
1234511111111111111111111111111111111110
每个记录都包含40字节的数据,后面跟着一个空记录终止符。
这是您修改后的程序,可以编译并运行数据。我使用`cobc -x -free prog.cob`来修复从问题中粘贴的列(从SO中粘贴不太适合COBOL)。由于它是有缺陷的,所以我没有特别关注新内容的位置。
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
.
我将尝试理解为什么最后一行会像原始文本中那样出现,并在GnuCOBOL讨论区中讨论。解决方法:要么在SELECT语句中使用LINE SEQUENTIAL并保留数据,要么删除数据中的所有空行/换行符。这两种方法都将为输入中的6条记录提供40个字节的正确对齐数据。我通过在两个SELECT语句中添加FILE STATUS子句来更改您的原始程序。我测试了每个IO(OPEN、CLOSE、READ和WRITE)之后我定义的文件状态字段。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,您的程序就可以知道您已读取了短记录或长记录。
如果只执行了五个 WRITE 语句,为什么会有六个输出记录呢?
好吧,因为你有35字节的数据加上一个“新行”,因为新行只会在第40个字节后被削减,当你显示数据时,你会得到两行。在文件中,有一个“记录”,但它有一个嵌入的换行符。我使用了
cat
而不是显示十六进制值的编辑器,所以看到了一个“第六条记录”,然后是一个文本编辑器,再次看到了第六条记录。
为什么你会看到一个几乎完整的“第六个记录”我不知道,但这是历史性的兴趣。如果你想查看 OpenCOBOL 源代码以尝试发现原因,你可以在 GnuCOBOL 网站的文件部分找到它。
在 GnuCOBOL 中,你可以用嵌入的空格来显示或写入40字节的字段。DISPLAY 总是在嵌入的“null”值上换行,这给了我表面上的35字节后跟四字节的记录,第40个字节(实际上是第36个)是“不可见”的 null 值。
直到你使用一些期望数据为文本而不是二进制的东西来“查看”文件,WRITE 才会导致嵌入 null 的换行。
在 GnuCOBOL 中,“问题”不是问题,因为 DISPLAY 的工作方式(期望文本数据而不是二进制数据)或者如果使用 WRITE,那么你就需要“查看”文件的方式。
您得到的实际 OpenCOBOL 输出实际上是一个 bug,但它不能在 GnuCOBOL 中复现。
您的程序输出的 GnuCOBOL 输出可以解释为最后一条记录有35个字节的数据。我得到的空格是由于 COBOL 在将较短的字段移动到较长的字段时进行“填充”。
READ ... INTO ...
包含了一个隐式 MOVE,所以你会得到填充的结果。
如果您只是使用 FD 下的数据区域中的记录,您将得到更加不可预测的结果。
所以它确实使问题与主题相关。我想。因此问题应该保留,因为其他人几乎肯定会在某个时候遇到类似的问题。问题是使用非文本数据的 DISPLAY 或使用仅限文本的工具查看输出文件。还是表示这最后一点意味着使用 WRITE 会使其成为无关话题?:-)
解决方法有两个。升级到 GnuCOBOL。始终使用 FILE STATUS,并在每次 IO 后始终检查(每个文件一个最好的)正确的文件状态字段,并在发生意外情况时采取一些明智的行动。
文件结束时的文件状态值为“10”。我将88添加到文件状态字段,并始终使用“10”进行文件结束检查。我从不使用AT END / NOT AT END的混乱。
如果您使用了Gilbert的建议,带有引导读取且没有INTO,我认为您会得到不同的结果,这将有助于解决问题。引导读取(在进入读取循环之前始终有当前记录,然后将下一个记录(或获取文件结束)作为循环中的最后一个逻辑事情)是更“COBOL”方式做事情。就像FILE STATUS和文件状态字段上的88一样,并且每次都要检查。
您还可以查看在SELECT上使用LINE SEQUENTIAL。这是Linux / Unix / Windows更自然的文件类型,“记录”则已知为分隔符,然后您将获得40、40、40、40和35字节的有效记录。
然后您有可变长度记录,需要知道如何处理这些记录。
这种类型的问题,数据问题,通常不在Stack Overflow上讨论。
但是,行为不正确,有理由期望您不会获得最后一个记录的最后五个字节(因为您最初使用了READ INTO)。但是,这样的数据错误不应使您的程序认为存在额外的记录。
我将在GnuCOBOL讨论页面上提出此问题,其中(披露)我是Moderator。
我建议您升级到GnuCOBOL 1.1.0,因为它比OpenCOBOL 1.1具有更多的错误修复,并且正在积极开发(GnuCOBOL是OpenCOBOL的新名称,因此不再开发OpenCOBOL本身)。
关于您的代码有几点要注意。
在COBOL程序中,先前由Gilbert LeBlanc提供的结构对于使用实际SELECT语句上的FILE STATUS比使用AT END及其相关项要好得多。每次IO之后测试文件状态(最好是使用88),以便尽快识别问题。我将测试这是否会在这种情况下有所帮助。