我正在尝试将文件从 ECDIC 转换为 ASCII 格式,并遇到了一个有趣的问题。这些文件包含固定长度记录,其中一些字段为有符号二进制整数(在记录布局中描述为B4),以及长精度数字值(在记录中描述为L8)布局)。我已经能够毫无问题地转换字符数据,但我不知道如何转换这些数值。从原始系统的参考手册(IBM 5110)开始,字段如下所述。
B表示数字数据项的长度(2,4或8个字节) 要转换为的定点有符号二进制整数格式 BASIC内部数据格式。对于记录I / O文件输入,接下来的2, 4,或者记录中的8个字节包含有符号的二进制值 由系统转换为内部数据格式并分配给 使用的READ FILE或REREAD FILE语句中指定的变量 表格声明。
和
L表示数值的长精度(8个字符)。对于 输入,此条目表示一个八位,长精度 记录中的值将被分配而不转换为a READ FILE或REREAD中指定的相应数字变量 文件声明。
编辑:这是我用于转换的代码
private void ConvertFile(EbcdicFile file)
{
if (file == null) return;
var filePath = Path.Combine(file.Path, file.FileName);
if (!File.Exists(filePath))
{
this.Logger.Info(string.Format("Cannot convert file {0}. It does not exist.", filePath));
return;
}
var ebcdic = Encoding.GetEncoding(37);
string convertedFilepath = Path.Combine(file.Path, file.ConvertedFileName);
byte[] fileData = File.ReadAllBytes(filePath);
if (!file.HasNumericFields)
File.WriteAllBytes(convertedFilepath, Encoding.Convert(ebcdic, Encoding.ASCII, fileData));
else
{
var convertedFileData = new List<byte>();
for (int position = 0; position < fileData.Length; position += file.RecordLength)
{
var segment = new ArraySegment<byte>(fileData, position, file.RecordLength);
file.Fields.ForEach(field =>
{
var fieldSegment = segment.Array.Skip(segment.Offset + field.Start - 1).Take(field.Length);
if (field.Type.Equals("string", StringComparison.OrdinalIgnoreCase))
{
convertedFileData.AddRange(
Encoding.Convert(ebcdic, Encoding.ASCII, fieldSegment.ToArray())
);
}
else if (field.Type.Equals("B4", StringComparison.OrdinalIgnoreCase))
{
// Not sure how to convert this field
}
else if (field.Type.Equals("L8", StringComparison.OrdinalIgnoreCase))
{
// Not sure how to convert this field
}
});
}
File.WriteAllBytes(convertedFilepath, convertedFileData.ToArray());
}
}
答案 0 :(得分:2)
您必须首先知道固定记录的大小。使用FileStream.Read()读取一个字节的记录。然后使用Encoding.GetString()将其转换为字符串。
然后使用String.SubString()从记录中捕获字段。 B4只是一个长度为4的子串调用,L8的长度为8.进一步将这样的字段转换为带有Decimal.Parse()的数字。您可能必须划分结果,不清楚使用什么定点乘数。 100的赔率很高。
答案 1 :(得分:1)
好的,所以我想出了如何转换这两个字段。 B4字段非常简单。它们本质上是一个4字节的数组,可以转换为整数。
//The IBM 5110 were big endian machines, so reverse the array
if (BitConverter.IsLittleEndian)
Array.Reverse(by);
int value = BitConverter.ToInt32(by, 0);
L8字段是8字节数组,表示IBM Double Precision Float。有很多方法可以将其转换为IEEE 754 Float。可以在以下网址找到一些示例:
这是我根据文章指导使用的版本。
private double IbmFloatToDouble(byte[] value)
{
if (ReferenceEquals(null, value))
throw new ArgumentNullException("value");
if (BitConverter.ToInt64(value, 0) == 0)
return 0;
int exponentBias = 64;
int ibmBase = 16;
double sign = 0.0D;
int signValue = (value[0] & 0x80) >> 7;
int exponentValue = (value[0] & 0x7f);
double fraction1 = (value[1] << 16) + (value[2] << 8) + value[3];
double fraction2 = (value[4] << 24) + (value[5] << 16) + (value[6] << 8) + value[7];
double exponent24 = 16777216.0; // 2^24
double exponent56 = 72057594037927936.0; // 2^56
double mantissa1 = fraction1 / exponent24;
double mantissa2 = fraction2 / exponent56;
double mantissa = mantissa1 + mantissa2;
double exponent = Math.Pow(ibmBase, exponentValue - exponentBias);
if (signValue == 0)
sign = 1.0;
else
sign = -1.0;
return (sign * mantissa * exponent);
}