如何在Java中从COBOL的COMP-3字段中读取日期?

3

我正在使用JRecord读取COBOL数据文件,其中有头部记录和详细记录,因此我使用了SPLIT_01_LEVEL和CopyBook文件格式作为FMT_OPEN_COBOL的解析方式。在平面文件中有一些日期字段是以COMP-3格式存储的,我无法理解如何将它们转换为Java日期字段。

ICobolIOBuilder iob = CobolIoProvider.getInstance()
                 .newIOBuilder(copybookName)
                     .setCopybookFileFormat(Convert.FMT_OPEN_COBOL)
                     .setSplitCopybook(CopybookLoader.SPLIT_01_LEVEL);
//I fetched fields as below
line.getFieldValue(field).asString();

CopyBook的字段如下:

MPOH-ENTRY-DATE              PIC S9(7) COMP-3.
MPOH-STATUS-DATE             PIC S9(7) COMP-3.
MPOH-APPROVED-DATE           PIC S9(7) COMP-3.
MPOH-ORDER-DATE              PIC S9(7) COMP-3.

当我按照上述方式解析时,输出结果为:
MPOH-ENTRY-DATE : 11261a1
MPOH-STATUS-DATE : 11261a1
MPOH-APPROVED-DATE : 11261a1
MPOH-ORDER-DATE : 11266140

请帮我将这些字段转换为Java日期字段。

哇哦。我以为我的COBOL时代已经过去了...你能试着获取一下那个输入的二进制表示吗?正如http://www.3480-3590-data-conversion.com/article-packed-fields.html中所述,这些数字是以压缩十进制的形式存储的 - 也许已经有一些转换代码存在,否则我很愿意自己写一些。 - Jan
嗯 - 看起来JRecord已经有了那个(该死)。看起来你需要从字段中使用asInt()而不是asString()来获取值。 - Jan
1
由于您的字段都是七位数字,最后日期中的八个字符是可疑的。显然,其他字符中的小写字母a是不正确的,但您能确认最后一个字段的值吗?好的,七位数字,那么期望的日期格式是什么?就像之前问过的那样,这些字段的十六进制值会很有用。 - Bill Woodger
1
由于日期字段是七位数字,我猜测格式是儒略日yyyyddd。例如2015338(2015年12月4日)将以COMP-3格式存储为x20 x15 x33 x8F。 - Gilbert Le Blanc
数据真的来自GNU Cobol吗? - Bruce Martin
2个回答

6

一个大问题是需要进行EBCDIC到ASCII的转换。

JRecord构建器创建

ICobolIOBuilder iob = CobolIoProvider.getInstance()
             .newIOBuilder(copybookName)
                 .setCopybookFileFormat(Convert.FMT_OPEN_COBOL)
                 .setSplitCopybook(CopybookLoader.SPLIT_01_LEVEL);

不包括setFont,因此在Unix / Linux / Windows PC上,这表示文件是ASCII。如果您正在Window / Linux / Unix上运行且文件是在主机上创建的,则情况并不好,而且数据是否真的来自GNUCobol

数据似乎已经通过EBCDIC -> Ascii转换?或者可能移位了1个字节。如果确实是GNU_Cobol,则可能需要其他格式之一,例如FMT_OPEN_COBOL_MVS。


以下4个数字都不是有效的comp-3数字:

MPOH-ENTRY-DATE : 11261a1
MPOH-STATUS-DATE : 11261a1
MPOH-APPROVED-DATE : 11261a1
MPOH-ORDER-DATE : 11266140

MPOH-ORDER-DATE现在是x'11 26 61 40',而EBCDIC原始值可能是x'11 50 81 7c'

CYY = 115 (or 2015)
 MM =  08 
 DD =  17

所以您需要进行以下操作:
  1. A binary transfer to Get the raw EBCDIC file. If it is a RECFM=VB file on the mainframe, convert it to RECFM=FB first.
  2. Add setFont("cp037") to the IOBuilder step (if you are using US ebcdic. There are different EBCDIC's for different countries e.g. cp273 for germany) .

    ICobolIOBuilder iob = CobolIoProvider.getInstance()
             .newIOBuilder(copybookName)
                 .setCopybookFileFormat(Convert.FMT_MAINFRAME)
                 .setSplitCopybook(CopybookLoader.SPLIT_01_LEVEL)
                 .setFont("cp037");
    
  3. For what it worth, the Dates look to be in a CYYMMDD format where C=0 is 1900 and C=1 is 2000


如果我不正确,请提供原始数据和复印本。
另一种选择是复印本位移的1字节错误。
例如:
MPOH-ENTRY-DATE : 1?11261
MPOH-STATUS-DATE : 1?11261
MPOH-APPROVED-DATE : 1?11261
MPOH-ORDER-DATE : 112661

但它看起来不像一个日期???


Convert.FMT_MAINFRAME和Convert.FMT_OPEN_COBOL之间并没有太大的区别。但这些是不同之处:

  • GNU Cobol有1、2、4、8字节的二进制整数,而主机有2、4、8个字节。
  • 在GNU-Cobol(在英特尔硬件上)中,Comp-5是小端序的(主机是大端序的)。
  • 数字以区域码方式存储的方法不同。
  • 浮点数不同(comp-1、comp-2)。

以下字段是可能会看到差异的地方:

03 f1              pic s9(3).
03 f2              pic s99 comp.
03 f3              pic s9(4) comp-5  
03 f4     comp-1.
03 f5     comp-2.

@Bruce Martin,我不确定它是否是GNU Cobol,也不确定我收到的文件是原始的ebcdic文件还是已经重新格式化过了。我正在尝试找到正确的人来回答这些问题。同时,我尝试在我的Windows上将字体设置为cp037,但我得到了错误的值或一些特殊字符。但如果我不使用任何字体,则我至少能够获取一些字段(除了COMP-3字段)的值,这些字段我能够在文件中识别出来。 - java-ocean
您已将问题标记为Mainframe,如果它确实来自IBM Mainframe(ZOS),那么几乎可以确定是FMT_Mainframe。如果是IBM AS-400,请尝试使用FMT_Mainframe。Cobol文件来自哪个操作系统?还要询问他们正在使用哪个Cobol编译器。 - Bruce Martin
我无法获取有关该文件的更多信息,我认为我没有获得原始文件,这就是错误值的原因。由于我无法验证,因此我无法选择任何答案作为正确答案。现在我已经转移到另一个项目。 - java-ocean

1
从文档和JRecords的设置来看,您应该能够替换。
line.getFieldValue(field).asString();

使用

line.getFieldValue(field).asInt();

为了得到一些有意义的结果。如果那个 int 是 20151204 或完全不同的内容仍有待观察 - 但如果我还记得我的 COBOL 日子,它可能是 yyyyMMdd 只是以数字形式存储。


谢谢您的回复,按照您的建议尝试后,我遇到了错误:java.lang.NumberFormatException: For input string: "11261a1"。由于它只有7位数字而不是8位,我不确定我们是否可以使用yyyyMMdd。 - java-ocean
1
很奇怪。你可以分享足够的代码来制作一个可运行的示例吗?你到底改了什么? - Jan
你是在暗示布鲁斯的回答没有解决问题吗?你到目前为止改变了什么? - Jan
我使用的代码是 while((line = reader.read()) != null){ Map<String, IFieldDetail> fieldNameMap = line.getLayout().getFieldNameMap(); for(String field : fieldNameMap.keySet()){ if(field.indexOf("DATE") > 0){ System.out.print(field + " : "+line.getFieldValue(field).asInt() + "\t"); } } } - java-ocean
Bruce的回答对于理解JRecord和其他与Cobol相关的事情非常有帮助。这就是我点赞Bruce的原因。我仍在尝试按照Bruce建议的方式工作。 - java-ocean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接