【问题标题】:Read COBOL COMP data in C#在 C# 中读取 COBOL COMP 数据
【发布时间】:2019-09-22 10:16:49
【问题描述】:

我是 COBOL 的新手,我一直在尝试从作为表格输出的文本文件中读取记录信息。

我可以接受的大多数非 comp 数据类型,这是我遇到的“COMP”数据类型。

我今天一整天都在努力解决这个问题,尽可能多地阅读。

下面的日期字段是我无法转换为日期字符串的:

05 VALDATE          PIC 9(6) COMP
05 PAYDATE          PIC 9(6) COMP
05 SYSDATE          PIC 9(6) COMP

据我了解,上述所有这些类型在文件中都将是 4 个字节。

它们应该是代表YYMMDD 的日期,但数据似乎没有那么小。我查看了 EBCDIC 并反转了 byte[] 数据并使用 BitConverter.ToUNIT32() 并更改了用于读取文件的编码,但没有运气。

我读到计算成整数的日期存储为从 1601 年 1 月 1 日开始的天数,因此下面的代码试图将值添加到 1601。(http://www.techtricky.com/cobol-date-functions-list-add-find-duration/)

我的问题是文本文件中的数据不正确,或者我错过了获取类似于YYMMDD 的日期的步骤。

以上3个的数据如下:

[ 32] [237] [ 44] [  4] | 00100000 11101101 00101100 00000100
[ 33] [ 14] [ 32] [237] | 00100001 00001110 00100000 11101101
[131] [ 48] [ 48] [ 48] | 10000011 00110000 00110000 00110000

以及我如何打开文件,我已将编码更改为 ascii,但没有运气:

 using (BinaryReader reader = new BinaryReader(File.Open(nFilePath, FileMode.Open), Encoding.Default))

用于尝试读取 COMP 字段的代码:

  public class DateFromUIntExtractor : LineExtractor
  {
    public DateFromUIntExtractor() : base(4)
    {
    }

    public override string ExtractText(BinaryReader nReader)
    {
      // e.g 32,237,44,44, included but commented out things i've tried
      byte[] data = nReader.ReadBytes(Length); // Length = 4

      //Array.Reverse(data); - Makes num = 552414212
      //data = ConvertAsciiToEbcdic(data);

      int num = BitConverter.ToUInt32(data, 0);
      // in this example num = 70053152

      DateTime date = new DateTime(1601,1,1);
      date = date.AddDays(num); // Error : num is too big

      Extract = date.ToString("yyyyMMdd");
      return Extract;
    }
  }

数据是否格式错误?还是我错过了什么?

更新

我要完成的任务是复制一个 COBOL 程序,该程序将数据从一种定义转换为另一种但采用 CSV 格式,因为程序输出一个 .dat 文件。

来源

我对源定义的缺乏经验的解释是文本文件中的数据是PUA-ICGROUP 或PUA-PUGROUP。查看 COBOL 程序,它选择 PUA-ICGROUP 时选择 PUA-HEADER>PUA-KEY>PUA-RTYPE = "03",其他的都是 PUA-PUGROUP。

C-WRITE-START.
    IF  PUA-RTYPE = 3 THEN
        PERFORM C-WRITE-A
    ELSE
        PERFORM C-WRITE-B
    END-IF.

C-WRITE-EXIT.
    EXIT.

定义

01  DLRPUARC.
    03  PUA-HEADER.
        05  PUA-KEY.
            07  PUA-CDELIM         PIC 99.
            07  PUA-SUPNO          PIC 9(7).
            07  PUA-RTYPE          PIC 99.
            07  PUA-REF            PIC 9(9).
            07  PUA-SEQ            PIC 999.
        05  PUA-ALTKEY.
            07  PUA-ACDELIM        PIC 99.
            07  PUA-ASUPNO         PIC 9(7).
            07  PUA-ATRNDATE       PIC 9(6).
            07  PUA-ARTYPE         PIC 99.
            07  PUA-AREF           PIC 9(9).
            07  PUA-ASEQ           PIC 999.
        05  FILLER                 PIC X(82).

    03  PUA-ICGROUP REDEFINES PUA-HEADER.
        05  FILLER                 PIC X(52).
        05  PUA-ICEXTREF           PIC X(10).
        05  PUA-ICORDNO            PIC 9(11).
        05  PUA-ICVALDATE          PIC 9(6) COMP.
        05  PUA-ICPAYDATE          PIC 9(6) COMP.
        05  PUA-ICSYSDATE          PIC 9(6) COMP.
        05  PUA-ICTRNVAL           PIC S9(9).
        05  PUA-ICCLRREF           PIC 9(6).
        05  PUA-ICDELDATE          PIC 9(6) COMP.
        05  PUA-ICOTHQRY           PIC X.
        05  PUA-ICPRCQRY           PIC X.
        05  PUA-ICMRSQRY           PIC X.
        05  PUA-ICDSCTYPE          PIC 9.
        05  PUA-ICDSCVAL           PIC S9(9) COMP.
        05  PUA-ICVATCODE          PIC 9.
        05  PUA-ICVATAMT           PIC S9(8) COMP.
        05  PUA-ICTAXAMT           PIC S9(8) COMP.
        05  PUA-ICMRSREF           PIC 9(6).
        05  PUA-ICSUBDIV           PIC 9.
        05  PUA-ICCOSTCTR          PIC X(5).
        05  PUA-ICSEQIND           PIC X.
        05  FILLER                 PIC X(4).

    03  PUA-PUGROUP REDEFINES PUA-HEADER.
        05  FILLER                 PIC X(52).
        05  PUA-PUEXTREF           PIC X(10).
        05  PUA-PUORDNO            PIC 9(11).
        05  PUA-PUVALDATE          PIC 9(6) COMP.
        05  FILLER                 PIC XXX.
        05  PUA-PUSYSDATE          PIC 9(6) COMP.
        05  PUA-PUTRNVAL           PIC S9(9).
        05  PUA-PUCLRREF           PIC 9(6).
        05  PUA-PUDELDATE          PIC 9(6) COMP.
        05  PUA-PUOTHQRY           PIC X.
        05  PUA-PUSUBDIV           PIC 9.
        05  FILLER                 PIC X(32).

输出定义

01  OUT-A-REC.
    03  OUT-A-PUA-CDELIM             PIC 99.
    03  OUT-A-PUA-SUPNO              PIC 9(7).
    03  OUT-A-PUA-RTYPE              PIC 99.
    03  OUT-A-PUA-REF                PIC 9(9).
    03  OUT-A-PUA-SEQ                PIC 999.
    03  OUT-A-PUA-ATRNDATE           PIC 9(8).
    03  OUT-A-PUA-ICEXTREF           PIC X(10).
    03  OUT-A-PUA-ICORDNO            PIC 9(11).
    03  OUT-A-PUA-ICVALDATE          PIC 9(8).
    03  OUT-A-PUA-ICPAYDATE          PIC 9(8).
    03  OUT-A-PUA-ICSYSDATE          PIC 9(8).
    03  OUT-A-PUA-ICTRNVAL           PIC S9(9) SIGN LEADING SEPARATE.
    03  OUT-A-PUA-ICCLRREF           PIC 9(6).
    03  OUT-A-PUA-ICDELDATE          PIC 9(8).
    03  OUT-A-PUA-ICOTHQRY           PIC X.
    03  OUT-A-PUA-ICPRCQRY           PIC X.
    03  OUT-A-PUA-ICMRSQRY           PIC X.
    03  OUT-A-PUA-ICDSCTYPE          PIC 9.
    03  OUT-A-PUA-ICDSCVAL           PIC S9(9) SIGN LEADING SEPARATE.
    03  OUT-A-PUA-ICVATCODE          PIC 9.
    03  OUT-A-PUA-ICVATAMT           PIC S9(8) SIGN LEADING SEPARATE.
    03  OUT-A-PUA-ICTAXAMT           PIC S9(8) SIGN LEADING SEPARATE.
    03  OUT-A-PUA-ICMRSREF           PIC 9(6).
    03  OUT-A-PUA-ICSUBDIV           PIC 9.
    03  OUT-A-PUA-ICCOSTCTR          PIC X(5).
    03  OUT-A-PUA-ICSEQIND           PIC X.
    03  OUT-A-CTRL-M                 PIC X.
    03  OUT-A-NL                     PIC X.

FD  F-OUTPUTB
    LABEL RECORDS OMITTED.

01  OUT-B-REC.
    03  OUT-B-PUA-CDELIM             PIC 99.
    03  OUT-B-PUA-SUPNO              PIC 9(7).
    03  OUT-B-PUA-RTYPE              PIC 99.
    03  OUT-B-PUA-REF                PIC 9(9).
    03  OUT-B-PUA-SEQ                PIC 999.
    03  OUT-B-PUA-ATRNDATE           PIC 9(8).
    03  OUT-B-PUA-PUEXTREF           PIC X(10).
    03  OUT-B-PUA-PUORDNO            PIC 9(11).
    03  OUT-B-PUA-PUVALDATE          PIC 9(8).
    03  OUT-B-PUA-PUSYSDATE          PIC 9(8).
    03  OUT-B-PUA-PUTRNVAL           PIC S9(9) SIGN LEADING SEPARATE.
    03  OUT-B-PUA-PUCLRREF           PIC 9(6).
    03  OUT-B-PUA-PUDELDATE          PIC 9(8).
    03  OUT-B-PUA-PUOTHQRY           PIC X.
    03  OUT-B-PUA-PUSUBDIV           PIC 9.
    03  OUT-B-CTRL-M                 PIC X.
    03  OUT-B-NL                     PIC X.

程序

以下是 cobol 程序对日期所做的一小部分摘录,无论天气如何,它们的来源是 COMP 与否。 (我没有写这段代码)。它似乎正在尝试解决 2kY 问题。

IF  PUA-ATRNDATE IS ZERO THEN
    MOVE ZERO                TO OUT-A-PUA-ATRNDATE
ELSE
    MOVE PUA-ATRNDATE        TO W-DATE-6DIGIT
    MOVE W-DATE-SEG1         TO W-DATE-YY
    MOVE W-DATE-SEG2         TO W-DATE-MM
    MOVE W-DATE-SEG3         TO W-DATE-DD
    IF W-DATE-YY > 50 THEN
        MOVE "19"            TO W-DATE-CC
    ELSE
        MOVE "20"            TO W-DATE-CC
    END-IF
    MOVE W-DATE-CCYYMMDD     TO OUT-A-PUA-ATRNDATE
END-IF.

MOVE PUA-ICEXTREF            TO OUT-A-PUA-ICEXTREF.
MOVE PUA-ICORDNO             TO OUT-A-PUA-ICORDNO.

IF  PUA-ICVALDATE IS ZERO THEN
    MOVE ZERO                TO OUT-A-PUA-ICVALDATE
ELSE
    MOVE PUA-ICVALDATE       TO W-DATE-6DIGIT
    MOVE W-DATE-SEG1         TO W-DATE-YY
    MOVE W-DATE-SEG2         TO W-DATE-MM
    MOVE W-DATE-SEG3         TO W-DATE-DD
    IF W-DATE-YY > 50 THEN
        MOVE "19"            TO W-DATE-CC
    ELSE
        MOVE "20"            TO W-DATE-CC
    END-IF
    MOVE W-DATE-CCYYMMDD     TO OUT-A-PUA-ICVALDATE
END-IF.

程序工作存储部分

01  W-DATE-6DIGIT               PIC 9(6).
01  W-DATE-6DIGIT-REDEF REDEFINES W-DATE-6DIGIT.
    03  W-DATE-SEG1             PIC 99.
    03  W-DATE-SEG2             PIC 99.
    03  W-DATE-SEG3             PIC 99.

01  W-DATE-CCYYMMDD             PIC 9(8).
01  W-DATE-CCYYMMDD-REDEF REDEFINES W-DATE-CCYYMMDD.
    03  W-DATE-CC               PIC 99.
    03  W-DATE-YY               PIC 99.
    03  W-DATE-MM               PIC 99.
    03  W-DATE-DD               PIC 99.

数据

从 Notepad++ 复制,每行从 '220...' 开始,结束列是 135,然后再进入下一行,意思是长度是 134(?)

    2200010010300005463400022000100106062003000054634000062703    09720200000 í,! íƒ00056319D001144ÕšNNN0    1 G¨    000000197202G    
    2200010010300005463500022000100106062903000054635000062858    09720200000 í, í" íƒ00082838{050906±RNNN0    1 áð    000000197202G    
    2200010010300005465500022000100106073003000054655000063378    09720200000 í í† í00179637A050906±RNNN0    1     000000197202G    

注意到上面缺少一些符号:

    2200010010300005463400022000100106062003000054634000062703    09720200000 í,[EOT]![SO] íƒ00056319D001144[SOH]ÕšNNN0    1 [SOH]G¨    000000197202G    
    2200010010300005463500022000100106062903000054635000062858    09720200000 í, í" íƒ00082838{050906[SOH]±RNNN0    1 [SOH]áð    000000197202G    
    2200010010300005465500022000100106073003000054655000063378    09720200000 í í† í00179637A050906[SOH]±RNNN0    1 [EOT][NAK][EM]    000000197202G    

更新 2

我在下面接受了 Rick Smith 的回答,因为当我将他的数据放入时,我得到了正确的日期时间值。因此,要么我的数据是伪造的,要么是其他原因,因为我的数据会在未来 1000 年后抛出错误或日期时间值。

我已经能够得到这些日期时间实际应该是什么的输出 CSV:

[使用:int n = ((b[0]

HEX: 0x20 0xED 0x2C
BIN: 32   237  44
INT: 2157868     (longer than 6 digit)
Actual DATE: 2006-07-16

HEX: 0x04 0x21 0x0e
BIN: 4    33   14
INT: 270606      (correct but segments are in reverse)
Actual DATE: 2006-06-27

HEX: 0x20 0xED 0x83
BIN: 32   237  131
INT: 2157955     (longer than 6 digits)
Actual DATE: 2006-08-03

更新 3

原来是坏数据...

【问题讨论】:

  • cobol 中的 COMP 通常是一个大端整数。
  • 这些数字毫无意义。看来这些字段每个应该是 3 个字节,大端二进制。但是编码可能会改变值。只需将数据读入字节数组并提供这些值。剩下的我们可以稍后处理。
  • @RickSmith 是对的。如果文件是 EBCDIC,则“如果记录包含二进制、BCD、浮点或非单独符号显示数字数据,则必须逐个字段完成 EBCDIC 到 ASCII 转换”-来源:talsystems.com/tsihome_html/downloads/C2IEEE.htm-我建议你按照我在第一条评论中所说的去做或使用转换工具。
  • @NoChance 文件不是 EBCDIC,48 的三个匹配项是 ASCII 000。这意味着没有转化。
  • documentation for ICOBOL 可能很有趣。特别是“USAGE IS BINARY 或 COMPUTATIONAL 子句指定计算机存储中数字项的二进制补码 big-endian 二进制表示。下表列出了存储 BINARY 和 COMPUTATIONAL 项所需的字节。”在第 196 页

标签: c# cobol data-conversion


【解决方案1】:

我使用示例数据的第一条记录中包含的值创建了一个包含三个日期字段的 COBOL 文件。第一个和第三个日期是 YYMMDD,第二个是 DDMMYY。

日期在代码中给出,并且与您尝试读取的日期具有相同的格式,3 字节,大端二进制。

   environment division.
   input-output section.
   file-control.
       select out-file assign "dates.dat"
           organization sequential
       .
   data division.
   file section.
   fd out-file.
   01 date-rec.
     02 date-1 comp pic 9(6).
     02 date-2 comp pic 9(6).
     02 date-3 comp pic 9(6).
   procedure division.
   begin.
       open output out-file
       move 060716 to date-1
       move 270606 to date-2
       move 060803 to date-3
       write date-rec
       close out-file
       stop run
       .

然后,此 C# 程序使用 BinaryReader 读取这些日期,并显示字节、十进制值和转换后的日期。请注意,我评论了/*, Encoding.Default*/,因为它不需要。

using System;
using System.Globalization;
using System.IO;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] b = { 0, 0, 0 };
            string s;
            DateTime d = new DateTime();
            using (BinaryReader reader = new BinaryReader(File.Open(@"y:\dates.dat", FileMode.Open)/*, Encoding.Default*/))
            {
                for (int i = 0; i < 3; i++)  // Three dates in file
                {
                    b = reader.ReadBytes(b.Length);
                    Console.WriteLine("Bytes: {0}, {1}, {2}", b[0].ToString("X2"), b[1].ToString("X2"), b[2].ToString("X2"));
                    int n = ((b[0] << 16) + (b[1] << 8) + b[2]);
                    s = n.ToString("D6");
                    switch (i)
                    {
                        case 0:
                        case 2:
                            Console.WriteLine("Date(YYMMDD): {0}", s);
                            d = DateTime.ParseExact(s, "yyMMdd", CultureInfo.InvariantCulture);
                            Console.WriteLine("Date(yyyyMMdd): {0}", d.ToString("yyyyMMdd"));
                            break;
                        case 1:
                            Console.WriteLine("Date(DDMMYY): {0}", s);
                            d = DateTime.ParseExact(s, "ddMMyy", CultureInfo.InvariantCulture);
                            Console.WriteLine("Date(yyyyMMdd): {0}", d.ToString("yyyyMMdd"));
                            break;
                        default:
                            break;
                    }
                    Console.WriteLine("");
                }
            }
        }
    }
}

这是输出:

Bytes: 00, ED, 2C
Date(YYMMDD): 060716
Date(yyyyMMdd): 20060716

Bytes: 04, 21, 0E
Date(DDMMYY): 270606
Date(yyyyMMdd): 20060627

Bytes: 00, ED, 83
Date(YYMMDD): 060803
Date(yyyyMMdd): 20060803

DateTime.ParseExact(s, "yyMMdd", CultureInfo.InvariantCulture); 来自这个问题的答案String to DateTime conversion as per specified format。

【讨论】:

  • 感谢您的回答,使用您的值,例如 (0x0e, 0xf6, 0x87) 它可以工作,所以我将其标记为正确答案,但最终我的数据以 ( 0x20 0xED 0x2C ) 并在使用 n.ToString("D6"), "D8" 将字符串 s 变为 '2157868' 时解析到日期时间时引发错误。我已经确认这三个日期应该是 20060716 20060627 20060803
  • YYMMDD 060716 是 00 ED 2C,060637 是 00 EC D3,060803 是 00 ED 83。这是一个 hack,但在将字节转换为 int 之前,您可以做到if (b[0] == 32) { b[0] = 0; }。但是,其他一些值,[ 4] [ 33] [ 14],对于第二个日期不正确。我确实注意到,当我将文件 (dates.dat) 加载到 Notepad++ 中时,0x00 更改为 0x20。您正在处理的数据可能正在发生类似的事情。
  • 谢谢瑞克,你帮助我恢复了理智。是的,Notepad++ 确实这样做了,给我的数据很糟糕,并且故意将 NULL 字符更改为空格字符 (32)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
相关资源
最近更新 更多