【问题标题】:How can I convert a Cobol COMP field output to readable decimal in C#?如何在 C# 中将 Cobol COMP 字段输出转换为可读的十进制?
【发布时间】:2017-06-12 16:37:25
【问题描述】:

在将 cobol 程序转换为 C# 时,我遇到了 COMP:

03  Var1                     PIC X(4).
03  Var2                     PIC X(3).
03  Var3                     PIC X(3).
03  Var4                     PIC X(4).
03  Var5                     PIC X(16).
03  Var6                     PIC X(4).
03  Var7                     PIC X(2).
03  Var8                     PIC X.
03  Var9                     PIC X(4).
03  Var10                    PIC X(16).
03  Var11                    PIC S9(7)V9(2) COMP.
03  Var12                    PIC S9(7)V9(2) COMP.
03  Var13                    PIC S9(7)V9(2) COMP.
03  Var14                    PIC S9(7)V9(2) COMP.
03  Var15                    PIC S9(7)V9(2) COMP.
03  Var16                    PIC S9(7)V9(2) COMP.
03  Var17                    PIC S9(7)V9(2) COMP.
03  Var18                    PIC S9(7)V9(2) COMP.
03  Var19                    PIC S9(7)V9(2) COMP.
03  Var20                    PIC S9(7)V9(2) COMP.
03  Var21                    PIC S9(7)V9(2) COMP.
03  Var22                    PIC S9(7)V9(2) COMP.
03  Var23                    PIC S9(7)V9(2) COMP.
03  Var24                    PIC S9(7)V9(2) COMP.

我花了几个小时研究 COMP。大多数搜索都会产生关于 COMP-3 的信息,或者提到 COMP 是二进制转换。但是,cobol 程序的 COMP 输出是非 COMP 字段,后跟(括号之间):

( F ” " )

而实际值都是0.00,除了var13是64.70

注意:这些是从 Notepad++ 复制的值。另外请注意,我对 cobol 知之甚少。

如何将 COMP 转换为十进制?理想情况下,我也可以将十进制转换为 COMP,因为我需要将内容恢复为相同的格式。

我已经尝试以二进制形式读取数据:

public static void ReadBinaryFile(string directoryString)
    {
        using (BinaryReader reader = new BinaryReader(File.Open(directoryString, FileMode.Open)))
        {
            string myString = Encoding.ASCII.GetString(reader.ReadBytes(113));
            Console.WriteLine(myString);
        }
    }

编辑:在正确的轨道上

感谢 @piet.t 和 @jdweng 的帮助。

虽然此测试代码仍然存在问题,但这应该可以帮助我所在位置的任何人解决问题:

public static void ReadBinaryFile(string directoryString)
    {
        using (BinaryReader reader = new BinaryReader(File.Open(directoryString, FileMode.Open)))
        {
            string asciiPortion = Encoding.ASCII.GetString(reader.ReadBytes(57)); // Read the non-comp values

            Console.Write(asciiPortion); // Test the ascii portion 

            Console.WriteLine("var11: " + reader.ReadInt32());
            Console.WriteLine("var12: " + reader.ReadInt32());
            Console.WriteLine("var13: " + reader.ReadInt32());
            Console.WriteLine("var14: " + reader.ReadInt32());
            Console.WriteLine("var15: " + reader.ReadInt32());
            Console.WriteLine("var16: " + reader.ReadInt32());
            Console.WriteLine("var17: " + reader.ReadInt32());
            Console.WriteLine("var18: " + reader.ReadInt32());
            Console.WriteLine("var19: " + reader.ReadInt32());
            Console.WriteLine("var20: " + reader.ReadInt32());
            Console.WriteLine("var21: " + reader.ReadInt32());
            Console.WriteLine("var22: " + reader.ReadInt32());
            Console.WriteLine("var23: " + reader.ReadInt32());
            Console.WriteLine("var24: " + reader.ReadInt32());
        }
    }

编辑 2:试图找到问题

问题:每个值后面似乎都有一些垃圾值,这些值被打印为下一个 int32。

实际值:

var11 = var12 = 0.00
var13 = 58.90
var14 = 0.00
var15 = -0.14
var16 = 0.00
var17 = var18 = var19 = var20 = 0.00
var21 = var22 = var23 = var24 = 0.00

输出(带填充):

Var11:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var12:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var13:  5890  HEX: 00001702  BIN: 00000000000000000001011100000010
Var14:   368  HEX: 00000170  BIN: 00000000000000000000000101110000
Var15:   -14  HEX: FFFFFFF2  BIN: 11111111111111111111111111110010
Var16:    -1  HEX: FFFFFFFF  BIN: 11111111111111111111111111111111
Var17:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var18:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var19:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var20:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var21:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var22:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var23:     0  HEX: 00000000  BIN: 00000000000000000000000000000000
Var24:     0  HEX: 00000000  BIN: 00000000000000000000000000000000

Notepad++(复制)表示:

          p  òÿÿÿÿÿÿÿ                                

Notepad++(视觉)表示:

[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][STX][ETB][NUL][NUL]p[SOH]
[NUL][NUL]òÿÿÿÿÿÿÿ[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]
[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]
[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][LF]

编辑 3:解决方案!

@piet.t 没问题。感谢您对我的第一个问题的有用回答!这个问题是 cobol 程序特有的。我被引导相信 Var14 始终为 0,但是:

Var14 = SomeCalculationIHadNoIdeaAbout(Var13, SomeOtherNumber);

我使用RecordEdit 更轻松地调整数据(警告:程序在某些地方有点奇怪)并注意到“垃圾”值的奇怪趋势。

我的问题的真正解决方案是我几天前制作的第一个 EDIT 中的代码:/。

注意:我还必须使用一个换行符,我没有放入该代码中。为此,只需添加另一个 reader.ReadBytes(1);

注意 2:您可能需要查看 EBDCDIC 和/或 Endianness,这可能会使您的解决方案比我的更困难。

【问题讨论】:

  • 使用 BitConverter。可能需要使用 Big Endian 或 Little Endian,因为 IBM 通常从 microsoft 倒退。它看起来像 Cobol 使用标准 4/8 字节 IEEE 格式的浮点数。请参阅 IBM:ibm.com/support/knowledgecenter/en/SS6SG3_3.4.0/…
  • 我假设你有 4 个字节,这是你的二进制文件。由于它在文件中,因此必须将 ascii 解析为字节。然后使用 BitConverter 将字节转换为浮点/双精度。
  • @jdweng 请注意,COMP-fields 始终是定点/整数,float/double 将是 COMP-1COMP-2
  • Comp-3 真的很老了。看起来 BCD (Binary Coded Decimal).PACKED-DECIMAL 和 COMP-3 是同义词。您在 PICTURE 描述中编码的每两个十进制数字,压缩十进制项目占用 1 个字节的存储空间,除了最右边的字节仅包含一个数字和符号。当您在 PICTURE 描述中编码奇数位数时,这种格式最有效,因此最左边的字节被完全使用。压缩十进制项目被处理为用于算术目的的定点数。见维基:en.wikipedia.org/wiki/Binary-coded_decimal
  • @jdweng 我不相信 2014 年之前的 COBOL 标准规定了浮点数的内部格式;这取决于编译器编写者。 IBM 大型机 COBOL 编译器仍然使用十六进制浮点(HFP,原始 System/360 格式),即使是 6.1。保留字 STANDARD-BINARY(IEEE 二进制浮点 (BFP) 和 STANDARD-DECIMAL(十进制浮点 (DFP),IEEE-754/2008)在 2002 标准的 2010 修订版中添加,但 IBM 尚未为大型机编译器。他们这样做很容易,因为现在都使用相同的底层代码生成器。

标签: c# cobol packed-decimal


【解决方案1】:

这里的事情会变得有点复杂,因为 COBOL 程序使用的是我认为 C# 不知道的定点变量。

对于转换,将每个 PIC S9(7)V9(2) COMP-field 视为 Int32(它应该是 BigEndian 格式)。但请注意,由于 COBOL 字段声明中隐含小数点,因此您不会得到实际值而是 value*100

请注意,使用定点数据将允许精确计算带小数的值,而在 C# 中将其转换为浮点可能会导致舍入,因为二进制浮点不能总是精确地表示小数。

【讨论】:

  • 谢谢!我终于得到了一些有意义的数据。我遇到了一个奇怪的问题。当我打印出每个 int32 时,如果一个值非零,则即使它为零,下一个值也会打印为非零数。例如:var13Actual 是 58.90,var14Actual 是 0.00,但是当我读取值 var13 是 5890(很好)和 var14 是 368(不太好)。知道这是为什么吗?
  • 我会读取输入行并编写输入的十六进制转储,以查看 C# 认为的字符。如果您看到像 0x40f5f84ef9f0 这样的值,那么您正在阅读 EBCDIC(0x4e 是句点,0x40 是空格,数字是 0xf_); 0x2035382e3930 是 ASCII。字节序也可能是一个问题,特别是如果您没有按照我输入它们的顺序(大字节序)看到值。
  • @zarchasmpgmr,我进行了第二次编辑,提供了更多信息。这看起来适合您的 0xf_ 数字。当我有更多时间时,我会尝试转换。
  • @JinC 在您的示例中,垃圾看起来像移动半字节之前的字段。这可能只是一个巧合,也可能表明在数组中前进时出现了一些错误——比如前进 4 位而不是 4 字节。
  • 这对我来说真的很奇怪。我使用 ReadBytes() 读取所有文本数据,并使用 ReadInt32() 读取小数,因此我没有进行任何手动推进。错误的可能性似乎是 ReadBytes(),但我尝试将输入改变 1 以查看是否有一些我忘记的字节。如果我弄明白了,我会继续努力并更新,但我现在会标记你的答案,因为你非常有帮助,让我走上了正确的道路。感谢您的所有帮助。
猜你喜欢
  • 1970-01-01
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 2016-09-04
  • 1970-01-01
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多