使用 PHP 解压大型机压缩十进制 (BCD)

Posted

技术标签:

【中文标题】使用 PHP 解压大型机压缩十进制 (BCD)【英文标题】:Unpack Mainframe packed Decimal (BCD) with PHP 【发布时间】: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 数据库转储。 【参考方案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 编写)。见

Cobol to Csv notes Cobol To Xml notes

【讨论】:

【参考方案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 事情。可能意味着有更深的根源。

以上是关于使用 PHP 解压大型机压缩十进制 (BCD)的主要内容,如果未能解决你的问题,请参考以下文章

在Fortran中设计一个大型可压缩二进制浮点数

[计组]压缩BCD码指二进制编码的十进制

压缩和导航大型压缩目录的策略

使用 SORT 进行大型机 JCL 记录转置

二进制数转换为BCD码的方法都有哪些?

在java中将字符串转换为压缩十进制