【问题标题】:Unpack Mainframe packed Decimal (BCD) with PHP使用 PHP 解压大型机压缩十进制 (BCD)
【发布时间】:2016-06-23 14:30:40
【问题描述】:

我从大型机获得了一个数据文件。我已经用 PHP 处理了 EBCDIC 到 latin1 的转换。但现在这些压缩十进制字段都剩下了。

例如,数字 12345 被打包成 3 个字节,看起来像:x'12345C'

负数就像:x'12345D'

所以右半字节告诉符号。有没有办法用 PHP 轻松做到这一点?

现在我这样做:

$bin = "\x12\x34\x5C";
var_dump(
    unpack("H*", $bin)
);

结果:

array(1) {
  [1]=>
  string(4) "123c"
}

现在我可以检查最后一个符号是 C 还是 D 并手动完成所有操作。但也许有更好的解决方案?

【问题讨论】:

  • 更好的解决方案是让大型机人员为您提供“仅文本”的所有内容。然后您可以在文件/记录级别进行代码集转换,并且不会有任何问题。这意味着一个“单独的符号”,以及一个比例因子或一个实际的小数点,以您更容易做的为准。除非 Mainframe 程序是用 Assembler 编码的,否则它们生成这样的数据是微不足道的,那么它就省去了你做很多事情的时间。另请参阅标记为 packed-decimal 的其他问题。
  • TL;DR "大型机。但也许有更好的解决方案?"是的。让恐龙处理程序来解决。
  • @Rhymoid 你的意思是 TS:DR?没有办法弄清楚。纳达。零。没有任何。你不需要想出很酷的绰号来让我们了解你有阅读困难。随便写。
  • Bill 是对的 - 在大型机上进行转换。它可以在 toytown 计算机停机的额外正常运行时间内完成。
  • 当然最好在大型机上进行。但就我而言,我只是在 PC 上已经有了这些文件。它是每周完成的 dli 数据库转储。

标签: php mainframe bcd packed-decimal


【解决方案1】:

正如比尔所说,让大型机人员将文件转换为大型机上的文本并发送文本文件,排序等实用程序可以在大型机上执行此操作。也是只是压缩十进制在文件中还是你有二进制Zoned Decimal以及???

如果您坚持在 PHP 中执行此操作,则需要在进行 EBCDIC 转换之前进行压缩十进制转换,因为对于像 x'400c' 这样的压缩十进制 EBCDIC 转换器将查看 x'40' 并说这是一个空格并将其转换为 x'20',因此您的 x'400c' 变为 x'200c'。

压缩十进制的最后一个 nyble 也可以是 f - unsigned 以及 c 和 d。

最后,如果你有 Cobol Copybook,我的项目JRecord 有 Cobol 到 Csv && Cobol 到 Xml 转换程序(用 java 编写)。见

【讨论】:

    【解决方案2】:

    好的,因为我没有找到更好的解决方案,所以我创建了一个 php-class 来处理来自该数据集的记录:

    <?php
    namespace Mainframe;
    
    /**
     * Mainframe main function
     *
     * @author vp1zag4
     *        
     */
    class Mainframe
    {
    
        /**
         * Data string for reading
         * 
         * @var string | null
         */
        protected $data = null;
    
        /**
         * Default ouput charset
         * 
         * @var string
         */
        const OUTPUT_CHARSET = 'latin1';
    
        /**
         * Record length of dataset
         *
         * @var integer
         */
        protected $recordLength = 10;
    
        /**
         * Inits the
         *
         * @param unknown $data            
         */
        public function __construct($data = null)
        {
            if (! is_null($data)) {
                $this->setData($data);
            }
        }
    
        /**
         * Sets the data string and validates
         *
         * @param unknown $data            
         * @throws \LengthException
         */
        public function setData($data)
        {
            if (strlen($data) != $this->recordLength) {
                throw new \LengthException('Given data does not fit to dataset record length');
            }
    
            $this->data = $data;
        }
    
        /**
         * Unpack packed decimal (BCD) from mainframe format to integer
         *
         * @param unknown $str            
         * @return number
         */
        public static function unpackBCD($str)
        {
            $num = unpack('H*', $str);
            $num = array_shift($num);
            $sign = strtoupper(substr($num, - 1));
            $num = (int) substr($num, 0, - 1);
            if ($sign == 'D') {
                $num = $num * - 1;
            }
            return (int) $num;
        }
    
        /**
         * convert EBCDIC to default output charset
         *
         * @param string $str            
         * @return string
         */
        public static function conv($str, $optionalCharset = null)
        {
            $charset = (is_string($optionalCharset)) ? $optionalCharset : self::OUTPUT_CHARSET;
            return iconv('IBM037', $charset, $str);
        }
    
        /**
         * Reads part of data string and converts or unpacks
         *
         * @param integer $start
         * @param integer $length
         * @param bool $unpack
         * @param bool | string $conv
         */
        public function read($start, $length, $unpack = false, $conv = true)
        {
            if (empty($this->data)) {
                return null;
            }
    
            $result = substr($this->data, $start, $length);
    
            if($unpack) {
                return self::unpackBCD($result);
            }
    
            if ($conv) {
                return self::conv($result, $conv);
            }
    
            return $result;
        }
    }
    

    使用 $class->read(1, 3, True) 可以读取部分数据并同时对其进行转换/解包。

    也许它也可以随时帮助任何人。

    但我当然会尝试设置一些作业,它会直接在大型机上为我执行此操作,并使用一些 JSON 数据作为输出。

    【讨论】:

    • 您遗漏了一些小花絮 - 例如,符号半字节可以是 0xC 和 0xD 之外的其他值,我认为您没有正确处理它们。您可能需要查看操作原理 (publibfp.dhe.ibm.com/epubs/pdf/dz9zr010.pdf) 中的第 8 章,了解压缩十进制指令的所有血腥细节。
    • 我在想象固定宽度的文本字段。如果这对您的语言来说不容易处理,那么 Enterprise COBOL V6.1 原生支持生成 JSON,但 V6.1 是相当新的。确实很新。还有一个系统服务来生成 JSON(这是 COBOL 编译器使用的)。它增加了双方的开销,但如果它比您能够处理固定宽度的字符字段更好,那么它可以完成。是的,@ValerieR 是正确的。除了 C、D 或 F 之外,您不太可能看到其他内容,但如果您看到了怎么办?所以你必须为它编写代码,如果你要像上面那样编写代码。
    • 我不禁想到不必做任何事情更容易,但也许这会扼杀你的语言(似乎是 C# 的一个问题,它在很多固定宽度的字段上不是很好,因为这意味着大量的字符串处理)。
    • 顺便说一句,如果我没有指出 IBM 在此处描述的可移植 Java 组件中拥有您想要的大部分转换例程,那我就失职了:ibm.com/support/knowledgecenter/SSYKE2_8.0.0/…
    • @ValerieR 有趣,来自链接“0x0 和 0xF 之间的所有值都被解释为有效的符号代码。”而 0x0-0x9 会导致大型机十进制算术......窒息。哪里窒息 - 异常终止 |抛出异常。我认为“一切都是有效的符号......直到你做算术”也是一个 DFSORT 事情。可能意味着有更深的根源。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多