.net 如何使用非标准代码页读取EBCDIC数据,而不弄乱数字?
这是一个给老手的:——) 我正在从大型机DB2表读取二进制转储。该表有varchar、char、smallint、integer和float列。为了使其有趣,DB2使用代码页424(希伯来语)。我需要我的代码是代码页独立的 因此,我使用System.Text.Encoding使用streamreader打开文件,如下所示:.net 如何使用非标准代码页读取EBCDIC数据,而不弄乱数字?,.net,encoding,codepages,ebcdic,.net,Encoding,Codepages,Ebcdic,这是一个给老手的:——) 我正在从大型机DB2表读取二进制转储。该表有varchar、char、smallint、integer和float列。为了使其有趣,DB2使用代码页424(希伯来语)。我需要我的代码是代码页独立的 因此,我使用System.Text.Encoding使用streamreader打开文件,如下所示: Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding(20424) Dim sr A
Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding(20424)
Dim sr As New StreamReader(item.Key, encoding)
并继续使用将VARCHAR和CHAR数据根据其长度读入CHAR数组
sr.ReadBlock(buffer, 0, iFieldBufferSize)
始终记住VARCHAR列中的前2个字节应该被丢弃
并使用
SringValue = encoding.GetString(encoding.GetBytes(buffer))
一切都很好
但现在我转到SMALLINT列,我有麻烦了。有符号数字的值存储在2个字节中,因为它是大端,所以我需要
Dim buffer(iFieldBufferSize - 1) As Byte
buffer(1) = sr.Read ''switch the bytes around!
buffer(0) = sr.Read
Dim byteBuffer(iFieldBufferSize - 1) As Byte
Dim i16 As Int16 = BitConverter.ToUInt16(buffer, 0)
我打错电话了!例如,如果字节是00 03,我在缓冲区(1)中得到0,在缓冲区(0)中得到3-好。但当两个字节为0020时,我将128个字节读入缓冲区(0)
所以,在我做了半天的头发后,我从streamreader声明中删除了编码器,现在我将32个读数据放入缓冲区(0),就像它应该做的那样
底线是,非标准代码页编码器弄乱了字节读数
知道如何解决这个问题吗?不要使用StreamReader来读取此文件。它将解释文件中的二进制数,就好像它们是字符一样,这会弄乱它们的值。使用文件流和二进制读取器。仅在从表示字符串的文件中翻译一组字节时使用Encoding.GetString()。@Hans Passant是正确的。如果您正在读取包含二进制数据的文件(如您的描述所示),那么将文件当作文本来读取是不正确的 幸运的是,BinaryReader类包含一个构造函数,该构造函数将字符编码作为参数之一。您可以使用此选项将文件中的任何希伯来EBCDIC字符串自动转换为普通Unicode字符串,而不影响非文本(二进制)部分的解释 此外,您可能应该使用双字节VARCHAR length字段来读取字符串,而不是将其丢弃
ReadString()方法在这种情况下不起作用,因为该文件不是用.NET BinaryWriter类编码的。相反,您应该获取VARCHAR的长度(或CHAR字段的硬编码长度)并将其传递给ReadChars(int)方法。然后从返回的字符数组构造生成的字符串。不能将EBCDIC文件转储之类的内容作为流读取。StreamReader类是文本阅读器的一种类型,用于读取字符。您正在读取一个记录——一个包含混合二进制和文本的复杂数据结构 您需要使用文件流进行读取,并根据需要读取八位字节块。您将需要一些简单的助手方法,如:
private byte[] ReadOctets( Stream input , int size )
{
if ( size < 0 ) throw new ArgumentOutOfRangeException() ;
byte[] octets = new byte[size] ;
int octets_read = input.Read( octets , 0 , size ) ;
if ( octets_read != size ) throw new InvalidDataException() ;
return octets ;
}
public string readCharVarying( Stream input )
{
short size = readShort( input ) ;
return readCharFixed( input , size ) ;
}
public string readCharFixed( Stream input , int size )
{
Encoding e = System.Text.Encoding.GetEncoding(20424) ;
byte[] octets = ReadOctets( input , size ) ;
string value = e.GetString( octets ) ;
return value ;
}
private short readShort( Stream input )
{
byte[] octets = ReadOctets(input,2) ;
short bigEndianValue = BitConverter.ToInt16(octets,0) ;
short littleEndianValue = System.Net.IPAddress.NetworkToHostOrder( bigEndianValue ) ;
return littleEndianValue ;
}
private int readInt( Stream input )
{
byte[] octets = ReadOctets(input,4) ;
int bigEndianValue = BitConverter.ToInt32(octets,0) ;
int littleEndianValue = System.Net.IPAddress.NetworkToHostOrder( bigEndianValue ) ;
return littleEndianValue ;
}
private long readLong( Stream input )
{
byte[] octets = ReadOctets(input,8) ;
long bigEndianValue = BitConverter.ToInt64(octets,0) ;
long littleEndianValue = System.Net.IPAddress.NetworkToHostOrder( bigEndianValue ) ;
return littleEndianValue ;
}
private byte[]ReadOctets(流输入,整数大小)
{
如果(大小<0)抛出新ArgumentOutOfRangeException();
字节[]八位字节=新字节[大小];
int-octets_-read=input.read(八位字节,0,大小);
如果(八位字节_read!=大小)抛出新的InvalidDataException();
返回八位组;
}
公共字符串readCharVariable(流输入)
{
短尺寸=读短(输入);
返回readCharFixed(输入,大小);
}
公共字符串readCharFixed(流输入,整数大小)
{
编码e=System.Text.Encoding.GetEncoding(20424);
字节[]八位字节=读取八位字节(输入,大小);
字符串值=e.GetString(八位字节);
返回值;
}
专用short readShort(流输入)
{
字节[]八位字节=读取八位字节(输入,2);
短bigEndianValue=位转换器.ToInt16(八位字节,0);
short littleEndianValue=System.Net.IPAddress.NetworkToHostOrder(bigEndianValue);
返回LittleEndian值;
}
私有int readInt(流输入)
{
字节[]八位字节=读取八位字节(输入,4);
int bigEndianValue=位转换器.ToInt32(八位字节,0);
int littleEndianValue=System.Net.IPAddress.NetworkToHostOrder(bigEndianValue);
返回LittleEndian值;
}
私有long readLong(流输入)
{
字节[]八位字节=读取八位字节(输入,8);
long bigEndianValue=位转换器.ToInt64(八位字节,0);
long littleEndianValue=System.Net.IPAddress.NetworkToHostOrder(bigEndianValue);
返回LittleEndian值;
}
IBM大型机的文件系统中通常有固定或可变长度的记录。固定长度很简单:您只需要知道记录长度,就可以在对read()方法的一次调用中读取记录的所有字节,然后根据需要转换片段
可变长度记录有点棘手,它们从4个八位记录描述符字开始,由2个八位(16位)逻辑记录长度组成,然后是一个2个八位(16位)0值。逻辑记录长度不包括4个八位字节的记录描述符字
您可能还会看到可变的、跨越的记录。这些记录类似于可变长度记录,不同之处在于4-octet前缀是段描述符字。前两个八位字节包含段长度,下一个八位字节标识段类型,最后一个八位字节为NUL(0x00)。段类型如下:
- 0x00表示完整的逻辑记录
- 0x01表示这是跨距记录的第一段
- 0x10表示这是已跨越记录的最后一段
- 0x11表示这是跨记录的“内部”段,即“除第一段或最后一段以外的多段记录段”