1

从我们的供应商之一提供给我们的文件中解压缩包含数字和日期数据的 COMP-3 字段时,我们遇到了一些困难。

文件规范提供以下信息:

0066-0070 DATE-OPENED S9(9) COMP-3

规范表明日期将扩展为 MMDDYYYY 格式。

当我从文件中检索这块数据时,我可以将它加载到内存中,并看到我检索了 5 个字节的数据。(在文件中,每个字符有一个字节。)检索到的字节如下:

0: 10
1:  0
2: 18
3:  0
4:  2

没有符号被覆盖到最低有效数字(它总是出现的地方),所以这不是问题。这些位扩展为以下半字节:

0 1 0 0 1 2 0 0 0 2

这里有几个问题:

  1. 01001200 不太可能代表 MMDDYYYY 格式的有效日期,但这似乎是数据被打包到字段中的方式。

  2. 解压缩 COMP-3 字段时,模板指定它应扩展为 9 个字符,但如果扩展 COMP-3,它的大小将始终加倍(生成具有偶数个字符的字符串)。因此,预期大小与解压后的大小不匹配。

  3. 我在网上找到的算法似乎都无法解压这些数据。对于我们的源文件中的任何(假定的)BCD 值,似乎没有任何东西能够提出可识别的日期。

在这一点上,我怀疑我们可能没有处理真正的 BCD 格式。但是,请记住,我应该始终怀疑自己而不是工具,我正在寻求有关我对 COMP-3 格式的理解和我正在查看的数据的性质可能做错的建议。

我对格式的理解来自以下来源:

值得注意的是,在尝试解包之前,我曾尝试将数据从 EBCDIC 转换为 ASCII,反之亦然;两者都没有产生任何可理解的结果。我尝试了所有可以在 Internet 上找到的算法,但似乎没有一个产生任何有用的结果。

我想,最后,我的问题是:我真的在这里处理 BCD 或 COMP-3 数据吗?

更新

在回答一些问题时:

  1. 一旦我确定该值包含一个符号半字节,我就会清除该半字节。
  2. 我已经包含了所有我能做到的代码,这样你就可以准确地看到我在做什么。该类旨在提供大量诊断信息(如 Bytes 和 Nibbles 属性),以便您可以在解析值后查看它的结果。

我们手头有原始文件和未解析文件,可用作参考资料。我期待回来的日期是 06152008 (这不在我的脑海中,但你明白了要点)。我计算的值不是那样的。

根据要求,个人啃食:

0 1 0 0 1 2 0 0 0 2

对于那些对我的工作方式感兴趣的人,正在解包的课程:

using System.Collections.Generic;
using System.Linq;
using System.Text;

internal class PackedDecimal
{
    #region Fields
    private bool _isPositive;
    private bool _isNegative;
    private bool _isUnsigned = true;

    #endregion

    #region Constructor
    /// <summary>
    /// Initializes a new instance of the <see cref="PackedDecimal"/> class.
    /// </summary>
    public PackedDecimal()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="PackedDecimal"/> class.
    /// </summary>
    /// <param name="compressedDecimal">The compressed decimal.</param>
    public PackedDecimal(string compressedDecimal)
    {
        this.ParsedValue = this.Parse(compressedDecimal);
    }
    #endregion

    #region Properties
    /// <summary>
    /// Gets the bytes.
    /// </summary>
    public IEnumerable<byte> Bytes { get; private set; } 

    /// <summary>
    /// Gets the hexadecimal values.
    /// </summary>
    public IEnumerable<string> HexValues { get; private set; }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is positive.
    /// </summary>
    /// <value>
    /// <c>true</c> if this instance is positive; otherwise, <c>false</c>.
    /// </value>
    public bool IsPositive
    {
        get { return this._isPositive; }
        set
        {
            this._isNegative = !this.IsPositive;
            this._isUnsigned = false;
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is negative.
    /// </summary>
    /// <value>
    /// <c>true</c> if this instance is negative; otherwise, <c>false</c>.
    /// </value>
    public bool IsNegative
    {
        get { return this._isNegative; }
        set
        {
            this._isNegative = value;
            this._isPositive = !value;
            this._isUnsigned = false;
        }
    }

    /// <summary>
    /// Gets a value indicating whether this instance is unsigned.
    /// </summary>
    /// <value>
    /// <c>true</c> if this instance is unsigned; otherwise, <c>false</c>.
    /// </value>
    public bool IsUnsigned { get { return this._isUnsigned; } }

    /// <summary>
    /// Gets the nibbles.
    /// </summary>
    public IEnumerable<int> Nibbles { get; private set; }

    /// <summary>
    /// Gets the parsed value.
    /// </summary>
    public string ParsedValue { get; private set; }
    #endregion

    /// <summary>
    /// Parses the specified value.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public string Parse(string value, SourceEncoding sourceEncoding = SourceEncoding.Ascii, int decimalPlaces = 0)
    {
        var localValue = value; // Encoding.Convert(Encoding.ASCII, Encoding.GetEncoding("IBM037"), value.ToByteArray()).FromByteArray();
        var sign = this.GetSign(localValue, out localValue);
        var bytes = localValue.ToByteArray();
        var nibbles = new List<int>();

        var buffer = new StringBuilder();

        foreach (var b in bytes)
        {
            var hi = (int)b.HiNibble();
            var lo = (int)b.LoNibble();
            nibbles.Add(hi);
            nibbles.Add(lo);

            buffer.AppendFormat("{0}{1}", hi, lo);
        }

        this.Bytes = bytes;
        this.Nibbles = nibbles;
        this.HexValues = nibbles.Select(v => v.ToString("X"));

        switch (sign)
        {
            case Sign.Unsigned:
                this.ParsedValue = buffer.ToString();
                break;

            case Sign.Positive:
                this.ParsedValue = "+" + buffer;
                break;

            case Sign.Negative:
                this.ParsedValue = "-" + buffer;
                break;
        }

        this.IsPositive = sign == Sign.Positive;
        this.IsNegative = sign == Sign.Negative;

        return this.ParsedValue;
    }

    #region GetSign Method
    /// <summary>
    /// Gets the sign for the packed decimal represented by this instance.
    /// </summary>
    /// <param name="value">The value to analyze.</param>
    /// <param name="buffer">Receives <paramref name="value"/>, less the sign digit if it is present.</param>
    /// <returns>The sign for the packed decimal represented by this instance.</returns>
    /// <remarks>If the value provided does not include a sign digit, it is assumed to be unsigned.</remarks>
    private Sign GetSign(string value, out string buffer)
    {
        var lastDigit = value.ToByteArray().Last();
        var loNibble = lastDigit.LoNibble();
        var hiNibble = lastDigit.HiNibble();

        var result = Sign.Unsigned;

        var hasSignDigit = true;

        switch (hiNibble)
        {
            case 0xC0: // "c"
                result = Sign.Positive;
                break;

            case 0xD0: // "d"
                result = Sign.Negative;
                break;

            case 0xF0: // "f"
                result = Sign.Unsigned;
                break;

            default:
                hasSignDigit = false;
                break;
        }

        // Remove the sign digit if it's present.
        buffer = hasSignDigit 
            ? value.Substring(0, value.Length - 1) + loNibble 
            : value;

        return result;
    }
    #endregion

    #region Sign Enum
    private enum Sign
    {
        Unsigned,
        Positive,
        Negative
    }
    #endregion
}

以及支持它的扩展方法:

using System;
using System.Linq;
using System.Text;

public static class Extensions
{
    /// <summary>
    /// Gets the high nibble (the high 4 bits) from a byte.
    /// </summary>
    /// <param name="value">The byte from which the high 4-bit nibble will be retrieved.</param>
    /// <returns>A byte containing the value of this byte, with all bits shifted four bits to the right.</returns>
    public static byte HiNibble(this byte value)
    {
        return (byte)((value & 0xF0) >> 4);
    }

    /// <summary>
    /// Gets the low nibble (the lowest 4 bits) from this byte.
    /// </summary>
    /// <param name="value">The byte from which the low 4-bit nibble will be retrieved.</param>
    /// <returns>A byte containing the value of this byte, with the high four bits discarded.</returns>
    public static byte LoNibble(this byte value)
    {
        return (byte)(value & 0x0F);
    }

    /// <summary>
    /// Gets the individual bytes from a string.
    /// </summary>
    /// <param name="value">The string to convert to a byte array.</param>
    /// <returns>An array of bytes representing the string.</returns>
    public static byte[] ToByteArray(this string value)
    {
        var bytes = new byte[Encoding.ASCII.GetByteCount(value)];
        Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
}

public enum SourceEncoding
{
    Ascii,
    Ebcdic
}
4

0 回答 0