这是旧手(呃)的手: - )
我正在从大型机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 As New StreamReader(item.Key, encoding)
并继续使用
将VARCHAR和CHAR数据根据其长度读入char数组sr.ReadBlock(buffer, 0, iFieldBufferSize)
始终记住VARCHAR列中的前2个字节应该被丢弃 并使用
获取正确的字符串SringValue = encoding.GetString(encoding.GetBytes(buffer))
一切都很棒!
但现在我进入了SMALLINT专栏,我遇到了麻烦。有符号数的值存储在2个字节中,因为它的Big endian,我做
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 - 好。但是当两个字节是00 20时,我将128读入缓冲区(0)!
所以经过半天拉我的头发后,我从streamreader声明中删除编码器,现在我将32读入缓冲区(0),就像它应该是!!!
底线,非标准代码页编码器会弄乱字节读数!!!
知道怎么解决这个问题吗?
答案 0 :(得分:3)
不使用StreamReader读取此文件。它将解释文件中的二进制数,就好像它们是字符一样,这会弄乱它们的值。使用FileStream和BinaryReader。当您从表示字符串的文件转换一组字节时,仅使用Encoding.GetString()。
答案 1 :(得分:3)
您无法读取类似于EBCDIC文件转储的内容。 StreamReader类是一种TextReader,用于读取字符。您正在阅读记录 - 一个包含混合二进制和文本的复杂数据结构。
您需要使用FileStream进行读取,并根据需要读取八位字节块。你需要一些简单的辅助方法,如:
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 ;
}
IBM大型机通常在其文件系统中具有固定或可变长度的记录。固定长度很简单:你只需要知道记录长度,你可以在一次调用Read()方法中读取记录的所有字节,然后根据需要转换它们。
可变长度记录有点棘手,它们以4字节的记录描述符字开头,包括2个八位字节(16位)逻辑记录长度,后跟2个八位字节(16位)0值。逻辑记录长度不包括4个八位字节的记录描述符字。
您可能还会看到变量跨区记录。这些类似于可变长度记录,除了4个八位字节前缀是段描述符字。前2个八位字节包含段长度,下一个八位字节标识段类型,最后一个八位字节为NUL(0x00)。细分类型如下:
您可以将可变长度和可变跨区记录视为相同。要读取其中一个,首先需要解析段/记录/描述符字,并从其组成段读取/组装完整记录到byte [],然后执行转换该字节所需的任何操作[]成你可以使用的表格。
答案 2 :(得分:2)
@Hans Passant是正确的。如果您正在读取包含二进制数据的文件(如您的描述所示),那么读取文件就好像它是文本一样。
幸运的是,BinaryReader类包含一个构造函数,它将字符编码作为参数之一。您可以使用它自动将文件中的任何希伯来语EBCDIC字符串转换为普通的Unicode字符串,而不会影响对非文本(二进制)部分的解释。
另外,您应该使用双字节VARCHAR长度字段来读取字符串而不是将其丢弃!
在这种情况下,ReadString()方法不起作用,因为该文件未使用.NET BinaryWriter类进行编码。相反,您应该获取VARCHAR的长度(或CHAR字段的硬编码长度)并将其传递给ReadChars(int)方法。然后从返回的字符数组构造结果字符串。