【问题标题】:Unpacking EBCDIC Packed Decimals (COMP-3) in an ASCII Conversion在 ASCII 转换中解压缩 EBCDIC 压缩十进制 (COMP-3)
【发布时间】:2011-01-22 07:42:50
【问题描述】:

我正在使用Jon Skeet's EBCDIC implementation in .NET 从大型机系统读取通过 FTP 以二进制模式下载的 VSAM 文件。它非常适合以这种编码方式读取/写入,但它没有任何东西可以读取压缩十进制值。我的文件包含这些,我需要将它们解压缩(显然要以更多字节为代价)。

我该怎么做?

我的字段定义为PIC S9(7)V99 COMP-3.

【问题讨论】:

  • 是 EBCDIC 格式的压缩十进制部分,还是这只是一个碰巧同时使用两者的文件?恐怕我不熟悉那种格式:(
  • 这是一个同时使用两者的文件。这是一种通过压缩数字来节省字节的老式方法。 a -99999.99 可以存储在 4 个字节中。
  • @T.E.D.哈哈谢谢!仍然努力节省几个字节有点有趣,但是当你有数百万条记录时,我想它会加起来。

标签: c# .net ascii ebcdic comp-3


【解决方案1】:

啊,BCD。如果您在 6502 装配中使用它,请鸣喇叭。

当然,最好的办法是让 COBOL MOVE 为您完成这项工作!这些可能性之一可能会有所帮助。

(可能性#1)假设您确实可以访问大型机和源代码,并且输出文件仅供您使用,请修改程序,使其仅将值移动到普通解压缩的 PIC S9(7)V99 .

(可能性 #2)假设它不是那么容易(例如,文件是其他 pgms 的输入,或者无法更改代码),您可以在系统上编写另一个 COBOL 程序来读取该文件并写入另一个文件。将带有 BCD 的文件记录布局剪切并粘贴到用于输入和输出文件的新程序中。将输出版本修改为非打包。读取一条记录,执行“移动对应”以传输数据,然后写入,直到 eof。然后传输那个文件。

(可能性#3)如果您无法触摸大型机,请注意您在评论中链接的文章中的描述。 BCD相对简单。 可能就这么简单(vb.net):

Private Function FromBCD(ByVal BCD As String, ByVal intsz As Integer, ByVal decsz As Integer) As Decimal
    Dim PicLen As Integer = intsz + decsz
    Dim result As Decimal = 0
    Dim val As Integer = Asc(Mid(BCD, 1, 1))
    Do While PicLen > 0
        result *= 10D
        result += val \ 16
        PicLen -= 1
        If PicLen > 0 Then
            result *= 10D
            result += val Mod 16
            PicLen -= 1
            BCD = Mid(BCD, 2)
        End If
        val = Asc(Mid(BCD, 1, 1))
    Loop
    If val Mod 16 = &HD& Then
        result = -result
    End If
    Return result / CDec(10 ^ decsz)
End Function

我用这个调用的一些变体对其进行了测试:

MsgBox(FromBCD("@" & Chr(13 + 16), 2, 1))

例如,是 -40.1。但只有少数。所以它可能仍然是错误的。

那么如果你的 comp-3 从输入记录布局的第 10 个字节开始,这将解决它:

dim valu as Decimal = FromBCD(Mid(InputLine,10,5), 7,2))

注意数据转换文章中用于发送字节数的公式,以及 V 之前和之后的 9 数。

将结果存储在小数中以避免舍入错误。特别是如果是$$$。 Float & Double 会让你伤心!如果你不处理它,即使是一个字符串也更好。

当然可能会更难。在我工作的地方,大型机是每字节 9 位。严肃的。这就是使前两种可能性如此突出的原因。当然,真正让他们变得更好的是,您可能只是一名 PC 程序员,这是让大型机程序员为您完成工作的一个很好的借口!如果你有幸拥有这样的选择......

和平, -铝

【讨论】:

  • 谢谢艾尔!可能会更改 COBOL 程序或编写另一个程序来解包,但目标是在 .NET 中完成此操作,因此我们可以直接运行 VSAM -> FTP 任务。根据二进制表示,我如何知道 V(十进制)在哪里?这甚至可能吗?主机怎么知道?再次感谢,感谢您的帮助。
  • 乔希,您无法仅从数据中知道 V 的位置;你需要PIC。大型机通过 PIC 对其进行跟踪。打孔卡时代典型的精益、省位解决方案。但是如果你仔细想想,系统永远不会没有它需要的信息。哎呀,如果您自己看不到记录布局,那么无论如何您都将不得不破解它!
  • 是的,你是对的,我认为没有办法。我确实确定了所有偏移量并且记录布局在这里,我只是好奇是否有办法以编程方式计算小数位。好的,我明天会试一试这个功能,让你知道它是如何工作的!再次感谢您在这里的帮助,我希望我能投票两次。
  • 再次感谢您的回答!六年后仍然有用。
【解决方案2】:

我使用这种扩展方法进行压缩十进制 (BCD) 转换:

    /// <summary>
    /// computes the actual decimal value from an IBM "Packed Decimal" 9(x)v99 (COBOL COMP-3) format
    /// </summary>
    /// <param name="value">byte[]</param>
    /// <param name="precision">byte; decimal places, default 2</param>
    /// <returns>decimal</returns>
    public static decimal FromPackedDecimal(this byte[] value, byte precision = 2)
    {
        if (value.Length < 1)
        {
            throw new System.InvalidOperationException("Cannot unpack empty bytes.");
        }
        double power = System.Math.Pow(10, precision);
        if (power > long.MaxValue)
        {
            throw new System.InvalidOperationException(
                $"Precision too large for valid calculation: {precision}");
        }
        string hex = System.BitConverter.ToString(value).Replace("-", "");
        var bytes = Enumerable.Range(0, hex.Length)
                 .Select(x => System.Convert.ToByte($"0{hex.Substring(x, 1)}", 16))
                 .ToList();
        long place = 1;
        decimal ret = 0;
        for (int i = bytes.Count - 2; i > -1; i--)
        {
            ret += (bytes[i] * place);
            place *= 10;
        }
        ret /= (long)power;
        return (bytes.Last() & (1 << 7)) != 0 ? ret * -1 : ret;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 2010-09-13
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    相关资源
    最近更新 更多