4

我从大型机获得了一个数据文件。我已经用 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 并手动完成。但也许有更好的解决方案?

4

2 回答 2

2

正如比尔所说,让大型机人员将文件转换为大型机上的文本并发送文本文件,排序等实用程序可以在大型机上执行此操作。此外,它只是在文件中打包十进制还是您也有二进制分区十进制???

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

此外,压缩十进制中的最后一个 nyble 可以是f - 无符号以及 c 和 d。

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

于 2016-06-24T00:20:51.237 回答
1

好的,因为我没有找到更好的解决方案,所以我创建了一个 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 数据作为输出。

于 2016-06-24T15:04:52.003 回答