如何使用 Java 解压缩 COMP-3 数字?
Posted
技术标签:
【中文标题】如何使用 Java 解压缩 COMP-3 数字?【英文标题】:How to unpack COMP-3 digits using Java? 【发布时间】:2013-12-03 04:17:27 【问题描述】:我有巨大的大型机文件,并且该文件中有一些压缩数字。我想知道如何使用java解压后面的数字?
压缩数字:?
我阅读了解包数字的教程,发现以下规则来计算解包数字所需的字节数:
total_number_of_bytes = (no. of digits + 1) / 2
我写了以下代码来解压数字:
public String unpackData(String packedData, int decimalPointLocation)
String unpackedData = "";
char[] characters = packedData.toCharArray();
final int impliedPositive = 15;
final int positiveNumber = 12;
final int negativeNumber = 13;
for (int currentCharIndex = 0; currentCharIndex < characters.length; currentCharIndex++)
byte[] unpackedDigits = unpackByte((byte) characters[currentCharIndex]);
if(currentCharIndex == (characters.length - 1))
if(unpackedDigits[1] == impliedPositive || unpackedDigits[1] == positiveNumber)
unpackedData += String.valueOf(unpackedDigits[0]);
else if(unpackedDigits[1] == negativeNumber)
unpackedData = "-" + unpackedData;
else
unpackedData += String.valueOf(unpackedDigits[0]) + String.valueOf(unpackedDigits[1]);
if(decimalPointLocation > 0)
unpackedData = unpackedData.substring(0, (decimalPointLocation - 1)) +
"." +
unpackedData.substring(decimalPointLocation);
return unpackedData;
private byte[] unpackByte(byte packedData)
byte firstDigit = (byte) (packedData >>> 4);
firstDigit = setBitsToZero(firstDigit, 4, 8);
//System.out.println(" firstDigit = "+ firstDigit + ", and its bit string after unpacking = " + getBitString(firstDigit, 7));
byte secondDigit = setBitsToZero(packedData, 4, 8);
//System.out.println("second digit = " + secondDigit + ", and its bit string of second digit after unpcking = " + getBitString(secondDigit, 7));
byte[] unpackedData = new byte[2];
unpackedData[0] = firstDigit;
unpackedData[1] = secondDigit;
return unpackedData;
private byte setBitsToZero(byte number, int startBitPosition, int endBitPosition)
for (int i = startBitPosition; i < endBitPosition; i++)
number = (byte) (number & ~(1 << i));
return number;
该程序对整数类型值正常工作,但不适用于浮点类型值。
谁能告诉我我的程序是否正确?
【问题讨论】:
您是否验证了多个测试用例的数字序列是否正确?我看不到您在哪里添加 char '0' 或 48 以转换为可打印字符。您确定 String.valueOf() 返回字符 '0'..'9' 而不是整数字节值 0x00 .. 0x09 吗?将小数点插入字符串时出现问题?看起来 decimalPointLocation 1 是 .######,2 是 #.#####,3 是 ##.#### 等。JUnit 可用于验证您的 unpackData 函数在所有测试条件下都能正常工作。即使没有测试格式不正确的数据,也有很多极端情况需要检查。 ***.com/questions/17448008/… 浮点数与压缩十进制不同。 如果这些压缩字段中的任何一个被签名,你也必须处理它......通常,符号是用最低有效数字编码的。最后,压缩字段通常包含一个隐含的小数点,您将需要原始 COBOL 记录定义来对它们进行排序。packedData >>> 4
中间多了一个>
符号。
【参考方案1】:
COMP-3(或“压缩十进制”)数据如下所示:0x12345s,其中“s”是 C 表示正数,D 表示负数,或 F 表示无符号数。因此 0x12345c -> "12345"、x012345d -> "-12345" 和 0x12345f -> "12345"。
您有一个明显的错误:如果符号为负,您将忽略包含符号 nybble 的字节中的 nybble(例如,上面的“5”)。此外,您在操纵 nybble 方面工作太辛苦了,这是一个简单的按位与或 4 位移位来隔离 nybble。
试试这样的东西(未经测试):
public String unpackData(String packedData, int decimalPointLocation)
String unpackedData = "";
char[] characters = packedData.toCharArray();
final int negativeSign = 13;
for (int currentCharIndex = 0; currentCharIndex < characters.length; currentCharIndex++)
byte firstDigit = ((byte) characters[currentCharIndex]) >>> 4);
byte secondDigit = ((byte) characters[currentCharIndex]) & 0x0F;
unpackedData += String.valueOf(firstDigit);
if (currentCharIndex == (characters.length - 1))
if (secondDigit == negativeSign)
unpackedData = "-" + unpackedData;
else
unpackedData += String.valueOf(secondDigit);
if (decimalPointLocation > 0)
unpackedData = unpackedData.substring(0, (decimalPointLocation - 1)) +
"." +
unpackedData.substring(decimalPointLocation);
return unpackedData;
【讨论】:
这个答案可能大部分时间都有效,从文件读取时它不可靠。将压缩十进制作为字符串读取可能会损坏压缩十进制。您必须将压缩十进制作为字节读取和处理。【参考方案2】:public static final int UNSIGNED_BYTE = 0xff;
public static final int BITS_RIGHT = 0xf;
public long parseComp3(byte[] data)
long val = 0L;
boolean negative = false;
for (int i = 0; i < data.length; i++)
int raw = data[i] & UNSIGNED_BYTE;
int digitA = raw >> 4;
int digitB = raw & BITS_RIGHT;
if (digitA < 10)
val *= 10L;
val += (long) digitA;
else if (digitA == 11 || digitA == 13) // Some non-IBM systems store the sign on left or use 11 for negative.
negative = true;
if (digitB < 10)
val *= 10L;
val += (long) digitB;
else if (digitB == 11 || digitB == 13)
negative = true;
if (negative)
val = -val;
return val;
【讨论】:
您是否想表达一些特别的东西,使这个答案比以前接受的答案更好? 我认为这是一个更好的答案,因为输入是一个字节数组,以字符格式读取 ebcdic comp-3 会损坏 comp-3 数据。【参考方案3】:Ross Paterson 解决方案在将前 4 位向右移动时存在错误。必须应用掩码 0x0F。
这里是更正的方法:
private static String unpackData(byte[] packedData, int decimalPointLocation)
String unpackedData = "";
final int negativeSign = 13;
for (int currentCharIndex = 0; currentCharIndex < packedData.length; currentCharIndex++)
byte firstDigit = (byte) ((packedData[currentCharIndex] >>> 4) & 0x0F);
byte secondDigit = (byte) (packedData[currentCharIndex] & 0x0F);
unpackedData += String.valueOf(firstDigit);
if (currentCharIndex == (packedData.length - 1))
if (secondDigit == negativeSign)
unpackedData = "-" + unpackedData;
else
unpackedData += String.valueOf(secondDigit);
if (decimalPointLocation > 0)
int position = unpackedData.length() - decimalPointLocation;
unpackedData = unpackedData.substring(0, position) + "." + unpackedData.substring(position);
return unpackedData;
【讨论】:
【参考方案4】:我已经测试了Ross Paterson 解决方案,运行不正常,但有一些小细节。感谢罗斯,也感谢 Dr. Bob 的“int raw”
测试解决方案在这里:
private static String unpackData(byte[] packedData, int decimals)
String unpackedData="";
final int negativeSign = 13;
int lengthPack = packedData.length;
int numDigits = lengthPack*2-1;
int raw = (packedData[lengthPack-1] & 0xFF);
int firstDigit = (raw >> 4);
int secondDigit = (packedData[lengthPack-1] & 0x0F);
boolean negative = (secondDigit==negativeSign);
int lastDigit = firstDigit;
for (int i = 0; i < lengthPack-1; i++)
raw = (packedData[i] & 0xFF);
firstDigit = (raw >> 4);
secondDigit = (packedData[i] & 0x0F);
unpackedData+=String.valueOf(firstDigit);
unpackedData+=String.valueOf(secondDigit);
unpackedData+=String.valueOf(lastDigit);
if (decimals > 0)
unpackedData = unpackedData.substring(0,numDigits-decimals)+"."+unpackedData.substring(numDigits-decimals);
if (negative)
return '-'+unpackedData;
return unpackedData;
以及将解包数据转换为打包数据的函数:
private static byte[] packData(String unpackedData)
int unpackedDataLength = unpackedData.length();
final int negativeSign = 13;
final int positiveSign = 12;
if (unpackedData.charAt(0)=='-')
unpackedDataLength--;
if (unpackedData.contains("."))
unpackedDataLength--;
int packedLength = unpackedDataLength/2+1;
byte[] packed = new byte[packedLength];
int countPacked = 0;
boolean firstHex = (packedLength*2-1 == unpackedDataLength);
for (int i=0;i<unpackedData.length();i++)
if (unpackedData.charAt(i)!='-' && unpackedData.charAt(i)!='.')
byte digit = Byte.valueOf(unpackedData.substring(i,i+1));
if (firstHex)
packed[countPacked]=(byte) (digit<<4);
else
packed[countPacked]=(byte) (packed[countPacked] | digit );
countPacked++;
firstHex=!firstHex;
if (unpackedData.charAt(0)=='-')
packed[countPacked]=(byte) (packed[countPacked] | negativeSign );
else
packed[countPacked]=(byte) (packed[countPacked] | positiveSign );
return packed;
【讨论】:
以上是关于如何使用 Java 解压缩 COMP-3 数字?的主要内容,如果未能解决你的问题,请参考以下文章
如果使用struct.pack(fmt,v1,v2,...)在python打包,如何在cpp中解压缩数字