1

我有一些长度可变的 csv 记录,例如:

0005464560,45667759,ZAMTR,!至 ACC 12345678,DR,79.85

0006786565,34567899,ZAMTR,!至 ACC 26575443,DR,1000

我需要分隔这些字段中的每一个,并且我需要最后一个字段应该是钱。

但是,当我读取文件并将记录解串成字段时,我发现最后一个字段在其自身末尾包含垃圾值。金额(金额)字段应为 8 个字符,前 5 位,1 点,末尾 2 位。输入的值可以是任何值,例如 13.5、1000 和 354.23。

    "FILE SECTION"

        FD INPUT_FILE.
            01 INPUT_REC                                   PIC X(66).

    "WORKING STORAGE SECTion"

            01 WS_INPUT_REC                                 PIC X(66).

            01 WS_AMOUNT_NUM                                PIC 9(5).9(2).
            01 WS_AMOUNT_TXT                                PIC X(8).

"MAIN SECTION"

                        UNSTRING INPUT_REC DELIMITED BY ","
                        INTO WS_ID_1, WS_ID_2, WS_CODE, WS_DESCRIPTION, WS_FLAG, WS_AMOUNT_TXT

                        MOVE WS_AMOUNT_TXT(1:8) TO WS_AMOUNT_NUM(1:8)

                        DISPLAY WS_AMOUNT_NUM

从显示上看,数值比较正常:345.23, 1000,和原来的一样,但是我把字段写入文件后,变成了这样:

79.85^M^@^@ 137.35^M^@

我检查了字段 WS_AMOUNT_NUM,它来自字段 WS_AMOUNT_TXT,发现 ^@ 是一种 LOW-VALUE。但是,我找不到 ^M 是什么,它不是空格,也不是高值。

4

2 回答 2

4

我在猜测,但看起来您可能正在将文件中的可变长度记录读取到固定长度的 COBOL 记录中。COBOL 记录末尾的垃圾让您有些悲伤。很难说垃圾从一次读取到下一次读取的一致性如何(超出实际输入记录长度范围的数据在技术上是未定义的)。那个垃圾最终被包括在WS_AMOUNT_TXT之后UNSTRING

有很多方法可以解决这个问题。我在这里给你的建议可能不是最佳的,但它很简单,应该可以完成工作。

您的语句中的最后一个INTO字段是接收所有尾随垃圾的字段。那个垃圾需要去掉。知道最后一个字段中唯一有效的字符是数字和十进制字符,您可以按如下方式对其进行清理:WS_AMOUNT_TXTUNSTRING

PERFORM VARYING WS_I FROM LENGTH OF WS_AMOUNT_TXT BY -1
          UNTIL WS_I = ZERO
    IF WS_AMOUNT_TXT(WS_I:1) IS NUMERIC OR
       WS_AMOUNT_TXT(WS_I:1) = '.'
       MOVE ZERO TO WS_I
    ELSE
       MOVE SPACE TO WS_AMOUNT_TXT(WS_I:1)
    END-IF
END-PERFORM

上面代码中的基本思想是从最后一个UNSTRING输出字段的末尾扫描到开头,用空格替换任何不是有效数字或小数点的内容。一旦找到一个有效的数字/小数,假设其余的都是有效的,就退出循环。

清理后,使用我对上一个问题NUMVAL的回答中概述的内在函数 将 WS_AMOUNT_TXT 转换为数字数据类型。

最后一条建议,MOVE SPACES TO INPUT_REC在每次READ删除之前读取可能留在缓冲区中的数据之前。这将在读取“长”记录之后的非常“短”记录时保护您 - 否则您可能会绊倒前一次读取留下的数据。

希望这可以帮助。

编辑刚刚注意到这个关于读取可变长度文件的问题的答案。使用可变长度输入记录是一种更好的方法。给定实际的输入记录长度,您可以执行以下操作:

UNSTRING INPUT_REC(1:REC_LEN) INTO...

之后为文件REC_LEN指定的变量在哪里。您遇到的所有垃圾都发生在由定义的记录结束之后。使用如上图所示的引用修改将其修剪掉,然后再分离出各个数据字段。OCCURS DEPENDING ONINPUT_RECFDREC_LENUNSTRING

编辑 2: 不能使用参考修改UNSTRING。该死... 其他一些 COBOL 方言可以,但 OpenVMS COBOL 则不行。尝试以下操作:

MOVE INPUT_REC(1:REC_LEN) TO WS_BUFFER
UNSTRING WS_BUFFER INTO...

哪里WS_BUFFER是一个足够长的工作存储PIC X变量来保存最长的输入记录。当您MOVE将短的字母数字字段转换为较长的字段时,目标字段左对齐,空格用于填充剩余空间(即。WS_BUFFER)。由于函数可以接受前导和尾随空格,NUMVAL因此您可以完全满足您的需要。

我有理由把你推向这个方向。读取短记录时,任何在记录缓冲区尾端结束的垃圾都是未定义的。有些垃圾有可能最终成为数字或小数点。如果发生这种情况,我最初建议的清理例程将失败。

编辑 3: 生成的 WS_AMOUNT_TXT 中没有 ^@,但仍然有 ^M

看起来文件系统将每条记录末尾的 <CR> (那个 ^M 东西)视为数据。

如果您正在阅读的文件来自 Windows 平台,而您现在正在 UNIX 平台上阅读它,则可以解释该问题。在 Windows 下,记录以 <CR><LF> 结束,而在 UNIX 上,它们仅以 <LF> 结束。UNIX 文件系统将 <CR> 视为记录的一部分。

如果是这种情况,您可以非常确定在每条读取记录的末尾都会有一个 <CR>。有很多方法可以解决这个问题:

方法 1:正如您已经指出的,在通过 COBOL 程序进行处理之前,使用 Notepad++ 或其他一些工具对文件进行预编辑以删除 <CR> 字符。就个人而言,我认为这不是最好的方法。我更喜欢仅使用 COBOL 解决方案,因为它涉及的处理步骤更少。

方法 2:在处理之前修剪每个输入记录的最后一个字符。最后一个字符应始终为 <CR>。如果您正在以可变长度读取记录并且具有可用的实际输入记录长度,请尝试以下操作。

SUBTRACT 1 FROM REC_LEN
MOVE INPUT_REC(1:REC_LEN) TO WS_BUFFER
UNSTRING WS_BUFFER INTO...

方法3:在UNSTRINGing时把<CR>当作分隔符,如下:

UNSTRING INPUT_REC DELIMITED BY "," OR x"0D"
    INTO WS_ID_1, WS_ID_2, WS_CODE, WS_DESCRIPTION, WS_FLAG, WS_AMOUNT_TXT

方法 4:通过用空格替换尾随的非数字/非小数点字符来调整来自 UNSTRING 的最后一个接收字段。我在这个问题的前面稍微概述了这个解决方案。您还可以使用选项(格式 2)探索INSPECT语句。REPLACING这应该能够做几乎相同的事情 - 只需将所有 x"00" 替换为 SPACE,将 x"0D" 替换为 SPACE。

有志者,事竟成。上述任何解决方案都应该适合您。选择一个你最舒服的。

于 2011-07-28T14:58:31.547 回答
0

^M 是回车。

Google Refine对纠正这些数据有用吗?

于 2011-07-28T08:58:18.310 回答