Я получил файл данных с мэйнфрейма. Я уже занимался преобразованием EBCDIC в latin1 с помощью PHP. Но теперь остались эти упакованные десятичные поля.
Например, число 12345 упаковано в 3 байта и выглядит так: x’12345C ‘
Отрицательный будет как: x’12345D ‘
Таким образом, правая половина Байт говорит знак. Есть ли способ сделать это легко с PHP?
Теперь я делаю это как:
$bin = "\x12\x34\x5C";
var_dump(
unpack("H*", $bin)
);
Это приводит к:
array(1) {
[1]=>
string(4) "123c"}
Теперь я могу проверить, является ли последний знак C или D, и сделать все вручную. Но, может быть, есть лучшее решение?
Как сказал Билл, заставьте людей из мэйнфреймов конвертировать файл в Текст на мейнфрейме и отправьте текстовый файл, утилиты вроде sort и т. д. могут сделать это на мэйнфрейме. И это только что упакованный десятичный в файле или у вас есть либо двоичный или же Десятичная зона также ???
Если вы настаиваете на том, чтобы сделать это в PHP, вам нужно выполнить преобразование десятичного числа в упакованном виде. до вы делаете преобразование EBCDIC, потому что для упакованного десятичного числа, как x’400c ‘
конвертер EBCDIC будет смотреть на x’40 ‘и говорить, что это пробел, и конвертировать его в x’20’, так что ваш x’400c ‘становится x’200c’.
Кроме того, окончательный nyble в упакованном десятичном виде может быть f — без знака а также c и d.
Наконец, если у вас есть тетрадь Cobol, мой проект JRecord имеет Cobol в CSV && Программы преобразования Cobol в Xml (написано на Java). Увидеть
Хорошо, поскольку я не нашел более подходящего решения, я создал php-класс для обработки записи из этого набора данных:
<?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 в качестве вывода.