3

これは古い(古い)手のためのものです:-)

メインフレーム DB2 テーブルからバイナリ ダンプを読み込んでいます。テーブルには、varchar、char、smallint、integer、float の列があります。興味深いことに、DB2 はコード ページ 424 (ヘブライ語) を使用します。コードをコードページに依存しないようにする必要があります。

したがって、次のように System.Text.Encoding を使用してストリームリーダーでファイルを開きます。

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 を取得します。しかし、2 バイトが 00 20 の場合、128 が buffer(0) に読み込まれます!

髪を引っ張って半日後、ストリームリーダー宣言からエンコーダーを削除すると、32 がバッファー (0) に読み込まれます。

要するに、非標準のコードページエンコーダーはバイトの読み取りを台無しにします!!!

これを回避する方法はありますか?

4

3 に答える 3

4

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()メソッドを1回呼び出すだけでレコードのすべてのバイトを読み取り、必要に応じて断片を変換できます。

可変長レコードは少し注意が必要です。2オクテット(16ビット)の論理レコード長とそれに続く2オクテット(16ビット)の0値で構成される4オクテットのレコード記述子ワードで始まります。論理レコード長は、4オクテットのレコード記述子ワードを除きます。

また、可変のスパンレコードが表示される場合があります。これらは、4オクテットのプレフィックスがセグメント記述子ワードであることを除いて、可変長レコードに似ています。最初の2オクテットにはセグメントの長さが含まれ、次のオクテットはセグメントタイプを識別し、最後のオクテットはNUL(0x00)です。セグメントタイプは次のとおりです。

  • 0x00は、完全な論理レコードを示します
  • 0x01は、これがスパンレコードの最初のセグメントであることを示します
  • 0x10は、これがスパンレコードの最後のセグメントであることを示します
  • 0x11は、これがスパンレコードの「内部」セグメント、つまり「最初または最後のセグメント以外のマルチセグメントレコードのセグメント」であることを示します。

可変長レコードと可変スパンレコードを同一として扱うことができます。これらのいずれかを読み取るには、最初にセグメント/レコード/記述子の単語を解析し、レコード全体を構成セグメントからbyte []に​​読み取り/アセンブルしてから、そのバイトを変換するために必要な処理を行う必要があります。 []あなたが使用できる形に。

于 2011-02-24T20:52:30.213 に答える
4

StreamReader を使用してこのファイルを読み取らないでください。ファイル内の 2 進数が文字であるかのように解釈され、その値が台無しになります。FileStream と BinaryReader を使用します。 文字列を表すファイルからバイトのグループを変換する場合にのみ、Encoding.GetString() を使用します。

于 2011-02-24T19:52:34.643 に答える
3

@Hans Passantは正しいです。バイナリ データを含むファイルを読み取る場合 (説明が示すように)、ファイルをテキストとして読み取るのは正しくありません。

さいわい、BinaryReader クラスには、パラメーターの 1 つとして文字エンコードを受け取るコンストラクターが含まれています。これを使用して、非テキスト (バイナリ) 部分の解釈に影響を与えることなく、ファイル内の任意のヘブライ語 EBCDIC 文字列を通常の Unicode 文字列に自動的に変換できます。

また、文字列を単に破棄するのではなく、2 バイトの VARCHAR 長さフィールドを使用して読み取る必要があります。

この場合、ファイルが .NET BinaryWriter クラスでエンコードされていないため、ReadString() メソッドは機能しません。代わりに、VARCHAR の長さ (またはハードコードされた CHAR フィールドの長さ) を取得し、それを ReadChars(int) メソッドに渡す必要があります。次に、返された文字配列から結果の文字列を作成します。

于 2011-02-24T20:43:14.433 に答える