使用 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)的主要内容,如果未能解决你的问题,请参考以下文章