用PHP解包大型机压缩十进制(BCD)
我从主机上得到了一个数据文件。我已经用PHP处理了EBCDIC到latin1的转换。但现在剩下的是压缩的十进制字段 例如,数字12345被压缩成3个字节,看起来像:x'12345C' 负片是:x'12345D' 所以右半个字节表示符号。有没有一种方法可以用PHP轻松地做到这一点 现在我这样做:用PHP解包大型机压缩十进制(BCD),php,mainframe,bcd,packed-decimal,Php,Mainframe,Bcd,Packed Decimal,我从主机上得到了一个数据文件。我已经用PHP处理了EBCDIC到latin1的转换。但现在剩下的是压缩的十进制字段 例如,数字12345被压缩成3个字节,看起来像:x'12345C' 负片是:x'12345D' 所以右半个字节表示符号。有没有一种方法可以用PHP轻松地做到这一点 现在我这样做: $bin = "\x12\x34\x5C"; var_dump( unpack("H*", $bin) ); 其结果是: array(1) { [1]=> string(4) "1
$bin = "\x12\x34\x5C";
var_dump(
unpack("H*", $bin)
);
其结果是:
array(1) {
[1]=>
string(4) "123c"
}
现在我可以检查最后一个符号是C还是D,然后用手来做所有的事情。但也许有更好的解决方案?正如比尔所说,让大型机人员在大型机上将文件转换为文本,然后发送文本文件,排序等实用程序可以在大型机上执行此操作。文件中是否只有压缩的十进制数,或者是否有二进制数 如果坚持使用PHP进行转换,则需要在执行EBCDIC转换之前执行压缩十进制转换,因为对于像x'400c'这样的压缩十进制 EBCDIC转换器将查看x'40'并表示这是一个空格,然后将其转换为x'20',因此您的x'400c'将变成x'200c' 此外,压缩小数中的最后一个nyble可以是f-无符号以及c和d 最后,如果您有Cobol Copybook,那么我的项目有Cobol到Csv和Cobol到Xml的转换程序(用java编写)。看
<?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;
}
}
更好的解决方案是让大型机人员“只提供文本”给您所有内容。然后,您可以在文件/记录级别执行代码集转换,并且不会遇到任何问题。这意味着一个“单独的符号”,或者是一个比例因子,或者是一个实际的小数点,以您更容易做到的为准。除非大型机程序是用汇编语言编写的,否则它们生成这样的数据对您来说是微不足道的,这样您就不用做很多事情了。另见标记为packed-decimal.TL的其他问题;“大型机。但也许有更好的解决方案?”是的。让恐龙管理员来解决这个问题。@Rhymoid你是说TS:DR?没办法弄清楚。娜达。零。没有一个你不需要想出很酷的绰号来让我们理解你有阅读困难。只要写任何东西。比尔是对的-在大型机上进行转换。当toytown电脑停机时,它可以在额外的正常运行时间内完成这项工作。当然,在大型机上完成这项工作是最好的方法。但在我的例子中,我的电脑上已经有了这些文件。这是一个每周都会进行的dli数据库转储。您缺少一些小技巧-例如,符号半字节可以是0xC和0xD之外的其他值,我认为您没有正确处理它们。你可能想回顾一下操作原理()中的第8章,了解关于压缩十进制指令的所有血淋淋的细节。我想象的是固定宽度的文本字段。如果您的语言不容易处理这一点,那么EnterpriseCobolV6.1具有生成JSON的本机支持,但V6.1是相当新的。的确是很新的。还有一个生成JSON的系统服务(COBOL编译器使用它)。这会增加两侧的开销,但如果处理固定宽度字符字段的能力比处理固定宽度字符字段的能力强,则可以这样做。是的,@ValerieR是正确的。除了C、D或F之外,你不太可能看到其他的东西,但如果你看到了呢?因此,如果你要像上面那样进行编码,你必须为它编码。我忍不住想,不做任何事情更容易,但这可能会扼杀你的语言(C#似乎有问题,它只是不太擅长很多固定宽度的字段,因为这意味着大量的字符串处理),如果我没有指出IBM在这里描述的可移植Java组件中有您想要的大部分转换例程:@ValerieR interest,来自链接“0x0和0xF之间的所有值都被解释为有效的符号码”。而0x0-0x9会导致大型机十进制算术。。。窒息其中choke-abend |抛出异常。我认为“一切都是一个有效的符号…直到你做算术”也是一件很简单的事情。可能意味着有更深的根源。